# `passphrase` _(no submodules)_ ## Purpose `generatePassphrase(opts, rng?)` produces an EFF-style Diceware passphrase by drawing `length` words uniformly (via the bias-free `randomInt`) from the wordlist resolved via `opts.wordlist`. The default — 6 words from the EFF Long Wordlist joined with `-` — gives ≈ 77.55 bits of entropy, comfortably above the 75-bit floor recommended for symmetric-key-equivalent secrets. Diceware-style passphrases are dramatically more memorable than random char-class passwords at equivalent entropy, which is why the vault item editor in `@suaveplan/vault` defaults to this mode for the "Generate password" surface. The function honours configurable separator, three capitalisation modes (`none` / `firstChar` / `randomWord`), and optional `addDigits` / `addSymbols` injections at random positions in the joined output. The decoration knobs exist so the same generator can produce strings that satisfy downstream "must contain a digit" composition rules without abandoning the Diceware backbone. ## Features - Bundled EFF Long (7776) and EFF Short 1 (1296) wordlists via tree-shakeable subpath imports. - Caller-supplied `string[]` wordlists allowed past the 256-entry min-size guard. - Configurable separator (`default "-"`). - `capitalize: "none" | "firstChar" | "randomWord"`. - `addDigits` / `addSymbols` injection at randomised positions. - Deterministic under seeded `Rng`. - Zod-validated; mismatch → `PWGEN.INVALID_OPTIONS`. - Emits `pwgen.generate_duration_ms{mode=passphrase}` and `pwgen.entropy_bits{mode=passphrase}` histograms. ## Usage: Basic Example ```ts import { generatePassphrase } from "@suaveplan/password-generator"; const pp = generatePassphrase({}); // 6 words, EFF Long, joined with "-" ``` ## Usage: Advanced Example ```ts import { generatePassphrase } from "@suaveplan/password-generator"; // 5 short words capitalised at random, two trailing digits, one symbol const pp = generatePassphrase({ length: 5, wordlist: "short", capitalize: "randomWord", addDigits: 2, addSymbols: 1, separator: " ", }); // Caller-supplied themed wordlist (must be ≥ 256 entries) const dnd = [/* ... 300 fantasy nouns ... */]; const themed = generatePassphrase({ wordlist: dnd, length: 4 }); ``` ## API Reference ### `generatePassphrase(opts?: GeneratePassphraseOptions, rng?: Rng): string` - `opts.length` (default `6`): number of words to draw. - `opts.wordlist` (default `"long"`): `"long"` / `"short"` / registry name / inline `string[]`. - `opts.separator` (default `"-"`): string inserted between words before decoration. - `opts.capitalize` (default `"none"`): `"none"` / `"firstChar"` / `"randomWord"`. - `opts.addDigits` / `opts.addSymbols` (default `0`): count of characters injected at randomised positions. - `rng`: defaults to `defaultRng()`. Throws `PwgenError(PWGEN.INVALID_OPTIONS)` on Zod mismatch, `PwgenError(PWGEN.WORDLIST_TOO_SHORT)` for inline arrays below 256 entries, and `PwgenError(PWGEN.WORDLIST_NOT_REGISTERED)` for unknown registry names. ## Implementation Notes Wordlist resolution lazily routes through `getWordlist(name)` so the registry's min-size guard applies uniformly — there's no special-case for the bundled EFF lists. Inline `string[]` wordlists bypass the registry but go through the same 256-entry floor inline, so a caller passing a tiny array sees the same `PWGEN.WORDLIST_TOO_SHORT` they would see from `registerWordlist`. Decoration is applied _after_ joining: `addDigits` / `addSymbols` inserts characters at uniformly-random positions in the already-joined string. This means a digit can land mid-word (e.g. `hor5se-rifle`) — that's intentional because inserting only at word boundaries would create a small-cardinality position distribution that's easier to fingerprint. Per-class injection uses bias-free `randomInt` against the `DIGITS` / `SYMBOLS` sets defined in the `charsets` module. Entropy reporting uses `estimatePassphraseEntropy(words, wordlistSize)` against the _picked_ words (not the post-decoration string), so the reported figure reflects the Diceware contribution. Decoration adds a small amount of entropy on top of that, but the package does not currently roll that into the reported figure to keep the formula stable across runs.