7 releases
Uses new Rust 2024
| new 0.4.4 | Aug 10, 2026 |
|---|---|
| 0.4.3 | Aug 10, 2026 |
| 0.4.1 | May 11, 2026 |
| 0.3.0 | May 6, 2026 |
| 0.2.0 | May 6, 2026 |
#714 in Configuration
315KB
6.5K
SLoC
klef
klef stores your API keys in the OS Keychain and resolves them at runtime in your
.env(STRIPE_KEY=klef:stripe). No master password, no cloud, no plaintext on disk.
A local vault for your API keys and secrets — so you stop losing them in Dashlane, Notes, or scattered .env files.
The problem
You have 15 API keys (Stripe, Anthropic, OpenAI, Gemini, Telnyx, etc.). You stash them in Dashlane, in text files, in lost .env files. When you start a project, you spend 10 minutes hunting them down — and worst of all, you copy-paste the value into the project's .env, so it lingers in plaintext on your disk.
The solution
A local CLI that:
- Stores your keys in the OS Keychain — encryption handled by Apple/GNOME, not by us.
- Retrieves a key with a single command:
klef get stripe. - Injects values into your projects through references in the
.envinstead of plaintext values:
ThenSTRIPE_KEY=klef:stripe # reference — resolved at runtime ANTHROPIC_KEY=klef:anthropic # sameklef run -- npm startresolves it all and runs your command with the right env vars. - Stays 100% local — no server, no cloud, no telemetry.
Why not another tool?
| klef | 1Password CLI | doppler / infisical | direnv + .env | |
|---|---|---|---|---|
| Local-first | ✅ | ❌ (1P account) | ❌ (cloud) | ✅ |
| Native Keychain storage | ✅ | via op |
❌ | ❌ |
References in .env |
✅ klef: |
✅ op:// |
✅ {{var}} |
❌ literal |
| No master password | ✅ (OS keychain) | ❌ | ❌ | ✅ |
| Free | ✅ | $3/month | freemium | ✅ |
| Multi-machine sync | ❌ (v0.4) | ✅ | ✅ | ❌ |
klef targets the single-user, single-machine, local-first, free use case. The competitors are excellent — it's just a different niche. (Comparison verified on 2026-05-06.)
klef has no master password of its own: your OS keychain holds the secrets, and it's already unlocked by your login session. Biometric confirmation per access (Touch ID) is not implemented — it's tracked in #118. On macOS, if you're seeing repeated password prompts, that's the login keychain re-locking: run klef keychain configure once (see docs/macos-keychain.md).
Demo
# You have a .env lying around with plaintext secrets:
$ cat .env
STRIPE_API_KEY=sk_live_xyz
ANTHROPIC_API_KEY=sk-ant-zzz
PORT=3000
# One command to import everything into the Keychain and rewrite the .env as references:
$ klef import .env --rewrite
ENV VAR KLEF NAME VALUE
STRIPE_API_KEY stripe-api-key sk_l*** (16 chars)
ANTHROPIC_API_KEY anthropic-api-key sk-a*** (12 chars)
PORT port *** (4 chars)
Import 3 key(s)? [y/N] y
✓ STRIPE_API_KEY → klef:stripe-api-key
✓ ANTHROPIC_API_KEY → klef:anthropic-api-key
✓ PORT → klef:port
Imported 3 key(s).
Rewrote .env (3 reference(s) replaced).
$ cat .env
STRIPE_API_KEY=klef:stripe-api-key
ANTHROPIC_API_KEY=klef:anthropic-api-key
PORT=klef:port
# Now run your app like before — klef resolves the references on the fly:
$ klef run -- node app.js
Server on port 3000, Stripe wired ✓
Same thing as a playable recording: asciinema.org/a/5z9zCmNWd1igb3MH (cast source: docs/klef-demo.cast, re-uploadable if asciinema.org goes down; the GIF above is rendered from it with agg).
Install
Cargo (recommended)
cargo install klef
Homebrew (macOS / Linux)
Homebrew refuses to load anything from a third-party tap until you trust it, so
brew trust is a required step, not an optional one:
brew tap slewinus/tap
brew trust slewinus/tap
brew install klef # CLI — macOS (Intel + Apple Silicon) and Linux
brew install --cask klef # macOS menu bar GUI, with the CLI bundled inside the .app
The cask is Apple Silicon only and isn't codesigned yet, so macOS quarantines it
on first launch — clear it once with
xattr -dr com.apple.quarantine /Applications/klef.app. Signing and notarization
are tracked in #123.
Pre-built binaries
Available on the Releases page — macOS Intel + Apple Silicon, Linux x86_64 + ARM. Unpack and move into your $PATH.
Shell auto-completion
# zsh
klef completions zsh > ~/.zfunc/_klef
# bash
klef completions bash > /usr/local/etc/bash_completion.d/klef
# fish
klef completions fish > ~/.config/fish/completions/klef.fish
Static completion of subcommands and flags works today. Dynamic completion of key names (e.g.
klef get <TAB>) is tracked in #28 and not yet implemented.
Commands
| Command | Role |
|---|---|
klef add <name> |
Add a key (TTY prompt or stdin). Use --value-from-file <FILE> for multi-line secrets (PEM, JSON). |
klef get <name> |
Print the value (pipeable). |
klef show <name> |
Value + metadata. |
klef list [--format table|json] [-v|--verbose] [--filter PATTERN] |
List keys (never values). --verbose adds the date added, --filter does substring search. |
klef rm <name> (alias remove) |
Remove a key. |
klef edit <name> |
Change the value or metadata. --value-from-file for multi-line secrets. --note-edit opens $VISUAL/$EDITOR to edit the note. |
klef set-note <name> <text> |
Shortcut for edit --note. |
klef rename <old> <new> |
Rename a key. |
klef export <name>... [--format shell|dotenv] |
Emit export lines. |
klef import <file.env> [--prefix P] [--dry-run] [--rewrite] [--yes] |
Bulk-import from an existing .env. --rewrite replaces literal values with klef: references in the source file. |
klef run [--env-file FILE] -- <cmd> |
Resolve klef:<name> in .env and exec <cmd>. |
klef status [--format text|json] |
Diagnostics: version, backend, index path, key count, desync. Exit 1 on desync. |
klef completions <shell> |
Generate the auto-completion script. |
Run klef --help or klef <cmd> --help for the details of each option.
Stack
- Language: Rust (2024 edition)
- Storage: native Keychain via
keyring— Apple Security framework on macOS, Secret Service on Linux. - CLI:
clap(derive) - No server, no cloud, no account, no telemetry.
Dev
# Setup hooks (run once after clone)
./scripts/setup-dev.sh
# Build / test (cargo workspace: klef-core + klef-cli + klef-gui)
cargo build --workspace
cargo test --workspace --all-features
cargo run -p klef -- --help
GUI (klef-gui)
The Tauri crate has a Svelte frontend that must be bundled before any cargo build/run -p klef-gui (because tauri::generate_context! validates frontendDist at compile time):
cd crates/klef-gui
npm ci # once
npm run build # on every frontend change (or use `npm run dev` alongside cargo run)
cd ../..
cargo run -p klef-gui # menu bar mode: icon top-right, click to open (no Dock icon — LSUIElement=true)
macOS: stop the password prompts while developing
A cargo build binary is ad-hoc signed, so its code identity changes with its
bytes and the keychain's "Always Allow" never sticks — you get prompted on every
klef get. Sign your build with a stable certificate once:
scripts/sign-local.sh
See docs/macos-keychain.md for the full explanation
and for the other, unrelated cause (auto-lock timeout, fixed by klef keychain configure).
The git hooks (fmt, clippy, tests, line-cap < 300 lines/file) are versioned under .githooks/. CI on macOS + Ubuntu via GitHub Actions (.github/workflows/ci.yml).
Documentation
- Quickstart: examples/quickstart/ —
.env+ consumer script, end-to-end smoke test. - macOS users: if you see frequent password prompts, run
klef keychain configureonce — seedocs/macos-keychain.md. - Changelog: CHANGELOG.md
For AI agents
klef ships documentation designed for AI assistants:
llms.txt: navigation index (following the llmstxt.org convention)llms-full.txt: concatenated doc for single-prompt ingestiondocs/llm-usage.md: concrete patterns — decision table, exit codes, JSON outputsdocs/mcp.md: MCP server (klef mcp) — let Claude/Cursor use your keys without ever seeing the plaintext value.klef mcp installwires it into Claude Code and Codex in one command.
Claude Code, Cursor and ChatGPT agents can ingest these files and know how to drive klef without hallucinating.
Status
✅ v0.4.4 tagged (2026-08-10) — ships the MCP server that v0.4.0–v0.4.3 claimed to ship but didn't: release.yml never passed --features mcp, so every published tarball lacked the klef mcp subcommand while the binary bundled in klef.app had it. New klef mcp install wires klef into Claude Code and Codex in one command. klef restore --identity can finally read backups made with klef backup --recipient, which until now were written successfully and could never be restored (#59).
✅ v0.4.3 tagged (2026-08-10) — security + performance patch on v0.4.2. klef run and klef edit --note-edit no longer hand KLEF_PASSPHRASE to the child process (in CI, klef run -- npm start was giving the age vault's master key to npm and every postinstall script under it). The age backend now caches the decrypted vault while the file is unchanged: add drops from 7.9s to 4.0s, get from 2.9s to 1.3s (partial fix for #62).
✅ v0.4.2 tagged (2026-05-26) — bug-fix patch on v0.4.1. Three CLI/core fixes from the backlog audit: klef edit <name> no longer clobbers custom env_var (#71), piped commands (klef list | head, etc.) exit cleanly on SIGPIPE instead of panicking (#73), and Store::restore now holds the exclusive lock across both phases to prevent races with concurrent writes (#72). README translated to English (#126).
✅ v0.4.1 tagged (2026-05-11) — security patch on v0.4.0. Five audit findings fixed (shell-safe env_var validation, 0600 perms on metadata, O_EXCL tempfile for --note-edit, GUI dotenv import reworked to keep plaintext on the Rust side, MCP redaction documented as best-effort).
What's new in v0.4 (cumulative):
- MCP server (
klef mcp) —klef_list(metadata) +klef_run(spawn process withklef:refs injected as env vars) for Claude Desktop / Claude Code. Per-rule TOML policy, fail-closed NDJSON audit log, best-effort byte-exact redaction, shell denylist. Closes #24. Doc:docs/mcp.md. - macOS GUI (
klef.app) — menu-bar app (no Dock icon,LSUIElement=true) with a ⌘⇧K popover. Search, auto-clear copy, drag-and-drop a.envfor bulk import + rewrite asklef:refs. The CLI binary is bundled inside the.appfor a unified install. Closes #18. klef keychain configure(macOS) — disables Keychain auto-lock to stop the repeated prompts. Idempotent, prints the revert command.- One-time banner (macOS) pointing to
keychain configurewhen the Keychain is locked.
Security (v0.4.1):
-
env_varvalidation (^[A-Za-z_][A-Za-z0-9_]*$) on write and on render — closes theklef export | evalpath even for legacy indexes. -
klef_core::fsx::{write_private, write_inheriting}helper: 0600 onindex.json,audit.log,.age.tmp,backup.tmp; source-file perms preserved on.envrewrites. -
GUI dotenv import: plaintext never returned to the webview, session-id kept on the Rust side with a 5-min TTL, single-use on apply.
-
Supported platforms: macOS (native Keychain + menu-bar GUI) + Linux desktop (Secret Service) + headless Linux / CI / Docker via
--backend age:./vault.age(closes #12). -
Out of scope: Windows, multi-machine sync.
-
Roadmap: see issues by milestone. v0.5+ tracked in #125 (Rust-side clipboard, MCP
output_policy, audit log retention).
License
MIT — © 2026 Oscar R.
Dependencies
~15–34MB
~508K SLoC