pwmanager is a local, offline-first password manager. It is designed to protect stored credentials against:
- Casual inspection of the vault file on disk
- Offline brute-force of a strong master password (via Argon2id / high-iteration PBKDF2)
- Simple tampering of the vault wrapper (HMAC over salt + ciphertext; Fernet authenticated encryption of the payload)
It is not designed to resist:
- An attacker with unrestricted access to your unlocked session (decrypted entries live in process memory)
- Malware, keyloggers, or a compromised OS
- Physical memory forensics / cold-boot attacks
- Side-channel attacks against the Python runtime
- Cloud sync, multi-device compromise, or phishing of the master password
- Advanced adversaries who can modify the program while you use it
Bottom line: treat this as a solid personal/hobby tool. For high-stakes credentials (primary email, banking, work SSO), prefer mature audited products such as Bitwarden, 1Password, or KeePassXC.
| Good fit | Poor fit |
|---|---|
| Learning crypto / local vaults | Enterprise secret management |
| Small personal credential sets | Shared team vaults |
| Air-gapped or single-machine use | Sync across untrusted devices |
| Generating strong passwords & TOTP | Storing files/attachments |
| Optional HIBP check when online | Relying on breach checks offline |
| KDF | Parameters | Notes |
|---|---|---|
Argon2id (default when argon2-cffi is installed) |
time=3, memory=64 MiB, parallelism=4, hash_len=32 | Preferred |
| PBKDF2-HMAC-SHA256 | 600,000 iterations, hash_len=32 | Fallback if Argon2 unavailable |
Salt: 16 random bytes, stored in the vault file (not secret).
Derived key is urlsafe-base64-encoded for Fernet.
- Fernet (AES-128-CBC + HMAC-SHA256) encrypts the JSON map of entries.
- A separate HMAC-SHA256 over
salt|vault(using the derived key) detects tampering of the on-disk wrapper. Wrong master password and tampering both surface as decryption failure (InvalidToken). pwmanager verifyre-reads the vault file, recomputes the HMAC, and confirms the ciphertext still decrypts — without listing entry contents.
{
"version": 2,
"kdf": "argon2id",
"salt": "<base64>",
"vault": "<Fernet token>",
"hmac": "<hex sha256>"
}Entry fields (inside the encrypted payload): username, password, url,
notes, tags, totp_secret, history, created_at, updated_at,
favorite (optional, default false), kind (login | note, default login),
rotate_after_days (optional, default null → global 90-day rotation window),
last_accessed (optional, default 0).
Older vaults load with safe defaults for new fields (Entry.from_dict).
pwmanager audit --hibp (or interactive h) can check whether stored
passwords appear in known breach corpora using the HIBP Pwned Passwords
range API (k-anonymity model).
| Sent | Not sent |
|---|---|
First 5 hex characters of SHA-1(password) |
The password itself |
| The remaining hash suffix | |
| Entry names, usernames, or vault paths | |
| Master password |
- Locally compute
SHA-1of the password (UTF-8). - HTTP GET
https://api.pwnedpasswords.com/range/{prefix}with only the 5-char prefix. - Compare the local hash suffix against the returned list of
SUFFIX:COUNTlines. - Report entry names that match (never print the password or full hash).
If the network is unavailable, DNS fails, or the request times out, the audit reports skipped (network unavailable) and continues. HIBP is never required for normal vault use.
- This is the only optional outbound network call in pwmanager.
- Do not use
--hibpon a hostile network if you are concerned about traffic analysis of hash prefixes (theoretical risk; prefixes alone do not reveal passwords). - Prefer running HIBP checks on a trusted connection.
By default, the master password is always read via an interactive prompt
(getpass). For automated tests or tightly controlled pipelines only, you may
pass --password-env, which reads the master password from the
PWMANAGER_PASSWORD environment variable.
| Do | Don't |
|---|---|
| Use only with disposable test vaults | Put production master passwords in env files, Compose, or shell rc |
| Unset the variable immediately after use | Log or echo PWMANAGER_PASSWORD |
| Prefer OS keychain / secret stores outside this tool | Commit env files that contain real passwords |
| Keep the flag off for daily interactive use | Assume process environment is private on multi-user hosts |
Environment variables are visible to other processes with sufficient privilege, appear in some crash dumps, and are easy to leak via CI logs. This flag is an explicit insecure opt-in — not a recommended production unlock method.
Both commands write passwords, notes, and TOTP secrets in cleartext.
- Interactive use requires typing
YES(all caps). - Scripts must pass
--i-understand. - Prefer encrypted
exportfor backups and machine moves. - Delete plaintext files when migration is finished.
- Never commit
vault.json,*.vault.json, CSV, or JSON exports to git (see.gitignore).
- Master password — long passphrase (≥ 5 random words) or ≥ 16 chars with high entropy. There is no recovery.
- Install Argon2 —
pip install "pwmanager[full]"so Argon2id is used. Runpwmanager doctorto confirm. - Permissions — keep vault files on an encrypted volume; restrict file mode (
chmod 600). - Backups — use encrypted export; never commit vault files to git.
- Plaintext CSV/JSON export — treat as highly sensitive; delete when finished.
- Clipboard —
get --copyand interactive copy use auto-clear (--clipboard-timeout); still avoid shared machines. - Auto-lock — idle lock (default 5 minutes) clears the terminal screen in interactive mode; lock manually when stepping away.
- Rotation — audit flags passwords older than 90 days (or per-entry
rotate_after_days); usetouchafter rotating. - Updates — keep
cryptographyand Python patched. - Audit / stats / HIBP / verify — run
auditregularly;verifyafter copies or sync; use--hibpwhen online. - Profiles — keep separate vaults for work/personal under
~/.config/pwmanager/; do not sync vaults via unencrypted cloud folders. - Memory — Python strings cannot be securely wiped; assume secrets may linger until process exit.
- History / TOTP watch — history browser shows previous passwords only while unlocked; live TOTP is terminal-only.
- Automation — avoid
--password-envexcept for ephemeral test vaults.
Open a private security advisory or issue on the GitHub repository. Please do not include real passwords or vault files in public reports.
This project is provided under the MIT license as-is, without warranty. Security fixes are welcome via pull request.