key-amnesia 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. key_amnesia-0.3.2/LICENSE +201 -0
  2. key_amnesia-0.3.2/MANIFEST.in +2 -0
  3. key_amnesia-0.3.2/PKG-INFO +177 -0
  4. key_amnesia-0.3.2/README.md +145 -0
  5. key_amnesia-0.3.2/pyproject.toml +58 -0
  6. key_amnesia-0.3.2/setup.cfg +4 -0
  7. key_amnesia-0.3.2/src/key_amnesia/__init__.py +8 -0
  8. key_amnesia-0.3.2/src/key_amnesia/__main__.py +4 -0
  9. key_amnesia-0.3.2/src/key_amnesia/audit.py +55 -0
  10. key_amnesia-0.3.2/src/key_amnesia/cli.py +753 -0
  11. key_amnesia-0.3.2/src/key_amnesia/clipboard.py +9 -0
  12. key_amnesia-0.3.2/src/key_amnesia/config.py +74 -0
  13. key_amnesia-0.3.2/src/key_amnesia/crypto.py +57 -0
  14. key_amnesia-0.3.2/src/key_amnesia/guard.py +695 -0
  15. key_amnesia-0.3.2/src/key_amnesia/hooks/__init__.py +1 -0
  16. key_amnesia-0.3.2/src/key_amnesia/hooks/secret_guard.py +260 -0
  17. key_amnesia-0.3.2/src/key_amnesia/ipc.py +66 -0
  18. key_amnesia-0.3.2/src/key_amnesia/paths.py +62 -0
  19. key_amnesia-0.3.2/src/key_amnesia/platform.py +265 -0
  20. key_amnesia-0.3.2/src/key_amnesia/prompt_route.py +652 -0
  21. key_amnesia-0.3.2/src/key_amnesia/run_exec.py +55 -0
  22. key_amnesia-0.3.2/src/key_amnesia/scrub.py +25 -0
  23. key_amnesia-0.3.2/src/key_amnesia/setup_cmd.py +179 -0
  24. key_amnesia-0.3.2/src/key_amnesia/skills/key-amnesia-hygiene/SKILL.md +55 -0
  25. key_amnesia-0.3.2/src/key_amnesia/skills/key-amnesia-migrate/SKILL.md +72 -0
  26. key_amnesia-0.3.2/src/key_amnesia/skills/key-amnesia-usage/SKILL.md +66 -0
  27. key_amnesia-0.3.2/src/key_amnesia/theme.py +290 -0
  28. key_amnesia-0.3.2/src/key_amnesia/vault.py +168 -0
  29. key_amnesia-0.3.2/src/key_amnesia.egg-info/PKG-INFO +177 -0
  30. key_amnesia-0.3.2/src/key_amnesia.egg-info/SOURCES.txt +54 -0
  31. key_amnesia-0.3.2/src/key_amnesia.egg-info/dependency_links.txt +1 -0
  32. key_amnesia-0.3.2/src/key_amnesia.egg-info/entry_points.txt +4 -0
  33. key_amnesia-0.3.2/src/key_amnesia.egg-info/requires.txt +5 -0
  34. key_amnesia-0.3.2/src/key_amnesia.egg-info/top_level.txt +1 -0
  35. key_amnesia-0.3.2/tests/test_argparse_help_cp1252.py +68 -0
  36. key_amnesia-0.3.2/tests/test_audit_cli.py +141 -0
  37. key_amnesia-0.3.2/tests/test_foreground_unlock.py +118 -0
  38. key_amnesia-0.3.2/tests/test_guard_admission.py +205 -0
  39. key_amnesia-0.3.2/tests/test_guard_death_reporting.py +201 -0
  40. key_amnesia-0.3.2/tests/test_guard_extend_prompt.py +98 -0
  41. key_amnesia-0.3.2/tests/test_guard_startup_banner.py +118 -0
  42. key_amnesia-0.3.2/tests/test_guard_verbs_regression.py +96 -0
  43. key_amnesia-0.3.2/tests/test_init.py +74 -0
  44. key_amnesia-0.3.2/tests/test_ipc_guard.py +179 -0
  45. key_amnesia-0.3.2/tests/test_package_skills_data.py +58 -0
  46. key_amnesia-0.3.2/tests/test_passwd_cmd.py +117 -0
  47. key_amnesia-0.3.2/tests/test_posix.py +363 -0
  48. key_amnesia-0.3.2/tests/test_prompt_route.py +159 -0
  49. key_amnesia-0.3.2/tests/test_scrub.py +63 -0
  50. key_amnesia-0.3.2/tests/test_secret_guard.py +227 -0
  51. key_amnesia-0.3.2/tests/test_session.py +251 -0
  52. key_amnesia-0.3.2/tests/test_set_never_prints_value.py +140 -0
  53. key_amnesia-0.3.2/tests/test_setup_cmd.py +242 -0
  54. key_amnesia-0.3.2/tests/test_theme.py +274 -0
  55. key_amnesia-0.3.2/tests/test_vault.py +57 -0
  56. key_amnesia-0.3.2/tests/test_vault_migration.py +140 -0
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 key-amnesia contributors
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,2 @@
1
+ recursive-include src/key_amnesia/skills *.md
2
+ recursive-include src/key_amnesia/hooks *.py
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: key-amnesia
3
+ Version: 0.3.2
4
+ Summary: Encrypted secret vault with human-prompt routing and output scrubbing
5
+ Author: key-amnesia contributors
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/fujitoid/key-amnesia
8
+ Project-URL: Repository, https://github.com/fujitoid/key-amnesia
9
+ Project-URL: Issues, https://github.com/fujitoid/key-amnesia/issues
10
+ Keywords: secrets,credentials,vault,ai-agents,claude,cursor,security
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Security :: Cryptography
23
+ Classifier: Topic :: Utilities
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: pynacl>=1.5
28
+ Requires-Dist: pyperclip>=1.8
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # key-amnesia
34
+
35
+ [![tests](https://github.com/fujitoid/key-amnesia/actions/workflows/tests.yml/badge.svg)](https://github.com/fujitoid/key-amnesia/actions/workflows/tests.yml)
36
+
37
+ **Let your AI agent *use* your passwords and API keys — without ever letting it *see* them.**
38
+
39
+ ![key-amnesia — the vault hands the agent a sealed envelope it cannot open](media/assets/approved/readme-hero.png)
40
+
41
+ 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.
42
+
43
+ **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.
44
+
45
+ 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.
46
+
47
+ **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.
48
+
49
+ ## How it works, in 30 seconds
50
+
51
+ ```bash
52
+ # 1. Create the vault (type the master password twice to confirm)
53
+ ka init
54
+
55
+ # 2. Store a secret (you type it once, hidden, into a password prompt)
56
+ ka set OPENAI_API_KEY
57
+
58
+ # 3. The agent runs commands THROUGH key-amnesia instead of holding the key:
59
+ ka run --secret OPENAI_API_KEY --as OPENAI_API_KEY -- python my_script.py
60
+
61
+ # 4. That's it. The script gets the real key in its environment.
62
+ # The agent sees the script's output — with any leaked key censored:
63
+ # "Bearer ***REDACTED(OPENAI_API_KEY)***"
64
+ ```
65
+
66
+ `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.
67
+
68
+ 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.
69
+
70
+ ## Install
71
+
72
+ ```bash
73
+ pip install git+https://github.com/fujitoid/key-amnesia
74
+ ```
75
+
76
+ Or from a local clone: `pip install .` — either way you get both the full `key-amnesia` command and the short `ka` alias.
77
+
78
+ > Windows and Linux supported; macOS still falls back to fail-closed (not yet implemented).
79
+
80
+ ## Agent bootstrap
81
+
82
+ Paste this into your coding agent when you want it to install and teach itself key-amnesia (not a human pip walkthrough):
83
+
84
+ ```
85
+ Install key-amnesia and set yourself up to use it correctly for secrets in
86
+ this project:
87
+ 1. pip install key-amnesia
88
+ 2. Verify `ka --version` works in a fresh terminal (if not found, fix PATH
89
+ as instructed).
90
+ 3. Run `ka setup` (installs its skills + safety hook globally).
91
+ 4. Tell me to restart this session so the skill loads, then tell me exactly
92
+ what to do in my OWN terminal to finish setup (master password etc.) —
93
+ you cannot do that step yourself.
94
+ ```
95
+
96
+ `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.
97
+
98
+ ## Two modes: ask every time, or unlock a session
99
+
100
+ | Mode | What it feels like |
101
+ |------|--------------------|
102
+ | **`per-call`** (default) | Every use of a secret asks for your password. Maximum safety, maximum prompts. |
103
+ | **`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`. |
104
+
105
+ ```bash
106
+ ka config set session-mode cached # switch (asks for your password)
107
+ ka unlock # start a session
108
+ ka lock # end it early, any time
109
+ ```
110
+
111
+ `ka 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.
112
+
113
+ ## Commands
114
+
115
+ | Command | What it does |
116
+ |---------|--------------|
117
+ | `ka init` | Create an empty vault (type master password twice; refuse if already exists) |
118
+ | `ka passwd` / `ka change-password` | Change the master password (re-encrypts the vault with a fresh salt; refuses while a session is active) |
119
+ | `ka set NAME` | Store or update a secret (value typed hidden; password required; vault must already exist) |
120
+ | `ka remove NAME` | Delete a secret (password required) |
121
+ | `ka run --secret NAME --as ENV_VAR -- <command>` | Run a command with the secret injected; output censored. The agent-facing command. |
122
+ | `ka list` | Show secret *names* only — never values; safe for agents, no prompt |
123
+ | `ka unlock` / `ka lock` | Start / end a cached session |
124
+ | `ka reveal NAME` | Show a value to *you* (password required every time, even mid-session) |
125
+ | `ka copy NAME` | Copy a value to your clipboard instead of showing it (same rule) |
126
+ | `ka config show` / `ka config set KEY VALUE` | View / change settings (changes require your password) |
127
+ | `ka status` | Is a session active, and until when — plus, if not, what happened to the last one |
128
+ | `ka setup` | Install agent skills + the secret-guard hook for Claude Code / Cursor (`--skills-only` / `--hook-only`) |
129
+
130
+ Every command supports `--help`.
131
+
132
+ `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.
133
+
134
+ ## Under the hood
135
+
136
+ For the security-curious — the full detail lives in [DESIGN.md](DESIGN.md):
137
+
138
+ - **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.
139
+ - **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.
140
+ - **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`.
141
+ - **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.
142
+ - **Audit log:** `~/.key-amnesia/audit.log`, append-only JSON lines — timestamp, action, secret names (never values), route, allowed/denied/timeout.
143
+ - **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.
144
+ - **Honest death reporting:** `ka lock` / `ka status` tell you what actually happened to the last session (`locked`, `expired`, `interrupted`, or `crashed: <reason>`) instead of a bare "no active session."
145
+
146
+ Files live in `~/.key-amnesia/` (override: `KEY_AMNESIA_HOME`, `KEY_AMNESIA_VAULT_PATH`).
147
+
148
+ ## Security limits — read this part honestly
149
+
150
+ No tool in this class can promise absolute secrecy, and we'd rather tell you exactly where the edges are:
151
+
152
+ 1. **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`).
153
+ 2. **Output is not live.** Command output is collected fully, censored, then released — the agent sees it only after the command finishes.
154
+ 3. **Secret *names* are stored in plain text** (so `ka list` can work without a password). Values never are. Treat names as non-sensitive labels.
155
+ 4. **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.
156
+ 5. **Headless machines fail closed.** No display → no way to approve → the operation is denied. By design.
157
+ 6. **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.
158
+ 7. **The master password never crosses any inter-process channel**, in any form — it's consumed only inside the process that prompted you for it.
159
+ 8. **Avoid `ka set NAME VALUE` with 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 plain `ka set NAME` and 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.)
160
+ 9. **A live guard session can serve a stale secret.** The guard decrypts the vault once, at `ka unlock` time, and holds that snapshot in memory for its whole session — it does not notice a `ka set`/`ka remove` made against the vault file while it's already running. `ka run`/`ka list` against a live guard will keep returning the pre-update value until you cycle the session (`ka lock` then `ka 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.
161
+
162
+ ## CLI appearance
163
+
164
+ 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.
165
+
166
+ ## Development
167
+
168
+ ```bash
169
+ pip install -e ".[dev]"
170
+ pytest
171
+ ```
172
+
173
+ Design rationale, file formats, invariants: [DESIGN.md](DESIGN.md).
174
+
175
+ ## License
176
+
177
+ Apache License 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,145 @@
1
+ # key-amnesia
2
+
3
+ [![tests](https://github.com/fujitoid/key-amnesia/actions/workflows/tests.yml/badge.svg)](https://github.com/fujitoid/key-amnesia/actions/workflows/tests.yml)
4
+
5
+ **Let your AI agent *use* your passwords and API keys — without ever letting it *see* them.**
6
+
7
+ ![key-amnesia — the vault hands the agent a sealed envelope it cannot open](media/assets/approved/readme-hero.png)
8
+
9
+ 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.
10
+
11
+ **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.
12
+
13
+ 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.
14
+
15
+ **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.
16
+
17
+ ## How it works, in 30 seconds
18
+
19
+ ```bash
20
+ # 1. Create the vault (type the master password twice to confirm)
21
+ ka init
22
+
23
+ # 2. Store a secret (you type it once, hidden, into a password prompt)
24
+ ka set OPENAI_API_KEY
25
+
26
+ # 3. The agent runs commands THROUGH key-amnesia instead of holding the key:
27
+ ka run --secret OPENAI_API_KEY --as OPENAI_API_KEY -- python my_script.py
28
+
29
+ # 4. That's it. The script gets the real key in its environment.
30
+ # The agent sees the script's output — with any leaked key censored:
31
+ # "Bearer ***REDACTED(OPENAI_API_KEY)***"
32
+ ```
33
+
34
+ `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.
35
+
36
+ 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.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install git+https://github.com/fujitoid/key-amnesia
42
+ ```
43
+
44
+ Or from a local clone: `pip install .` — either way you get both the full `key-amnesia` command and the short `ka` alias.
45
+
46
+ > Windows and Linux supported; macOS still falls back to fail-closed (not yet implemented).
47
+
48
+ ## Agent bootstrap
49
+
50
+ Paste this into your coding agent when you want it to install and teach itself key-amnesia (not a human pip walkthrough):
51
+
52
+ ```
53
+ Install key-amnesia and set yourself up to use it correctly for secrets in
54
+ this project:
55
+ 1. pip install key-amnesia
56
+ 2. Verify `ka --version` works in a fresh terminal (if not found, fix PATH
57
+ as instructed).
58
+ 3. Run `ka setup` (installs its skills + safety hook globally).
59
+ 4. Tell me to restart this session so the skill loads, then tell me exactly
60
+ what to do in my OWN terminal to finish setup (master password etc.) —
61
+ you cannot do that step yourself.
62
+ ```
63
+
64
+ `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.
65
+
66
+ ## Two modes: ask every time, or unlock a session
67
+
68
+ | Mode | What it feels like |
69
+ |------|--------------------|
70
+ | **`per-call`** (default) | Every use of a secret asks for your password. Maximum safety, maximum prompts. |
71
+ | **`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`. |
72
+
73
+ ```bash
74
+ ka config set session-mode cached # switch (asks for your password)
75
+ ka unlock # start a session
76
+ ka lock # end it early, any time
77
+ ```
78
+
79
+ `ka 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.
80
+
81
+ ## Commands
82
+
83
+ | Command | What it does |
84
+ |---------|--------------|
85
+ | `ka init` | Create an empty vault (type master password twice; refuse if already exists) |
86
+ | `ka passwd` / `ka change-password` | Change the master password (re-encrypts the vault with a fresh salt; refuses while a session is active) |
87
+ | `ka set NAME` | Store or update a secret (value typed hidden; password required; vault must already exist) |
88
+ | `ka remove NAME` | Delete a secret (password required) |
89
+ | `ka run --secret NAME --as ENV_VAR -- <command>` | Run a command with the secret injected; output censored. The agent-facing command. |
90
+ | `ka list` | Show secret *names* only — never values; safe for agents, no prompt |
91
+ | `ka unlock` / `ka lock` | Start / end a cached session |
92
+ | `ka reveal NAME` | Show a value to *you* (password required every time, even mid-session) |
93
+ | `ka copy NAME` | Copy a value to your clipboard instead of showing it (same rule) |
94
+ | `ka config show` / `ka config set KEY VALUE` | View / change settings (changes require your password) |
95
+ | `ka status` | Is a session active, and until when — plus, if not, what happened to the last one |
96
+ | `ka setup` | Install agent skills + the secret-guard hook for Claude Code / Cursor (`--skills-only` / `--hook-only`) |
97
+
98
+ Every command supports `--help`.
99
+
100
+ `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.
101
+
102
+ ## Under the hood
103
+
104
+ For the security-curious — the full detail lives in [DESIGN.md](DESIGN.md):
105
+
106
+ - **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.
107
+ - **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.
108
+ - **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`.
109
+ - **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.
110
+ - **Audit log:** `~/.key-amnesia/audit.log`, append-only JSON lines — timestamp, action, secret names (never values), route, allowed/denied/timeout.
111
+ - **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.
112
+ - **Honest death reporting:** `ka lock` / `ka status` tell you what actually happened to the last session (`locked`, `expired`, `interrupted`, or `crashed: <reason>`) instead of a bare "no active session."
113
+
114
+ Files live in `~/.key-amnesia/` (override: `KEY_AMNESIA_HOME`, `KEY_AMNESIA_VAULT_PATH`).
115
+
116
+ ## Security limits — read this part honestly
117
+
118
+ No tool in this class can promise absolute secrecy, and we'd rather tell you exactly where the edges are:
119
+
120
+ 1. **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`).
121
+ 2. **Output is not live.** Command output is collected fully, censored, then released — the agent sees it only after the command finishes.
122
+ 3. **Secret *names* are stored in plain text** (so `ka list` can work without a password). Values never are. Treat names as non-sensitive labels.
123
+ 4. **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.
124
+ 5. **Headless machines fail closed.** No display → no way to approve → the operation is denied. By design.
125
+ 6. **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.
126
+ 7. **The master password never crosses any inter-process channel**, in any form — it's consumed only inside the process that prompted you for it.
127
+ 8. **Avoid `ka set NAME VALUE` with 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 plain `ka set NAME` and 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.)
128
+ 9. **A live guard session can serve a stale secret.** The guard decrypts the vault once, at `ka unlock` time, and holds that snapshot in memory for its whole session — it does not notice a `ka set`/`ka remove` made against the vault file while it's already running. `ka run`/`ka list` against a live guard will keep returning the pre-update value until you cycle the session (`ka lock` then `ka 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.
129
+
130
+ ## CLI appearance
131
+
132
+ 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.
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ pip install -e ".[dev]"
138
+ pytest
139
+ ```
140
+
141
+ Design rationale, file formats, invariants: [DESIGN.md](DESIGN.md).
142
+
143
+ ## License
144
+
145
+ Apache License 2.0 — see [LICENSE](LICENSE).
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "key-amnesia"
7
+ version = "0.3.2"
8
+ description = "Encrypted secret vault with human-prompt routing and output scrubbing"
9
+ readme = "README.md"
10
+ license = { text = "Apache-2.0" }
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "key-amnesia contributors" }]
13
+ keywords = ["secrets", "credentials", "vault", "ai-agents", "claude", "cursor", "security"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Operating System :: Microsoft :: Windows",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Security",
26
+ "Topic :: Security :: Cryptography",
27
+ "Topic :: Utilities",
28
+ ]
29
+ dependencies = [
30
+ "pynacl>=1.5",
31
+ "pyperclip>=1.8",
32
+ ]
33
+
34
+ [project.urls]
35
+ Homepage = "https://github.com/fujitoid/key-amnesia"
36
+ Repository = "https://github.com/fujitoid/key-amnesia"
37
+ Issues = "https://github.com/fujitoid/key-amnesia/issues"
38
+
39
+ [project.optional-dependencies]
40
+ dev = ["pytest>=7.0"]
41
+
42
+ [project.scripts]
43
+ key-amnesia = "key_amnesia.cli:main"
44
+ ka = "key_amnesia.cli:main"
45
+ key-amnesia-hook = "key_amnesia.hooks.secret_guard:main"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+
50
+ [tool.setuptools]
51
+ include-package-data = true
52
+
53
+ [tool.setuptools.package-data]
54
+ key_amnesia = ["skills/*/SKILL.md", "hooks/*.py"]
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ """key-amnesia: encrypted vault with human-prompt routing and output scrubbing."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version as _version
4
+
5
+ try:
6
+ __version__ = _version("key-amnesia")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0"
@@ -0,0 +1,4 @@
1
+ from key_amnesia.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())