passvault is a local encrypted password store with label-based lookup.
It uses session-based authentication: you must login before vault operations.
python3 -m venv .venv
source .venv/bin/activate
pip install -e .passvault also requires a local openssl binary to maintain the recovery bundle.
Initialize a new vault:
passvault initpassvault init creates a random vault key, asks you to set the initial master passphrase,
prints a 20-word recovery seed phrase, and creates an OpenSSL-encrypted recovery bundle that
contains every stored password with its labels.
Store the seed phrase offline. It can unlock the vault even after the master passphrase changes, and it can decrypt the recovery bundle outside this application.
Login with the master passphrase:
passvault loginLogin with the recovery seed phrase instead:
passvault login --seed-phraseLogin with a custom session TTL:
passvault login --ttl 12hLogout (invalidate active session):
passvault logoutRebuild the OpenSSL recovery bundle from the current vault contents:
passvault sync-recoveryThis is normally automatic after add, generate, and update. Use sync-recovery if OpenSSL was missing
or if a previous write reported that the recovery bundle is stale.
Change the master passphrase (requires an active session):
passvault change-master-passwordThis command prompts for the new master passphrase, rewrites only the master-password wrapper in
vault.json, and logs you out. Existing stored passwords stay encrypted under the same vault key,
so the seed phrase and recovery bundle remain valid across password changes.
Add a password (requires an active session):
passvault add service=gmail account=workAdd with an explicit password argument:
passvault add service=gmail account=personal --password 'example-secret'Retrieve when one match exists:
passvault get service=gmail account=workRetrieve when multiple matches exist:
passvault get service=gmailIf there are multiple matches, passvault prints the matching labels and prompts you to choose one.
When a password is retrieved, it is copied to your clipboard using pbcopy instead of being printed.
Generate and store a new secure password for a label set:
passvault generate service=gmail account=workGenerate with an explicit length:
passvault generate service=gmail account=work --length 40Update an existing password:
passvault update service=gmail account=workUpdate with an explicit password:
passvault update service=gmail account=work --password 'new-secret'Generate a replacement password, store it, and copy it to the clipboard:
passvault update service=gmail account=work --generateGenerate a replacement password with an explicit length:
passvault update service=gmail account=work --generate --length 40If more than one entry matches the supplied labels, passvault update shows the matches and asks you
to choose which password to replace.
List all services:
passvault listList all key/value labels found under a given service:
passvault list --service gmailVault data is stored in ./.passvault (inside this repository by default):
vault.json: wrapped vault key metadata plus an encrypted copy of the recovery seed phrasesession.json: active session metadata (session id + session expiry)passwords.json: encrypted entries (AES-GCM)recovery.json.enc: OpenSSL-encrypted recovery export with labels and passwords
Session keys are stored in the OS keychain (via keyring), not in session.json.
Override the location with PASSVAULT_DIR.
The simplest disaster-recovery path is the OpenSSL bundle in recovery.json.enc.
It is encrypted directly from the recovery seed phrase using:
openssl enc -aes-256-cbc-pbkdf2-iter 600000
Decrypt it with:
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 -in .passvault/recovery.json.encOpenSSL will prompt for the recovery seed phrase. The output is JSON containing every entry, including labels and passwords, for example:
[
{
"expires_at": null,
"id": "0e2c3c5f-4a2f-4cbe-8b9f-6db4f4fbfa5d",
"labels": {
"account": "work",
"service": "gmail"
},
"password": "example-secret"
}
]If you want to save the recovered JSON to a file:
openssl enc -d -aes-256-cbc -pbkdf2 -iter 600000 \
-in .passvault/recovery.json.enc \
-out recovered-passwords.jsonThe normal vault format still exists in vault.json and passwords.json.
vault.json stores:
master_wrap: the vault key encrypted with a key derived from the master passphraserecovery_wrap: the same vault key encrypted with a key derived from the recovery seed phraseseed_phrase_backup: the recovery seed phrase encrypted under the vault key so logged-in sessions can keeprecovery.json.encsynchronized automatically
The underlying entry encryption is still AES-256-GCM with a random 32-byte vault key. The recovery bundle is an additional export whose purpose is simple OpenSSL-based recovery.