Ultra-secure local API key wallet β AES-256-GCM Β· Argon2id Β· HMAC Integrity Β· Zero cloud dependency
VaultKey stores your API keys locally in an encrypted binary file (wallet.enc).
No cloud sync. No telemetry. No vendor lock-in. Your keys never leave your machine.
Every key is encrypted with a unique per-entry subkey derived via HKDF-SHA256 from your master key.
Compromising one entry reveals nothing about any other.
Three interfaces β one vault:
wallet CLI for scripts Β· wallet tui full-screen terminal Β· wallet gui desktop app
| Layer | Technology | Parameters |
|---|---|---|
| Key Derivation | Argon2id | 64 MB RAM Β· 3 iterations Β· 4 threads |
| Encryption | AES-256-GCM | 96-bit nonce Β· 128-bit AEAD tag |
| Per-entry subkeys | HKDF-SHA256 | Domain salt + entry UUID info |
| Password verification | Argon2id hash | Separate salt, stored encrypted |
| Integrity manifest | HMAC-SHA256 | Over all entry IDs + ciphertexts |
| AAD binding | SHA-256(wallet_path) | Prevents file relocation attacks |
| Memory security | ctypes.memset | Key zeroed on lock / timeout |
| File permissions | chmod 600 | User-only read/write |
| Atomic writes | temp + os.replace() | No partial writes on crash |
| Audit log | JSON-Lines | Per-event PID + user + timestamp |
| Brute-force protection | Exponential backoff | 1 β 2 β 4 β 60 s hard lockout |
| Clipboard hygiene | Auto-clear | Cleared after 30 s (configurable) |
# Install
pip install vaultkey
# Initialize a new wallet (sets master password, creates wallet.enc)
wallet init
# Unlock the session (15-min auto-lock)
wallet unlock
# Add an API key
wallet add --name "OpenAI Production" --tags "ai,production" --expires 2026-12-31
# Copy to clipboard (auto-clears after 30 s)
wallet get "OpenAI Production"
# Switch between vault profiles (work / personal / staging)
wallet profile use work
# Share a key securely via a one-time encrypted token
wallet share "OpenAI Production" --expires 1h
# Register a webhook for expiry/rotation events
wallet webhook add https://hooks.slack.com/... --events expiry,rotate
# Rotate all keys in bulk
wallet rotate-all --tag production --dry-run
# Watch for expiry in the background (daemon)
wallet watch-expiry --interval 3600
# Health report across all keys
wallet health
# Launch interactive TUI
wallet tui
# Launch desktop GUI
wallet gui
| Command | Description |
|---|---|
wallet init |
Create a new encrypted wallet |
wallet unlock |
Unlock β derive session key from master password |
wallet lock |
Lock and zero the session key from memory |
wallet status |
Session state, key counts, pending warnings |
wallet change-password |
Re-key entire wallet under new master password |
wallet wipe |
Emergency β 3-step confirmation β secure destroy wallet + backups |
| Command | Description |
|---|---|
wallet profile list |
List all available vault profiles |
wallet profile create <name> |
Create a new isolated vault profile |
wallet profile use <name> |
Switch active profile (all commands use this vault) |
wallet profile delete <name> |
Delete a profile and its wallet file |
wallet profile current |
Show the currently active profile name |
| Command | Description |
|---|---|
wallet add |
Add a new API key (prompts for value, never stored raw) |
wallet get <name> |
Copy key to clipboard (auto-clear 30 s) |
wallet get <name> --show |
Show masked value in terminal |
wallet get <name> --raw |
Print raw value (for piping / scripts) |
wallet get <name> --env |
Print as export SERVICE_API_KEY=value |
wallet list |
List all keys β no values ever shown |
wallet list --tag ai |
Filter by tag |
wallet list --service openai |
Filter by service |
wallet list --sort expires |
Sort by expiry date |
wallet list --expired |
Show only expired keys |
wallet info <name> |
Full metadata + health score for one key |
wallet rotate <name> |
Replace key value (new nonce, same entry ID) |
wallet rotate-all |
Rotate all keys in bulk (prompts per-key) |
wallet rotate-all --tag <tag> |
Rotate only keys matching a tag |
wallet rotate-all --dry-run |
Preview which keys would be rotated |
wallet rename <name> --to <new> |
Rename a key (metadata-only, no re-encryption) |
wallet delete <name> |
Delete with double confirmation |
wallet tag <name> --add prod |
Add tags without re-encryption |
wallet tag <name> --remove old |
Remove tags |
wallet search <query> |
Fuzzy search across name, service, tags, description |
| Command | Description |
|---|---|
wallet share <name> |
Generate a one-time encrypted share token |
wallet share <name> --expires 1h |
Token auto-expires after duration (1h / 24h / 7d) |
wallet share <name> --uses 1 |
Token valid for N redemptions only |
wallet share-receive <token> |
Decrypt and import a received share token |
wallet share-list |
List active outbound share tokens |
wallet share-revoke <token-id> |
Revoke a share token before it expires |
| Command | Description |
|---|---|
wallet webhook add <url> |
Register a webhook endpoint |
wallet webhook add <url> --events expiry,rotate |
Filter to specific event types |
wallet webhook list |
List registered webhooks |
wallet webhook remove <id> |
Remove a webhook by ID |
wallet webhook test <id> |
Send a test payload to verify the endpoint |
| Command | Description |
|---|---|
wallet health |
Wallet-wide health scores + rotation recommendations |
wallet health --all |
Show all entries, not just problematic ones |
wallet expiry-check |
List keys expiring within 7 days (color-coded urgency) |
wallet expiry-check --days 30 |
Custom look-ahead window |
wallet expiry-check --all |
Show all expiry status, not just warnings |
wallet watch-expiry |
Background daemon β polls expiry, prints warnings |
wallet watch-expiry --interval N |
Poll interval in seconds (default: 3600) |
wallet audit |
View structured audit log (last 50 events) |
wallet audit --event GET |
Filter by event type |
wallet audit --failed |
Show only FAIL events |
wallet verify |
Structural + HMAC integrity check |
| Command | Description |
|---|---|
wallet completion install |
Auto-detect shell and install completions |
wallet completion install --shell bash |
Install for a specific shell (bash/zsh/fish) |
wallet completion show |
Print completion script to stdout |
| Command | Description |
|---|---|
wallet export --output backup.enc |
Encrypted export with separate password |
wallet import <file> |
Import keys from encrypted backup |
wallet import <file> --on-conflict rename |
Conflict strategy: skip / overwrite / rename |
wallet bulk-import <file> |
Import from .env, .json, or .csv |
wallet bulk-import <file> --dry-run |
Preview what would be imported |
wallet bulk-import <file> --on-conflict overwrite |
Conflict strategy for bulk ops |
| Command | Description |
|---|---|
wallet tui |
Full-screen interactive TUI (Textual) |
wallet gui |
Desktop GUI (CustomTkinter, dark mode) |
Standard terminal interface. All commands scriptable. --raw and --env flags for shell integration.
wallet tui)Full-screen Textual app β keyboard-driven, works over SSH. Live search, key detail pane, health view, and a dedicated Import Panel (wallet/ui/tui_import.py) for in-TUI bulk import with format detection and conflict resolution.
wallet gui)Desktop app built with CustomTkinter. Dark mode only β secrets should not be visible in bright environments.
| Tab | Features |
|---|---|
| ποΈ Keys | Scrollable card list Β· live search Β· Copy / Info / Delete Β· Add Key dialog |
| π Health | Overall grade (AβF) Β· per-entry scores Β· issues + recommendations |
| βοΈ Settings | Change password (full re-encryption) Β· Export encrypted backup Β· Audit log viewer |
API key values are never displayed in the GUI β only a masked prefix (
sk-...).
Copy sends directly to clipboard; clipboard auto-clears afterclipboard_clear_seconds.
Manage multiple isolated wallets from a single installation:
wallet profile create work
wallet profile create personal
wallet profile use work # all subsequent commands target work.enc
wallet add --name "GitHub CI"
wallet profile use personal
wallet add --name "Personal OpenAI"
wallet profile list # shows all profiles + active marker
Each profile is a separate encrypted wallet-<name>.enc file under ~/.local/share/vaultkey/profiles/. Profiles are fully isolated β no shared keys, no shared audit log.
Share a key with a teammate without ever sending the raw value:
# Sender
wallet share "OpenAI Production" --expires 1h --uses 1
# β Prints: vaultkey-share://eyJhbGciOiJBMjU2R0NNIn0...
# Recipient (on their machine)
wallet share-receive vaultkey-share://eyJhbGciOiJBMjU2R0NNIn0...
# β Decrypts token, prompts for local name, stores encrypted
Share tokens are AES-256-GCM encrypted, self-describing (carry their own nonce + expiry), and single-use by default. The sender never stores the raw key in the token β only the ciphertext.
Get notified in Slack, Discord, or any HTTP endpoint when keys expire or are rotated:
wallet webhook add https://hooks.slack.com/services/... --events expiry,rotate
wallet webhook add https://discord.com/api/webhooks/... --events expiry
wallet webhook test <id> # verify with a test payload
Payload format (JSON POST):
{
"event": "EXPIRY_WARNING",
"key_name": "OpenAI Production",
"days_left": 3,
"ts": "2026-04-06T14:00:00Z"
}
Import keys from existing secrets files without manual entry:
wallet bulk-import .env --dry-run
wallet bulk-import .env --on-conflict rename
wallet bulk-import keys.json
wallet bulk-import secrets.csv --on-conflict skip
| Format | Detection | Expected shape |
|---|---|---|
.env |
Extension | KEY=value lines; service inferred from key name |
.json |
Extension | Array of {name, value, service?, tags?, description?} |
.csv |
Extension | Header row with name + value columns (others optional) |
Auto-runs silently on wallet unlock β warns only if keys are near expiry.
wallet expiry-check # keys expiring within 7 days (default)
wallet expiry-check --days 30
wallet expiry-check --all
wallet watch-expiry # persistent daemon, re-checks every hour
wallet watch-expiry --interval 1800
| Urgency | Threshold | Color |
|---|---|---|
expired |
past due | π΄ red |
critical |
β€ 3 days | π orange |
warning |
β€ 7 days | π‘ yellow |
info |
> 7 days | βͺ gray |
save() is calledwallet.encctypes.memset wipes derived key on lock, timeout, or process exit~/.local/share/vaultkey/
βββ wallet.enc # Default encrypted wallet
βββ audit.log # JSON-Lines audit trail (chmod 600)
βββ webhooks.json # Registered webhook endpoints
βββ profiles/
β βββ wallet-work.enc
β βββ wallet-personal.enc
β βββ ... # One file per named profile
βββ backups/
βββ wallet_20260101_120000.enc
βββ ... # Auto-pruned to last 20 backups
[4 bytes] magic: b'VKEY'
[2 bytes] version: uint16 big-endian (current: 1)
[4 bytes] kdf_params_len: uint32 big-endian
[N bytes] kdf_params_json: Argon2id params + salt_hex
[12 bytes] nonce: AES-GCM nonce
[M bytes] ciphertext: AES-GCM encrypted+authenticated payload
wallet/
βββ core/
β βββ crypto.py # AES-256-GCM encrypt/decrypt, HKDF subkey derivation
β βββ health.py # Per-entry + wallet-wide health scoring (AβF)
β βββ integrity.py # HMAC-SHA256 manifest, structural checks
β βββ kdf.py # Argon2id derive_key, hash/verify master password
β βββ rotate.py # rotate-all bulk rotation logic
β βββ session.py # SessionManager β unlock, lock, timeout, brute-force
β βββ storage.py # Binary wallet I/O, atomic writes, backups
β βββ wipe.py # Secure panic wipe
βββ models/
β βββ config.py # WalletConfig β env vars, paths, timeouts
β βββ wallet.py # WalletPayload, APIKeyEntry dataclasses
βββ ui/
β βββ cli.py # Typer CLI β all commands
β βββ cli_wave9.py # Wave 9 commands: profiles, share, webhook (v1.5)
β βββ gui.py # CustomTkinter desktop GUI
β βββ tui.py # Textual full-screen TUI
β βββ tui_import.py # TUI ImportPanel β in-app bulk import
βββ utils/
βββ audit.py # JSON-Lines audit log writer + reader
βββ bulk_import.py # .env / .json / .csv bulk import
βββ clipboard.py # Cross-platform copy + auto-clear timer
βββ expiry_checker.py # Expiry warnings + watch-expiry daemon
βββ prefix_detect.py # API key prefix β service auto-detection
βββ share_token.py # AES-GCM share token encode/decode (v1.5)
βββ shell_completion.py # bash/zsh/fish completion script generator
βββ validators.py # Key name, value, expiry date validators
βββ vault_profiles.py # Multi-profile wallet switcher (v1.5)
βββ webhook.py # Webhook registry + HTTP notifier (v1.5)
Configure via environment variables or .env file at project root:
VAULTKEY_WALLET_PATH=/secure/volume/wallet.enc
VAULTKEY_SESSION_TIMEOUT_MINUTES=10
VAULTKEY_CLIPBOARD_CLEAR_SECONDS=15
VAULTKEY_MAX_BACKUPS=30
VAULTKEY_ENABLE_INTEGRITY_CHECK=true
VaultKey has a comprehensive test suite with 407 tests across 17 files:
tests/
βββ conftest.py # Shared fixtures (session + function scope)
βββ test_crypto.py # 16 tests β AES-GCM, HKDF, SecureMemory
βββ test_kdf.py # 10 tests β Argon2id derive, hash, verify
βββ test_storage.py # 12 tests β binary format, atomic write, backups
βββ test_session.py # 14 tests β unlock, lock, timeout, brute-force
βββ test_audit.py # 12 tests β JSON-Lines, rotation, filtering
βββ test_validators.py # 20 tests β key name, API key value, expiry date
βββ test_health.py # 12 tests β health scoring, grading, recommendations
βββ test_prefix_detect.py # 8 tests β service detection (OpenAI, Anthropic, GitHubβ¦)
βββ test_integrity.py # 6 tests β HMAC manifest, structural checks
βββ test_hypothesis.py # 13 tests β property-based (Hypothesis)
βββ test_rotate.py # 16 tests β rotate-all, dry-run, tag scoping
βββ test_expiry_checker.py # 17 tests β watch-expiry daemon, urgency levels
βββ test_shell_completion.py # 12 tests β bash/zsh/fish completion scripts
βββ test_vault_profiles.py # 92 tests β profile create/switch/delete/isolate (v1.5)
βββ test_share_token.py # 81 tests β token encode, decode, expiry, revoke (v1.5)
βββ test_webhook.py # 78 tests β webhook register, notify, test payload (v1.5)
# Install dev dependencies
pip install -e ".[dev]"
# Run all tests with coverage
pytest tests/ -v --cov=wallet --cov-report=term-missing
# Run only fast tests (skip Hypothesis)
pytest tests/ -v --ignore=tests/test_hypothesis.py
# Run a specific file
pytest tests/test_crypto.py -v
# Clone and install
git clone https://github.com/Gzeu/vaultkey.git
cd vaultkey
pip install -e ".[dev]"
# Lint
ruff check wallet/ tests/
# Type check
mypy wallet/ --ignore-missing-imports
# Security scan
bandit -r wallet/ -c pyproject.toml --severity-level medium
# Build and serve docs locally
mkdocs serve
Full documentation available at gzeu.github.io/vaultkey (deploy with mkdocs gh-deploy).
init to wipewallet profile create/use/list/delete/current β multiple isolated wallets from one installwallet share / share-receive / share-list / share-revoke β AES-GCM one-time encrypted tokens with expiry + use-count limitswallet webhook add/list/remove/test β HTTP POST notifications for expiry, rotation, and audit eventswallet/ui/cli_wave9.py β dedicated CLI module for Wave 9 commandswallet/utils/vault_profiles.py β profile registry with per-profile configwallet/utils/share_token.py β token encode/decode, expiry enforcement, revocation listwallet/utils/webhook.py β webhook registry (JSON), HTTP notifier with retry + timeouttest_vault_profiles (92) Β· test_share_token (81) Β· test_webhook (78) β 407 totalprofiles/ subdirectory added to file layoutwebhooks.json added to file layoutwallet rotate-all [--tag <tag>] [--dry-run] β bulk rotate multiple keys in one passwallet watch-expiry [--interval N] β persistent background daemon, re-checks expiry on schedulewallet completion install [--shell bash|zsh|fish] β shell tab-completion for all commands + key nameswallet/ui/tui_import.py) β in-app bulk import with format detection + conflict resolutionwallet/core/rotate.py β isolated rotate-all logic with per-key prompt and single HMAC rewritewallet/utils/shell_completion.py β completion script generator (bash/zsh/fish)wallet/utils/expiry_checker.py extended with WatchExpiry daemon classtest_rotate (16) Β· test_expiry_checker (17) Β· test_shell_completion (12)wallet rename <name> --to <new> β metadata-only rename, zero re-encryptionwallet expiry-check [--days N] [--all] β color-coded expiry warnings with urgency levelswallet bulk-import <file> [--on-conflict] [--dry-run] β .env / .json / .csv importwallet unlock β silent unless keys are near expiryExpiryWarning frozen dataclass with sortable urgency propertyBulkImportResult counters: added / skipped / overwritten / renamed / errorstest_health, test_prefix_detect, test_integrity, 13 Hypothesis propertiesconftest.py with session-scoped Argon2id fixturespyproject.toml migrated to Hatchling / PEP 621wallet health, wallet audit, wallet verify, wallet wipewallet tag, wallet search, wallet rotate β key lifecycle commandsinit, unlock, lock, add, get, list, delete, infoMIT β see LICENSE.