vaultkey

VaultKey πŸ”

Ultra-secure local API key wallet β€” AES-256-GCM Β· Argon2id Β· HMAC Integrity Β· Zero cloud dependency

Python License Security Tests Version


What is VaultKey?

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


Security Architecture

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)

Quick Start

# 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

CLI Reference

Wallet Lifecycle

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

Vault Profiles

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

Key Management

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

Secure Sharing

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

Webhook Notifier

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

Security & Maintenance

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

Shell Completion

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

Backup & Portability

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

UI Launchers

Command Description
wallet tui Full-screen interactive TUI (Textual)
wallet gui Desktop GUI (CustomTkinter, dark mode)

Interfaces

CLI

Standard terminal interface. All commands scriptable. --raw and --env flags for shell integration.

TUI (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.

GUI (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 after clipboard_clear_seconds.


Vault Profiles

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.


Secure Sharing

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.


Webhook Notifier

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"
}

Bulk Import

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)

Expiry Checker

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

Security Guarantees


File Layout

~/.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

wallet.enc Binary Format

[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

Project Structure

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)

Configuration

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

Testing

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

Development

# 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

Documentation

Full documentation available at gzeu.github.io/vaultkey (deploy with mkdocs gh-deploy).


Changelog

v1.5.0 β€” Wave 9 (current)

v1.4.0 β€” Wave 8

v1.3.0 β€” Wave 7

v1.2.0 β€” Wave 5–6

v1.1.0 β€” Wave 3–4

v1.0.0 β€” Wave 1–2


License

MIT β€” see LICENSE.