Skip to content
vortex2jmPublic

About

An offline and encrypted vault manager CLI

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

60 Commits

Folders and files

Repository files navigation

🔐 Vault

A local, offline CLI password manager written in Rust. Vault stores your credentials in encrypted files on disk using AES-256-GCM and derives keys with Argon2id — so your master password never leaves your machine.


Features

  • AES-256-GCM authenticated encryption
  • Argon2id key derivation (memory-hard, resistant to brute force): 128 MiB and 3 passes, recorded in each vault so the cost can grow without breaking old vaults
  • Multiple named vaults — one per context (work, personal, etc.)
  • Tab-completion of commands, entry names, vault names and file paths, and command history in the interactive shell
  • Interactive selector (find): browse your entries with the arrow keys and fuzzy search
  • Notes and password history on each entry: the last 10 passwords are kept, with the date each one was replaced
  • Auto-lock: an unlocked vault left alone at the prompt for 5 minutes locks itself (with uncommitted changes it only warns)
  • Health report (health): reused and short passwords, and the oldest ones
  • Password generator: random passwords (optionally without symbols or look-alike characters) and passphrases from the EFF word list, with their strength in bits
  • Careful clipboard: copy keeps the password out of clipboard managers' history and clears it after 30 s
  • Commit model: changes stay pending until commit; status lists them, discard drops them, and restore brings back the version the last commit replaced
  • Crash-safe saves: every commit writes a new file and atomically swaps it in, keeping the previous version as a backup
  • Safe across sessions: open the same vault in two terminals; a commit never overwrites what the other one saved
  • Strict Security: Memory zeroization of the key and every secret (best effort: see the known gaps), no core dumps, no debugging or memory reads by your other programs (Linux), owner-only permissions on Unix (0700 directory; 0600 vaults, backups and exports), and path traversal protection.
  • Zero network access — entirely offline

Requirements

  • Rust 1.89 or newer (edition 2024), only to build from source
  • Linux/Unix: copy needs a graphical session, X11 or XWayland under Wayland (arboard talks to X11, with no system libraries to install). Without one, everything else works and copy reports the clipboard as unavailable.

Installation

A ready binary (Linux x86_64, static: no dependencies): download vault-v0.1.0-x86_64-linux.tar.gz and SHA256SUMS from the latest release, then

sha256sum -c SHA256SUMS
tar -xzf vault-v0.1.0-x86_64-linux.tar.gz
install vault-v0.1.0-x86_64-linux/vault ~/.local/bin/

From source, with Rust 1.89 or newer:

cargo install --git https://github.com/vortex2jm/vault --tag v0.1.0

Or from a clone:

git clone https://github.com/vortex2jm/vault
cd vault
cargo install --path .

Releases and what changed in each: CHANGELOG.md; how they are made: RELEASING.md.


Quick Start

--- Vault CLI ---

vault[locked]> create personal
New vault password: ••••••••
Confirm password: ••••••••
Vault 'personal' created.

vault[personal|0]> gen 16
Generated (≈98 bits): K9#mP2$qLv8nW!j&

vault[personal|0]> add github myuser
Service password: ••••••••
Notes (optional): 2FA on my phone
Entry 'github' added.

vault[personal*|1]> commit
Changes committed.

vault[personal|1]> copy github
Password for 'github' copied to clipboard (cleared in 30 s).

vault[personal|1]> lock
Vault locked.

vault[locked]>

Commands

Arguments with spaces go in quotes, as in a shell: add "My Bank" "joao silva", get "My Bank", export "~/My Files/vault.csv". Tab writes the quotes for you. Single quotes work too, and outside them \ keeps the next character as it is (My\ Bank).

Command Arguments Description
Core
create <name> Create a new vault (asks for the password twice; at least 8 characters)
unlock <name> Unlock an existing vault
lock — Lock the current vault (prompts to commit if there are unsaved changes)
passwd — Change the current vault's master password (at least 8 characters)
drop <name> Permanently delete a vault and its backup (vault must be locked)
Entries
add <service> <user> Add a credential entry (asks for its password and optional notes)
get <service> Show a credential entry (username, password and notes)
find [term] Pick an entry from an interactive list (↑/↓ to move, type to filter), then copy, show, update or remove it, or see its history
history <service> Show an entry's previous passwords, newest first, with the date each was replaced (the last 10 are kept)
copy <service> [user] Copy a password (or, with user, the username) to the clipboard, out of clipboard managers' history; cleared after 30 s or when vault exits
update <service> Update an entry: blank keeps the username or password; a new password sends the old one to the history; the notes come pre-filled to edit
mv <service> <new> Rename an entry (password, notes and history stay)
rm <service> Remove a credential entry
gen [length] [nosymbols] [noambiguous] Generate a random password (8–256 chars, default 20), without symbols or look-alike characters (Il1O0o) if asked; shows its strength in bits
gen words [count] Generate a passphrase of 4–20 words (default 6, ≈78 bits), such as maple-orbit-cactus-violin-ember-tundra
Data & I/O
commit — Save changes to disk
status — Show what changed since the last commit (added, removed, renamed, changed fields)
discard — Drop the uncommitted changes, keeping the vault open (asks first)
health — Report reused passwords and passwords under 12 characters, and list the 5 oldest by when they were set. Never shows a password
restore — Load the backup (the version the last commit replaced) as uncommitted changes: status to review, commit to keep, discard to undo
rename <new_name> Rename the current vault file
ls/list — List vaults (when locked) or entries (when unlocked)
info — Show the vault name, entry count and key strength
export <path> Export entries to a CSV file (Warning: Plaintext!). Created 0600; never overwrites an existing file
import <path> Import entries from a CSV file (merges new services; all or nothing, see below)
CLI
clear — Clear the screen and the scrollback (hides passwords shown by get)
help — Print help
exit — Exit (prompts to commit if there are unsaved changes)

Prompt Legend

vault[locked]>                    # vault is locked
vault[myname|3]>                  # unlocked, 3 entries
vault[myname*|3]>                 # unlocked, unsaved changes

Auto-Lock

An unlocked vault left at the prompt for 5 minutes without a command locks itself, wiping the key and the entries from memory. If it has uncommitted changes, it stays open instead and says so once: locking would throw them away, so committing or discarding them is up to you.

Set the time with VAULT_AUTO_LOCK, in seconds (0 turns it off):

VAULT_AUTO_LOCK=120 vault    # lock after 2 minutes

The time only runs at the main prompt: in the middle of a command (a password prompt, the find list) nothing locks.


Storage Layout

All vault files are stored in ~/.vault/. On Unix the directory is 0700 and every file 0600; if an older version left them more open, they are tightened on the next commit:

~/.vault/
├── .lock              # taken during each commit, so sessions never write at once
├── personal.vault     # encrypted vault
├── personal.bkp       # previous version, kept by each commit (`restore` loads it)
└── work.vault

Each .vault file (format v3) is a small header followed by the encrypted entries (service, username, password, notes, previous passwords and timestamps):

  • VAULT\0 and the format version;
  • the Argon2id cost used for this vault's key (memory, passes, lanes);
  • salt: 16 random bytes, new at creation and at each password change;
  • nonce: 12 random bytes, new at every commit;
  • the AES-256-GCM ciphertext of the entries. The header is authenticated with it, so changing any byte makes the vault refuse to open.

Vaults written by older versions (v0 to v2) still open. On unlock they are rewritten in the current format, with a stronger key if theirs was cheaper, and the previous file stays as the .bkp until the next commit. The byte-level layout is in docs/SECURITY.md.


CSV Export/Import Format

The export and import commands use standard RFC 4180 CSV files with a header row:

service,username,password,notes
github,myuser,mysecret123,
aws,admin,"super,secure!",root account
  • import finds the columns by name, in any order and any case: service, username and password are required, and notes is optional (exports from older versions don't have it). Other columns are ignored.
  • Every row has as many fields as the header. A field with a comma or a quote must be quoted, as in the aws row above.
  • Service names can have spaces (type them in quotes), but can't be empty or contain control characters. Spaces around a name are trimmed.
  • Import is all or nothing: if a row is invalid, the error names its line and no entry is added.
  • Importing merges entries: if a service already exists in the vault, or appears twice in the file, the first version is kept and the other row is skipped.
  • The password history is not exported.
  • Don't round-trip an export through a spreadsheet. Opening the CSV in Excel, LibreOffice or Google Sheets and saving it again turns values like 0042 into 42, long numbers into 1.2E+15 and some text into dates. Edit it in a text editor, or import it into the spreadsheet as text.

License

MIT.


Credits

Passphrases use the EFF large wordlist (src/domain/eff_large_wordlist.txt, unchanged), by the Electronic Frontier Foundation, under CC BY 3.0 US.


Further Reading

  • Architecture — module design, data flow, hexagonal pattern
  • Security Model — cryptographic choices, threat model, limitations
  • Testing — test layout, how to run it, conventions

About

An offline and encrypted vault manager CLI

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages