KV is a lightweight, feature-rich key-value store that lives right in your terminal. Think of it as a personal database for all those bits of information you need to store and retrieve quickly—configuration snippets, API keys, temporary notes, or any data you want at your fingertips.
Unlike traditional databases, KV is designed for simplicity and speed. No servers to manage, no complex setup—just store a value, retrieve it when you need it, and move on with your day.
Inspired by Charm's Skate, KV extends the personal key-value store concept with encryption, version control, TTL management, and enhanced security features.
| Feature | Skate | KV |
|---|---|---|
| Basic Key-Value Storage | ✅ | ✅ |
| Multiple Databases | ✅ | ✅ (via prefixes) |
| Binary Data | ✅ | ✅ |
| AES-256 Encryption | ❌ | ✅ |
| Value Visibility Control | ❌ | ✅ |
| Version History & Revert | ❌ | ✅ |
| Auto-Expiration (TTL) | ❌ | ✅ |
| Soft Deletes | ❌ | ✅ |
| Multi-Key Operations | ❌ | ✅ |
| JSON/YAML Output | ❌ | ✅ |
- Local & Fast: All data stored locally in SQLite—no network calls, no dependencies
- Secure: Built-in AES-256-GCM encryption for sensitive data
- Smart Expiration: Set TTLs on keys for automatic cleanup
- Version Control: Complete history tracking with the ability to revert changes
- Developer-Friendly: JSON/YAML output, shell completion, and intuitive commands
- KV vs Skate
- Installation
- Quick Start
- Core Capabilities
- Command Reference
- Configuration
- Data Storage
- Tips & Tricks
- Use Cases
- Contributing
Choose the installation method that works best for your platform:
brew install AmrSaber/tap/kvHomebrew:
brew install AmrSaber/tap/kvSnap (Ubuntu, Fedora, Arch, openSUSE, etc.):
sudo snap install kv-cliNote: The snap package is named
kv-cli(kvis reserved). After installation, the command will bekv-cli. To use justkv, create an alias:sudo snap alias kv-cli.kv-cli kv
Arch Linux (AUR):
yay -S kv-bin
# or
paru -S kv-binScoop:
scoop bucket add amrsaber https://github.com/AmrSaber/scoop-bucket
scoop install kvgo install github.com/AmrSaber/kv@latestNote: Package manager installations (Homebrew, Snap, Scoop, AUR) automatically include shell completion. If you installed via
go install, use the following manual completion setup.
KV provides intelligent auto-completion for commands, flags, and most importantly, relevant keys for each command context.
Bash:
echo 'eval "$(kv completion bash)"' >> ~/.bashrc
source ~/.bashrcZsh:
echo 'eval "$(kv completion zsh)"' >> ~/.zshrc
source ~/.zshrcFish:
echo 'kv completion fish | source' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fishOnce enabled, you can tab-complete key names when using commands like get, delete, lock, and more!
# Store some values
kv set api-key "sk-1234567890"
kv set database-url "postgres://localhost/mydb"
# Retrieve a value
kv get api-key
# Output: sk-1234567890
# List all keys - see the beautiful table output
kv list
# ┌──────────────┬────────────────────────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │
# ├──────────────┼────────────────────────────┼─────────────────────┤
# │ api-key │ sk-1234567890 │ 2025-10-20 21:29:02 │
# │ database-url │ postgres://localhost/mydb │ 2025-10-20 21:29:05 │
# └──────────────┴────────────────────────────┴─────────────────────┘
# Store with expiration (auto-deletes after 1 hour)
kv set temp-token "xyz789" --expires-after 1h
# Encrypt sensitive data with a password
kv set github-token "ghp_secret" --password "mypass"Lock individual values with password protection using military-grade AES-256-GCM encryption. Perfect for API keys, credentials, and sensitive configuration. Hide sensitive values from list output (without encryption) for privacy.
Set automatic expiration on keys. Great for temporary tokens, session data, or anything that shouldn't stick around forever.
Every change is versioned. Made a mistake? Revert to any previous value. Need to see what changed? Browse the full history.
View data as beautiful terminal tables, machine-readable JSON, or structured YAML—whatever fits your workflow.
Work with multiple keys at once — either by specifying them explicitly or using prefix matching. Operations are transactional: all keys succeed or none do.
KV organizes commands into intuitive groups. Here are the main workflows and capabilities with real output examples.
Note: These examples showcase common usage patterns. Each command has additional options and flags available—use
kv <command> --helpto see all available options.
# Store and retrieve values
kv set database-url "postgres://localhost/mydb"
kv get database-url
# Output: postgres://localhost/mydb
# List all keys - displays a beautiful table
kv list
# Output:
# ┌────────────────┬───────────────────────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │
# ├────────────────┼───────────────────────────┼─────────────────────┤
# │ api-key │ sk-1234567890abcdef │ 2025-10-20 21:29:02 │
# │ config.db.host │ localhost │ 2025-10-20 21:29:02 │
# │ config.db.port │ 5432 │ 2025-10-20 21:29:02 │
# │ database-url │ postgres://localhost/mydb │ 2025-10-20 21:28:55 │
# └────────────────┴───────────────────────────┴─────────────────────┘
# List keys with a specific prefix
kv list config
# Output:
# ┌────────────────┬───────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │
# ├────────────────┼───────────┼─────────────────────┤
# │ config.db.host │ localhost │ 2025-10-20 21:29:02 │
# │ config.db.port │ 5432 │ 2025-10-20 21:29:02 │
# └────────────────┴───────────┴─────────────────────┘
# Delete a key (soft delete - keeps history)
kv delete old-setting
# Permanently remove including history
kv delete cached-data --pruneSecurity Note: KV uses AES-256-GCM encryption with PBKDF2 key derivation (10,000 iterations). Passwords are never stored—they're only used to encrypt/decrypt your data. If you lose a password, the encrypted data cannot be recovered. Keep your passwords safe!
# Store an encrypted value directly
kv set github-token "ghp_secret123" --password "secure123"
# Retrieve encrypted value
kv get github-token --password "secure123"
# Output: ghp_secret123
# Lock an existing plain-text value
kv lock api-key --password "mypass"
# List shows locked values as [Locked]
kv list
# Output:
# ┌──────────────┬──────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │
# ├──────────────┼──────────┼─────────────────────┤
# │ api-key │ [Locked] │ 2025-10-20 21:30:02 │
# │ github-token │ [Locked] │ 2025-10-20 21:29:29 │
# └──────────────┴──────────┴─────────────────────┘
# Unlock a locked value back to plain text
kv unlock api-key --password "mypass"
# Lock multiple keys at once
kv lock config --prefix --password "mypass"Warning: Passing passwords directly on the command line (e.g., --password "mypass") will save them in your shell history, making them visible to anyone with access to your terminal history file. For better security, use environment variables or command substitution:
# Using an environment variable
kv set api-key "secret" --password "$KV_PASSWORD"
# Using command substitution (e.g., from a password manager)
kv get api-key --password "$(pass show kv/master)"
# Or read from a file
kv lock sensitive-data --password "$(cat ~/.kv-password)"Privacy Note: Hiding values is not encryption—it only controls visibility in output. Hidden values show as
[Hidden]in lists but remain accessible viaget. For true security, use encryption withlockinstead.
# Hide sensitive values from list output
kv set api-key "sk-1234567890"
kv hide api-key
# List shows hidden values as [Hidden]
kv list
# Output:
# ┌─────────┬──────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │
# ├─────────┼──────────┼─────────────────────┤
# │ api-key │ [Hidden] │ 2025-10-20 21:29:02 │
# └─────────┴──────────┴─────────────────────┘
# Hidden values are still accessible via get
kv get api-key
# Output: sk-1234567890
# Show a hidden value again
kv show api-key
# Hide multiple keys at once
kv hide api-key db-password secret-token
# Hide all keys with a prefix
kv hide secrets --prefix
# Note: Locked keys always show as [Locked] (takes precedence over hidden state)# Set a value with automatic expiration
kv set session-token "abc123xyz" --expires-after 1h
# List automatically shows "EXPIRES AT" column when any key has expiration
kv list
# Output:
# ┌───────────────┬───────────┬─────────────────────┬─────────────────────┐
# │ KEY │ VALUE │ TIMESTAMP │ EXPIRES AT │
# ├───────────────┼───────────┼─────────────────────┼─────────────────────┤
# │ database-url │ postgres… │ 2025-10-20 21:28:55 │ - │
# │ session-token │ abc123xyz │ 2025-10-20 21:29:25 │ 2025-10-20 22:29:25 │
# └───────────────┴───────────┴─────────────────────┴─────────────────────┘
# Check how long until expiration
kv ttl session-token
# Output: 59m56s (expires at 2025-10-20 22:29:25)
# Get expiration as timestamp only
kv ttl session-token --date
# Output: 2025-10-20 22:29:25
# Set expiration on an existing key
kv expire temp-data --after 30m
# Remove expiration from a key
kv expire session-token --never# Update a key to create history
kv set api-key "sk-1234567890abcdef"
kv set api-key "sk-updated-version"
# View complete history for a key
kv history list api-key
# Output:
# ┌───────┬─────────────────────┬─────────────────────┐
# │ INDEX │ VALUE │ TIMESTAMP │
# ├───────┼─────────────────────┼─────────────────────┤
# │ 1 │ sk-1234567890abcdef │ 2025-10-20 21:29:02 │
# │ - │ sk-updated-version │ 2025-10-20 21:29:37 │
# └───────┴─────────────────────┴─────────────────────┘
# Note: Index "-" indicates the current/latest value
# Revert to previous value (1 step back by default)
kv history revert api-key
# Output: sk-1234567890abcdef
# Revert multiple steps back
kv history revert api-endpoint --steps 3
# Interactively select from history
kv history select my-config
# Clear history for a key (keeps current value)
kv history prune old-key
# Clear history for all keys with a prefix
kv history prune temp --prefix# Get machine-readable JSON output
kv list --output json
# Output:
# [
# {
# "key": "api-key",
# "value": "sk-1234567890abcdef",
# "timestamp": "2025-10-20T20:29:02Z"
# },
# {
# "key": "github-token",
# "isLocked": true,
# "timestamp": "2025-10-20T20:29:29Z"
# }
# ]
# List only keys, hide values (adds "LOCKED" column for encrypted keys)
kv list --no-values
# Output:
# ┌──────────────┬─────────────────────┬────────┐
# │ KEY │ TIMESTAMP │ LOCKED │
# ├──────────────┼─────────────────────┼────────┤
# │ api-key │ 2025-10-20 21:29:02 │ - │
# │ github-token │ 2025-10-20 21:29:29 │ Yes │
# └──────────────┴─────────────────────┴────────┘
# YAML output is also available
kv list --output yaml# Operate on multiple keys at once
kv delete old-key temp-data cache-value
kv hide api-key db-password auth-token
kv lock secret1 secret2 secret3 --password "mypass"
kv expire session1 session2 session3 --after 1h
# Or use prefix matching for batch operations
kv delete cache --prefix
kv hide secrets --prefix
kv lock config --prefix --password "mypass"
kv unlock secrets --prefix --password "mypass"
# Unlock all keys at once
kv unlock --all --password "mypass"
# Note: Multi-key operations are transactional — if any key fails,
# none of the changes are applied (all-or-nothing behavior)Note: Backup creates a complete snapshot of your database including all keys, values, encryption, hidden state, TTL settings, and full history. Restore completely replaces your current database with the backup, creating a temporary backup of your current database first in case restoration fails.
# Create a backup to default location
kv db backup
# Output: Backup created successfully
# Backup to a custom path
kv db backup --path ~/backups/kv-$(date +%Y-%m-%d).db
# Backup to stdout (useful for piping)
kv db backup --stdout > backup.db
# Restore from default backup location
kv db restore
# Output: Database restored from backup successfully
# Restore from custom path
kv db restore --path /path/to/backup.db
# Restore from stdin
cat backup.db | kv db restore --stdin
# Or simply:
kv db restore --stdin < backup.db
# Practical examples:
# Transfer database between machines
# On source machine:
kv db backup --stdout | ssh user@remote 'kv db restore --stdin'
# Compress backup
kv db backup --stdout | gzip > kv-backup.db.gz
# Restore from compressed backup
gunzip -c kv-backup.db.gz | kv db restore --stdin
# Create dated backups
kv db backup --path ~/kv-backups/backup-$(date +%Y-%m-%d).dbWhat gets preserved in backup/restore:
- All keys and values (plain text, encrypted, and hidden)
- Password-encrypted keys (with their encryption intact)
- Hidden/visible state
- TTL and expiration settings
- Complete version history for all keys
- All configuration and metadata
Safety features:
- Backup overwrites existing file at the target path (use custom paths to keep multiple backups)
- Restore creates a temporary backup before replacing the database (auto-deleted on success)
- Restore validates the backup is a valid database before proceeding
- If restore fails, the original database is automatically recovered from the temporary backup
- See
kv infofor default backup location
# Clear all data (keeps configuration)
kv implode
# Warning: This permanently deletes all keys and history
# Generate shell completion (see Installation section for setup)
kv completion bash > /etc/bash_completion.d/kvFor detailed information about any command, including all available options and flags:
kv --help # View all commands
kv <command> --help # View specific command help
kv history <subcommand> --help # View history subcommand helpAll commands have comprehensive help text built into the CLI.
KV stores its configuration in a YAML file at your system's standard config location:
- Linux:
~/.config/kv/config.yaml - macOS:
~/Library/Application Support/kv/config.yaml - Windows:
%APPDATA%\kv\config.yaml
# How long to keep deleted keys in history (days)
prune-history-after-days: 30
# Maximum history entries to maintain per key
history-length: 15Both settings have sensible defaults.
Your key-value data is stored locally in a SQLite database at:
- Linux:
~/.local/share/kv/kv.db - macOS:
~/Library/Application Support/kv/kv.db - Windows:
%LOCALAPPDATA%\kv\kv.db
The database uses WAL (Write-Ahead Logging) mode for better performance and reliability. All data remains completely local—no network calls, no cloud sync, no telemetry.
Use dots or slashes to organize related keys:
kv set app.db.host "localhost"
kv set app.db.port "5432"
kv set app.db.name "myapp"
# List all database config
kv list app.dbStore JSON, multi-line text, or any structured data:
# Store JSON configuration
kv set app.config '{
"database": "postgres://localhost/db",
"port": 8080,
"debug": true
}'
# Retrieve and pipe to jq
kv get app.config | jq '.database'
# Output: "postgres://localhost/db"Use KV in your automation scripts:
#!/bin/bash
# Store build timestamp
kv set last-build "$(date)" --expires-after 24h
# Retrieve API key for deployment
API_KEY=$(kv get deploy-key --password "$MASTER_PASS")
curl -H "Authorization: Bearer $API_KEY" https://api.example.com/deployPerfect for sharing data between terminal sessions:
# Terminal 1
kv set clipboard "some long command or text"
# Terminal 2 (even different window/tab)
kv get clipboardTrack configuration changes over time:
# See all changes to production config
kv history list prod.api.endpoint
# Find out when something changed
kv history list db.password | grep "2025-10-15"Clean up temporary data efficiently:
# Set expiration on all temp keys
kv list temp --output json | jq -r '.[].key' | while read key; do
kv expire "$key" --after 1h
done
# Or just delete them all
kv delete temp --prefixDevelopment
- Store API keys, database URLs, and service endpoints locally
- Manage environment-specific configurations without
.envfiles - Quick access to frequently-used test data or tokens
DevOps & System Administration
- Temporarily store credentials during deployment or maintenance
- Share configuration snippets between terminal sessions
- Track configuration changes with built-in version control
Scripting & Automation
- Inter-script communication and data passing
- Store script state that persists between runs
- Cache expensive computation results with automatic expiration
Personal Productivity
- Keep track of license keys and access codes
- Store frequently-used snippets and commands
- Maintain a personal knowledge base of settings and configurations
Security & Secrets Management
- Encrypted storage for sensitive data with password protection
- Time-limited access tokens that auto-expire
- Local-only storage - no network exposure
Contributions are welcome! If you'd like to contribute:
- Fork the repository at github.com/AmrSaber/kv
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please report bugs and request features at: https://github.com/AmrSaber/kv/issues
When reporting bugs, please include:
- Your OS and Go version
- Steps to reproduce the issue
- Expected vs actual behavior