โโโโโโโ โโโโโโ โโโโโโโโโโโโโโโโ โโโ โโโ โโโโโโ โโโ โโโโโโ โโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโ โโโโโโโโโโโโโโ โโโโโโ โโโ
โโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโ โโโโโโโโโโโโโโโ โโโโโโ โโโ
โโโ โโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโ โโโ โโโโโโโโโโโโโโโโโโโโ โโโ
โโโ โโโ โโโโโโโโโโโโโโโโโโโ โโโโโ โโโ โโโ โโโโโโโ โโโโโโโโ โโโEncrypted command-line password manager โ Argon2id key derivation, AES-256-GCM authenticated encryption, atomic durable writes, advisory file locking. One master password protects every credential you trust to it.
This is a quick overview โ security theory, architecture, and full walkthroughs are in the learn modules.
Note
Foundations tier โ the hardest of the three. This is a stepping stone into the beginner tier. It assumes no prior Python experience but ramps faster than hash-identifier and http-headers-scanner. The source is heavily commented as a teaching aid, the learn/ folder explains every cryptographic idea from zero, and every Python feature is introduced when it first appears. If "what's a @dataclass" feels like the wrong question, start with hash-identifier first.
- Stores credentials in a single encrypted JSON file at
~/.password-vault/vault.json(mode0600) - Derives a 32-byte AES key from your master password via Argon2id (OWASP-recommended parameters, ~0.5s per derivation)
- Encrypts vault contents with AES-256-GCM โ confidentiality + tamper detection in one primitive
- Atomic, durable, concurrent-safe writes: tmp file โ fsync โ atomic rename โ directory fsync, with advisory
fcntllock to serialize concurrentpvinvocations - Master password rotation that re-encrypts the entire vault under a fresh salt and key
- Cryptographically secure password generator using
secrets(neverrandom) with a Fisher-Yates shuffle on top ofsecrets.randbelow - Stores KDF parameters in the file โ old vaults remain readable when defaults change, and rotation can upgrade them transparently
- Typed exception hierarchy (
WrongPasswordError,VaultFormatError,EntryNotFoundError, โฆ) for precise error handling - Rich-rendered colored panels and tables; pipe-friendly stdout/stderr separation
- Refuses to distinguish "wrong password" from "tampered file" โ both look the same cryptographically, exposing the difference helps attackers
./install.sh
just run -- init
just run -- add github
just run -- get github$ pv init
New master password: ************
Confirm master password: ************
Vault created at /home/you/.password-vault/vault.json
$ pv add github
Username for github: alice
Password for github (hidden): ************
URL (optional, press Enter to skip): https://github.com
Notes (optional, press Enter to skip):
Added entry: github
$ pv get github
โญโโโโโโโโโโโโโโโโโโ github โโโโโโโโโโโโโโโโโโโฎ
โ username alice โ
โ password hunter2-but-better โ
โ url https://github.com โ
โ created 2026-05-13T14:22:10+00:00 โ
โ updated 2026-05-13T14:22:10+00:00 โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Tip
This project uses just as a command runner. Type just to see all available recipes.
Install: curl -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin
| Command | What it does |
|---|---|
pv init |
Create a new empty vault. Prompts for the master password twice. |
pv add <name> |
Add an entry. Prompts for username, password, optional URL and notes. --generate / -g to use a random password. |
pv get <name> |
Show every field of one entry in a colored panel. |
pv list |
Print every entry name as a table (no passwords shown). |
pv delete <name> |
Remove an entry by name. |
pv gen [length] |
Generate a strong random password and print it to stdout. No vault required. |
pv change-password |
Rotate the master password โ re-encrypts the entire vault under a fresh salt and key. |
Every command takes --vault PATH (or $PV_VAULT) to point at an alternate vault file.
# Generate and copy to clipboard (macOS)
just run -- gen 32 | pbcopy
# Generate and copy to clipboard (Linux)
just run -- gen 32 | xclip -selection clipboard
# Generate into a shell variable
PASSWORD=$(just run -- gen 32)
# Letters + digits only, no symbols
just run -- gen 24 --no-symbolsImportant
pv never accepts the master password as a CLI flag. Passwords passed as flags leak into shell history (history command) and process listings (ps aux). Every prompt uses getpass.getpass() โ same primitive sudo uses โ so the password is never echoed and never logged.
| Concern | Mitigation |
|---|---|
| Vault file stolen | Argon2id with 64 MiB / 3 passes / 4 lanes makes each guess ~0.5s; a billion guesses โ 15 years |
| Vault file tampered | AES-GCM authentication tag refuses to decrypt; same error as "wrong password" by design |
| Power loss mid-save | Atomic write: tmp โ fsync โ os.replace โ parent-dir fsync. Always old-or-new, never half |
Two pv processes racing |
Advisory fcntl.LOCK_EX on sidecar .lock file (POSIX; NTFS atomic-rename on Windows) |
| Vault tmp world-readable | os.open with mode 0o600 at the very first syscall โ no chmod race window |
| Predictable random output | secrets module everywhere โ for salts, nonces, passwords, and the Fisher-Yates shuffle |
| Aging KDF parameters | Parameters stored in the vault file; change-password can upgrade them transparently |
| KDF parameter corruption | Validated against Argon2's algorithmic floors on load; clean VaultFormatError instead of library crash |
| Forward-incompatible format | Top-level version field; future versions can refuse or migrate |
What this project does not defend against โ and why โ is documented honestly in learn/01-CONCEPTS.md ยง12.
just # list available recipes
just test # run pytest (60+ tests across crypto, vault, generator)
just test-cov # tests + coverage report
just lint # ruff + mypy + pylint
just format # yapf
just run -- <cmd> [args]- Python 3.13+ โ the install script will check.
uvโ modern Python package manager (auto-installed by./install.sh).justโ command runner (auto-installed by./install.sh).- Linux, macOS, or WSL2 strongly recommended over native Windows โ file locking and directory
fsyncpaths are POSIX-flavored. NTFS gives atomicos.replaceregardless, so native Windows works with reduced concurrency guarantees.
No compilers or system libraries beyond what argon2-cffi and cryptography install through uv. No network access required at runtime.
This project includes step-by-step learning materials covering the security theory, architecture, and implementation โ written for someone who has never touched Python or cryptography before. Read them in order.
| Module | Topic |
|---|---|
| 00 - Overview | Quick start, prerequisites, project layout, common problems |
| 01 - Concepts | What encryption is, KDFs, Argon2id, salts, AES-GCM, nonces, the threat model, real breaches |
| 02 - Architecture | Five-file layout, on-disk format, per-command flow diagrams, the atomic-write pipeline |
| 03 - Implementation | Line-by-line walkthrough of every source file โ every Python feature explained when first encountered |
| 04 - Challenges | Fifteen extension ideas across four tiers, from a search command to porting the vault format to another language |
PROJECTS/foundations/hash-identifierโ the easiest foundations project. Start here if password-manager feels too dense.PROJECTS/foundations/http-headers-scannerโ the middle foundations project; covers HTTP and basic I/O.PROJECTS/beginner/hash-crackerโ the natural cracking companion. Once you understand why Argon2id is slow, that project shows you what it's slowing down.- OWASP Password Storage Cheat Sheet โ the authoritative reference for the parameter choices in
constants.py. ageโ a production-quality file encryption tool that makes many of the same trade-offs as this project at a much larger scale.
AGPL 3.0