Let your AI agent use your passwords and API keys — without ever letting it see them.
AI coding agents (Claude Code, Cursor, Codex) are incredibly useful — right up until they need an API key. Then your choices are ugly: paste the key into the chat (now it lives in the conversation forever), put it in a plain-text .env file the agent can read, or just do that part yourself.
key-amnesia is the fourth option. Your secrets live in an encrypted vault. The agent can trigger commands that use them — but the actual values are injected directly into the command's environment, out of the agent's sight. If a command tries to print a secret, key-amnesia censors it before the agent sees the output. And the master password can only ever be typed by you, a real human, at a real keyboard: when an agent needs your approval, a separate console window pops up on your screen — one the agent cannot read or type into.
The agent gets amnesia. That's the whole point. And every access attempt — allowed or denied — is written to an audit log you can review.
The part that matters more than any single integration: MCP connectors and vendor plugins only ever cover the popular APIs someone bothered to build for. The long tail — a random SaaS's REST API, an internal tool, plain SMTP — is never coming. With key-amnesia, your agent can write itself a script against any API that takes a key, run it through ka run, and you don't have to be afraid anything leaks. Not "we integrated with X" — a general-purpose unlock for the APIs no one will ever get around to wrapping.
# 1. Create the vault (type the master password twice to confirm)
ka init
# 2. Store a secret (you type it once, hidden, into a password prompt)
ka set OPENAI_API_KEY
# 3. The agent runs commands THROUGH key-amnesia instead of holding the key:
ka run --secret OPENAI_API_KEY --as OPENAI_API_KEY -- python my_script.py
# 4. That's it. The script gets the real key in its environment.
# The agent sees the script's output — with any leaked key censored:
# "Bearer ***REDACTED(OPENAI_API_KEY)***"ka init asks for the master password twice; if the entries do not match, nothing is created. There is no recovery if you forget that password — Argon2id + SecretBox leave none by design.
When the agent triggers step 3 and your approval is needed, you'll see a new console window appear with a clear message — "An agent-driven command is requesting: run with secret OPENAI_API_KEY" — and only your password, typed there, lets it proceed. Close the window to deny. Nothing the agent controls can type into that window.
pip install key-amnesiaOr from source: pip install git+https://github.com/fujitoid/key-amnesia, or from a local clone: pip install . — every path gets you both the full key-amnesia command and the short ka alias.
Windows and Linux supported; macOS still falls back to fail-closed (not yet implemented).
Paste this into your coding agent when you want it to install and teach itself key-amnesia (not a human pip walkthrough):
Install key-amnesia and set yourself up to use it correctly for secrets in
this project:
1. pip install key-amnesia
2. Verify `ka --version` works in a fresh terminal (if not found, fix PATH
as instructed).
3. Run `ka setup` (installs its skills + safety hook globally).
4. Tell me to restart this session so the skill loads, then tell me exactly
what to do in my OWN terminal to finish setup (master password etc.) —
you cannot do that step yourself.
ka setup copies the bundled key-amnesia-usage, key-amnesia-hygiene, and key-amnesia-migrate skills to ~/.claude/skills/ and ~/.cursor/skills/, and merges a PreToolUse (Claude Code) / preToolUse (Cursor) hook into each host's own config that blocks tool calls containing inline credential-shaped tokens. Restart or reload the host afterward to pick both up.
| Mode | What it feels like |
|---|---|
per-call (default) |
Every use of a secret asks for your password. Maximum safety, maximum prompts. |
cached |
You run ka unlock once in your terminal; a background "guard" keeps the vault open for 30 minutes (configurable). Agent commands run without prompts until it expires or you run ka lock. |
ka config set session-mode cached # switch (asks for your password)
ka unlock # start a session
ka lock # end it early, any timeka unlock runs the guard in that terminal — it's the same window for the life of the session. The startup line tells you when it expires and how to stop it early (Ctrl+C or ka lock from another terminal); a periodic nudge repeats that even if the guard sits idle the whole time. Before it expires, the guard asks right there whether to extend. No answer means it locks itself. The first command any client sends to a live guard also gets a one-time yes/no admission prompt in that same window (Session (pid ...) wants: ... Admit? [y/N]) — approve once and the rest of that session's commands go straight through.
| Command | What it does |
|---|---|
ka init |
Create an empty vault (type master password twice; refuse if already exists) |
ka passwd / ka change-password |
Change the master password (re-encrypts the vault with a fresh salt; refuses while a session is active) |
ka set NAME |
Store or update a secret (value typed hidden; password required; vault must already exist) |
ka remove NAME |
Delete a secret (password required) |
ka run --secret NAME --as ENV_VAR -- <command> |
Run a command with the secret injected; output censored. The agent-facing command. |
ka list |
Show secret names only — never values; safe for agents, no prompt |
ka unlock / ka lock |
Start / end a cached session |
ka reveal NAME |
Show a value to you (password required every time, even mid-session) |
ka copy NAME |
Copy a value to your clipboard instead of showing it (same rule) |
ka config show / ka config set KEY VALUE |
View / change settings (changes require your password) |
ka status |
Is a session active, and until when — plus, if not, what happened to the last one |
ka setup |
Install agent skills + the secret-guard hook for Claude Code / Cursor (--skills-only / --hook-only) |
Every command supports --help.
reveal and copy deserve a special note: even if an agent invokes them, the value appears only in the pop-up window on your screen (or your clipboard) — the agent's own process receives nothing but a status flag. And they always require a fresh password, session or no session — so an agent can never ride an open session into actually reading a value.
For the security-curious — the full detail lives in DESIGN.md:
- Encryption: the vault is a single file sealed with XSalsa20-Poly1305 (libsodium's SecretBox) under a key derived from your master password via Argon2id at its most expensive (
SENSITIVE) setting — deliberately slow to brute-force, and deliberately never dialed down. - The routing rule: any command needing your password checks whether it's running in a real terminal. Yes → asks right there. No (an agent invoked it) → spawns a fresh, isolated console window whose keyboard input can only come from you. No interactive session at all → fails closed, never falls back to something insecure.
- The guard never hands out secrets. In cached mode, the guard itself runs your command with the secret injected and returns only the censored output and exit code. Its protocol simply has no "give me the value" request — so even another process connecting to it directly can't ask for one. Guard verbs stay exactly
run/list/lock/status/renew. - Nothing sensitive on command lines. Windows records process command lines in its audit logs (event 4688); key-amnesia passes all sensitive hand-off data between its own processes via environment variables instead.
- Audit log:
~/.key-amnesia/audit.log, append-only JSON lines — timestamp, action, secret names (never values), route, allowed/denied/timeout. - Admission consent: the first command any client sends to a live guard triggers a one-time yes/no prompt in the guard's own terminal window; approve once and it's remembered (an opaque token, not a password) for the rest of that session. This sits on top of — never replaces — the hard guarantee above.
- Honest death reporting:
ka lock/ka statustell you what actually happened to the last session (locked,expired,interrupted, orcrashed: <reason>) instead of a bare "no active session."
Files live in ~/.key-amnesia/ (override: KEY_AMNESIA_HOME, KEY_AMNESIA_VAULT_PATH).
No tool in this class can promise absolute secrecy, and we'd rather tell you exactly where the edges are:
- A command you run can still leak its own secret. Censoring catches exact copies of the value in output — a command that base64-encodes or otherwise obfuscates the secret before printing slips through. This limit is shared by every tool of this kind (
op run,teller run). - Output is not live. Command output is collected fully, censored, then released — the agent sees it only after the command finishes.
- Secret names are stored in plain text (so
ka listcan work without a password). Values never are. Treat names as non-sensitive labels. - The pop-up window assumes the agent can't control your screen. If you've given an agent screen-reading and keyboard/mouse-injection powers, the window's isolation weakens — your typed password stays hidden, but a yes/no confirmation could theoretically be clicked by such an agent. The same caveat applies to the guard's admission prompt.
- Headless machines fail closed. No display → no way to approve → the operation is denied. By design.
- Same-user processes share your privileges. Any program running under your OS account can talk to a live guard session (this is equally true of
ssh-agent). That's why the guard is designed to never return raw values — the worst a rogue same-user process gets is the same bounded "run a command" capability the legitimate path has, and even that requires one admission prompt to be approved on your own screen first. - The master password never crosses any inter-process channel, in any form — it's consumed only inside the process that prompted you for it.
- Avoid
ka set NAME VALUEwith the value inline. It's supported for scripting, but an inline value briefly appears on the calling process's command line — visible to same-user process inspection and Windows command-line auditing. Prefer plainka set NAMEand type the value at the hidden prompt. (If an agent tries the inline form, the approval window shows you the incoming value before asking for your password — so you can still deny it.) - A live guard session can serve a stale secret. The guard decrypts the vault once, at
ka unlocktime, and holds that snapshot in memory for its whole session — it does not notice aka set/ka removemade against the vault file while it's already running.ka run/ka listagainst a live guard will keep returning the pre-update value until you cycle the session (ka lockthenka unlock). Not a leak — the guard never hands out anything it wasn't already holding — but a real staleness gap worth knowing about if you rotate a secret mid-session.
On a real terminal, status lines use a restrained brushed-chrome palette (cool chrome-blue for info/success, warm brass for warnings, red only for hard denials, slate for secondary/supporting detail lines like the auth prompt's secrets list). Set NO_COLOR or redirect output to a pipe/file and all ANSI escapes are omitted — agent-facing and scrubbed paths stay plain text. Unicode glyphs (✅ ❌ 🔒 🔓 ⏳ 💀) fall back to ASCII ([OK] / [DENIED] / [LOCKED] / [LISTENING] / [EXPIRED] / [CRASHED]) when color or unicode is unavailable. Scrubbed command output and raw revealed secret values are never styled.
pip install -e ".[dev]"
pytestDesign rationale, file formats, invariants: DESIGN.md.
Apache License 2.0 — see LICENSE.
