rusty-pwgen
Generate pronounceable or random passwords from the OS CSPRNG. Rust port of Theodore Ts'o's pwgen 2.08.
Default mode adds --help, --version, completions, & parse-time conflict detection. Strict mode reverts every observable surface to byte-equal pwgen 2.08 (including last-wins flag resolution). The -H reproducible-mode chain is locked at SHA256 + ChaCha20Rng so seed files keep producing the same password.
Part of the Rusty portfolio.
Install
# or, with prebuilt binaries:
# or, download directly from GitHub Releases:
# https://github.com/jsh562/rusty-pwgen/releases
To also install a pwgen binary alias (argv[0] auto-detect routes into Strict mode):
Usage
# Default 8-char pronounceable, columnar TTY output
# One 12-char pronounceable password (easy to type, lower entropy)
# One 16-char cryptographically random password (higher entropy, harder to type)
# 20-char random with symbols (passwords that need to resist offline cracking)
# Five passwords with ambiguous chars (0/O, 1/l/I) removed (good for verbal handoff)
# Reproducible mode: same seed file + same pin produce the same password
# Strict pwgen-compat mode (drop-in pwgen 2.08 replacement, last-wins flag resolution)
RUSTY_PWGEN_STRICT=1
# Shell completions
Library API
The library exposes pronounceable & secure modes without subprocesses, TTY detection, or CLI parsing. Those live behind the cli feature.
use ;
let mut pwgen = new
.length
.secure
.symbols
.build?;
let password: String = pwgen.generate_one;
let batch: = pwgen.generate_n;
let streaming = pwgen.iter.take.count;
# Ok::
For library-only consumers without CLI deps see the Cargo Features section.
Cargo Features
default enables full, which (for this single-capability port) resolves to the cli umbrella. pwgen-classic reproduces v0.1.x bare-port behavior matching upstream pwgen 2.08 1:1. To strip the CLI surface use default-features = false or --no-default-features & add the features you want.
rusty-pwgen is a single-capability port: its one documented job is "generate pronounceable or random passwords from the OS CSPRNG". No optional feature leaves are carved beyond the required umbrellas; see docs/feature-layout.md for why.
Feature matrix
| Feature | Description | Umbrella(s) |
|---|---|---|
cli |
All CLI-only dependencies (clap, clap_complete, anyhow, terminal_size) and the binary entry point, mode resolver, output formatter, seed-input resolver, and Strict-mode pre-scanner. Library consumers strip via default-features = false. |
full, pwgen-classic, pwgen-minimal, pwgen-alias |
pwgen-alias |
Installs an additional pwgen binary alongside rusty-pwgen. Both share source; argv[0] auto-detect routes pwgen invocations into Strict mode. PATH-collision behavior with upstream pwgen is documented in Compatibility below. |
(standalone, implies cli) |
bench |
Pulls criterion and enables benches/throughput.rs. Dev-tooling only; outside the convention's leaf surface. Name preserved verbatim from v0.1.x. |
(standalone) |
Preset bundles
| Bundle | Composition | Use case |
|---|---|---|
pwgen-classic |
cli |
Drop-in upstream pwgen 2.08 replacement. Strict mode is invoked via --strict, RUSTY_PWGEN_STRICT, or pwgen-alias argv[0] auto-detect. |
pwgen-minimal |
cli |
Explicit minimal-CLI alias for users who prefer the <port>-minimal naming convention seen across other portfolio ports. Identical composition to pwgen-classic. |
Keep-list workaround (Cargo features are union-only)
Cargo features cannot subtract from default. To get "everything except a specific feature," disable defaults & enumerate the features you want:
# → bare CLI with no pwgen alias, no bench tooling.
# → CLI + the pwgen alias binary.
For the common cases the named preset bundles are usually sufficient.
Library-only consumers
[]
= { = "0.2", = false }
This strips clap, clap_complete, anyhow, & terminal_size. The resulting build pulls only rand, rand_chacha, sha2, & thiserror (all required by the always-on library API: OS CSPRNG sourcing, ChaCha20 seeded source for -H, SHA256 hashing for seed input, & error types).
Convention authority
This layout follows the portfolio-wide Cargo Features Convention. The "why" lives in ADR-0006; the "what" lives in project-instructions.md §Cargo Feature Surface. Every Rusty port from v0.2 onward exposes the same umbrella set (default / full / cli / <port>-classic), per-port leaves named in kebab-case, & 2 to 4 preset bundles.
Compatibility
rusty-pwgen has two modes:
- Default mode. clap-styled flag parser.
--help,--version, & thecompletionssubcommand are all available. Conflicting flag pairs (-c -A,-n -0,-1 -C) MUST be rejected at parse time with a clear error. - Strict mode (activated by
--strict,RUSTY_PWGEN_STRICT=1, or invoking the binary aspwgen). Byte-equal stdout, stderr, & exit codes against pwgen 2.08 (Debian stable 2026-05). Last-wins flag resolution preserved.--help,--version, &completionsMUST be rejected.
Cryptographic safety
rusty-pwgen uses rand::rngs::OsRng, which wraps getrandom(2) / BCryptGenRandom / CCRandomGenerateBytes. This is appropriate for general-purpose passwords. It MUST NOT be treated as a substitute for dedicated crypto libraries when generating long-lived key material.
Entropy guidance
Pronounceable mode trades entropy for memorability. At default settings (length 8, capitals + numerals on, no symbols), pronounceable passwords carry around 30-32 bits of entropy vs around 48 bits for -s (secure) mode at the same length. Use -s when memorability isn't required.
Documented intentional divergences
- Default-mode conflict rejection. Upstream pwgen accepts conflicting flag pairs via last-wins; rusty-pwgen Default rejects at parse time. Strict mode preserves upstream last-wins.
-Huses SHA256 -> 32-byte ChaCha20 seed, not SHA1. ChaCha20Rng wants 32 bytes natively, so SHA256 fits without padding. The chain is locked at v0.1.0; any change is a MAJOR bump.-Hreproducible mode is not cryptographically appropriate for high-value secrets. Output is deterministic; an attacker with the same seed file produces the same password.- Unknown-flag stderr in Strict. Emits only the first unknown-flag error. Upstream's
Getopt::Longiterates per character; we don't replicate that. - No
-a/--alt-phonics(legacy no-op upstream; omitted entirely). - No "naughty word" filter. Upstream has an optional compile-time substring blocklist; v0.1.0 omits it. Forward-review candidate.
- No
--secure-source. Upstream's flag toggling/dev/randomvs/dev/urandomis Linux-only & meaningless on modern kernels; rusty-pwgen always usesOsRng.
See docs/COMPATIBILITY.md for the full per-flag matrix.
pwgen-alias PATH-collision warning
Building with --features pwgen-alias installs a second binary named pwgen alongside rusty-pwgen. If upstream pwgen is also on the same PATH, whichever directory comes first wins. Invoke rusty-pwgen (always unambiguous) or omit the pwgen-alias feature when upstream pwgen is also present.
What's not shipped
-a/--alt-phonics. Legacy no-op upstream; omitted entirely.- "Naughty word" substring blocklist. Upstream has an optional compile-time list; v0.1.0 omits it. Forward-review candidate.
--secure-sourceflag. Linux-only & meaningless on modern kernels; rusty-pwgen always usesOsRng.
MSRV
Rust 1.85 (edition 2024 floor; std::io::IsTerminal stable since 1.70).
License
Dual-licensed under MIT or Apache-2.0 at your option.