# @zaeniahmad/kpwgen

Generate **secure, reproducible passwords** with absolute consistency. As long as your inputs remain the same, the result is **bit-identical**. Works **100% offline**, no telemetry, no network calls.

## ✨ Features

* **Deterministic**: Same input ⇒ same output, always.
* **Cryptographically secure**: HMAC-based derivation (SHA-2).
* **Offline**: No external requests.
* **Configurable**: Prefix, suffix, target length, normalization.
* **Isomorphic**: Works in Node.js and Browser.
* **No storage**: Does not save secrets or passwords.

## 📦 Installation

```bash
npm install @zaeniahmad/kpwgen
# or
yarn add @zaeniahmad/kpwgen
# or
pnpm add @zaeniahmad/kpwgen
```

## ⚡️ Quick Start

**ESM (Node.js / Browser)**

```ts
import { generatePassword } from "@zaeniahmad/kpwgen";

const pwd = await generatePassword({
  masterSecret: process.env.MASTER_SECRET!,
  platform: "google",
  version: 1,
  lengthTarget: 18,
  prefix: "Qx9",
  suffix: "K7",
  normalize: true,
});

console.log(pwd);
```

## 🧠 How It Works

1. **Normalize**: Clean `platform` input.
2. **Derive**: Create message from `{platform, version}`.
3. **HMAC**: Compute SHA-256 HMAC with `masterSecret`.
4. **Encode**: Convert digest to base32/charset.
5. **Shape**: Apply length, prefix, suffix.

## 🧩 API

### `generatePassword(options: GenOptions): Promise<string>`

**Options**

* `masterSecret: string` – Required.
* `platform: string` – Required.
* `version?: number` – Default `1`.
* `prefix?: string`
* `suffix?: string`
* `lengthTarget?: number` – Default `18`.
* `normalize?: boolean` – Default `true`.

**Utilities**

* `normalizePlatform(raw: string): string`
* `toBase32(buf: Uint8Array): string`

## 🔒 Security Best Practices

* Never hardcode `masterSecret`.
* Use `version` for rotation.
* Add policy rules for site-specific requirements.
* Avoid storing secrets in local storage.

## 🧪 Testing

```bash
npm run lint
npm test
```

Example:

```ts
import { generatePassword } from "@zaeniahmad/kpwgen";
import { describe, it, expect } from "vitest";

describe("deterministic", () => {
  it("same input, same output", async () => {
    const a = await generatePassword({ masterSecret: "a", platform: "github", version: 1 });
    const b = await generatePassword({ masterSecret: "a", platform: "github", version: 1 });
    expect(a).toBe(b);
  });
});
```

## 📚 FAQ

**Why not use a normal password manager?**

> Stateless & reproducible. No sync needed.

**Does it work offline?**

> Yes.

**Can I enforce special characters?**

> Yes, via prefix/suffix or post-processing.

## 📝 License

ISC