A simple KMS solution based on macOS keychain. No plaintext secrets, explicit authentication everywhere.
- Secure secret storage using macOS keychain
- AES-256-GCM encryption
- Touch ID / local authentication for decrypt operations
- TOTP support for time-based one-time passwords
- Environment variable and file injection with automatic cleanup
- SSH agent with Touch ID gated signing (Ed25519, RSA, ECDSA P-256/P-384) and optional per-session/per-app auth caching
Download prebuilt binaries from GitHub Releases (macOS arm64, Linux amd64).
Or build from source:
cargo build --release
cp target/release/vt /usr/local/bin/-
Initialize the vault (creates keychain entries):
vt init
-
Start the KMS server (also starts the SSH agent on
~/.ssh/vt.sock):vt serve
-
Export the auth token (shown during
vt init):export VT_AUTH=<your_auth_token>
-
Create and read secrets:
# Create an encrypted secret (reads from stdin) vt create # Read/decrypt a vt protocol string vt read vt://mac/0xxxxx
| Command | Description |
|---|---|
version |
Show version information |
init |
(macOS) Initialize passcode and passphrase in keychain |
serve |
(macOS) Start the KMS HTTP server and SSH agent (supports --ssh-idle-timeout, --ssh-auth-cache-mode, --ssh-auth-cache-duration) |
create |
Read plaintext from stdin, output encrypted vt protocol |
read <vt> |
Decrypt a vt protocol string |
inject |
Decrypt vt protocols in env/files, optionally run a command |
secret export |
(macOS) Export the encrypted master secret |
secret import |
(macOS) Import an encrypted master secret |
secret rotate-passcode |
(macOS) Rotate the passcode for the master secret |
ssh agent |
(macOS) Start the SSH agent (supports --timeout, --ssh-auth-cache-mode, --ssh-auth-cache-duration) |
ssh add [-f <file>] [-c <comment>] |
(macOS) Add an SSH private key (from file or stdin) |
ssh list |
(macOS) List stored SSH keys (shows fingerprint, algorithm, comment, and public key) |
ssh comment <fingerprint> -c <comment> |
(macOS) Change the comment of a stored key |
ssh remove <fingerprint> |
(macOS) Remove an SSH key by fingerprint |
ssh remove-all |
(macOS) Remove all stored SSH keys |
ssh show <fingerprint> |
(macOS) Show the public key for a stored key |
The inject command supports several modes:
# Replace vt:// patterns in a file
vt inject -r config.yaml
# Read from input file, write to output file, then run command
vt inject -i template.env -o .env -- myapp --config .env
# Inject env vars and run command (output file auto-deleted after timeout)
vt inject -o secrets.env -t 5 -- ./run.shOptions:
-r, --replace-file <FILE>: Replace vt protocols in-place-i, --input-file <FILE>: Input file with vt protocols-o, --output-file <FILE>: Output file for decrypted content-t, --timeout <SECONDS>: Seconds before deleting output file (default: 2)
VT can act as an SSH agent, storing private keys encrypted in the macOS keychain and requiring Touch ID for every signing operation.
# Add a key from file (supports Ed25519, RSA, ECDSA P-256/P-384)
vt ssh add -f ~/.ssh/id_ed25519
# Optionally override the key's embedded comment
vt ssh add -f ~/.ssh/id_ed25519 -c "work laptop"
# Add a key interactively (paste key, Ctrl+D, then enter comment)
vt ssh add
# List stored keys
vt ssh list
# Show public key (for adding to GitHub, servers, etc.)
vt ssh show SHA256:...
# The SSH agent starts automatically with `vt serve`.
# To start it standalone:
eval $(vt ssh agent)
# Start with auth caching (skip repeated Touch ID within a time window):
# per-session: cache by terminal session (TTY)
eval $(vt ssh agent --ssh-auth-cache-mode per-session --ssh-auth-cache-duration 300)
# per-app: cache by application (e.g., Terminal.app, iTerm2)
eval $(vt ssh agent --ssh-auth-cache-mode per-app --ssh-auth-cache-duration 300)
# Set SSH_AUTH_SOCK to use the agent (add to your shell profile)
export SSH_AUTH_SOCK=~/.ssh/vt.sock
# Now ssh/git commands use vt for authentication
# Touch ID prompt shows the calling process name (e.g., "SSH sign: key (SHA256:...) by ssh")
ssh git@github.com
git push origin main
# Change a key's comment
vt ssh comment SHA256:... -c "new comment"
# Remove a key
vt ssh remove SHA256:...Keys are stored as a single encrypted JSON blob in the keychain (rusty.vault.ssh_keys) using the same mac_cipher as other secrets.
By default, Touch ID is required for every sign/decrypt request. You can enable auth caching to skip repeated prompts within a time window:
| Mode | --ssh-auth-cache-mode |
Scope |
|---|---|---|
| None (default) | none |
Touch ID every time |
| Per-session | per-session |
Shared within same terminal/TTY |
| Per-app | per-app |
Shared within same application (e.g., Terminal.app) |
--ssh-auth-cache-duration <SECONDS> controls how long a grant lasts (default: 300s). The cache is cleared when the agent is locked.
vt://{location}/{type}{data}
- location: Secret storage location (
macfor macOS keychain) - type:
0for raw secrets,1for TOTP - data: Base64 URL-safe encoded encrypted data
Example: vt://mac/0SGVsbG8gV29ybGQ
| Variable | Description | Default |
|---|---|---|
VT_ADDR |
Server address | 127.0.0.1:5757 |
VT_AUTH |
Authentication token (from vt init) |
- |
RUST_LOG |
Log level | info (release) / debug (dev) |
VT creates two keychain entries during initialization:
- passcode: Random bytes + auth_token, used to derive the passphrase encryption key
- passphrase: The actual encryption key (encrypted with key derived from passcode + USER + binary path)
- Run
vt servefrom the same user who ranvt init - Keep the
vtbinary at the same absolute path as duringvt init - The server requires Touch ID or local authentication for decrypt operations
┌─────────────┐ HTTP ┌─────────────┐ ┌─────────────┐
│ vt client │ ─────────────▶│ vt serve │────▶│ Keychain │
│ (create, │ encrypted │ (decrypt, │ │ (passcode, │
│ read, │◀───────────── │ encrypt) │◀────│ passphrase,│
│ inject) │ body └─────────────┘ │ ssh keys) │
└─────────────┘ │ └─────────────┘
▼ ▲
┌─────────────┐ │
│ Touch ID │ ┌──────┴──────┐
│ (decrypt, │ │ vt ssh agent│
│ sign) │ │ (Unix sock) │
└─────────────┘ └─────────────┘
MIT