keymaxxer 0.1.2 → 0.2.0

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 (3) hide show
  1. package/README.md +73 -54
  2. package/dist/cli.mjs +530 -484
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -10,22 +10,19 @@ its context window, its transcript, or your LLM provider's logs.
10
10
 
11
11
  keymaxxer stores secrets in a single [Turso](https://turso.tech) database encrypted
12
12
  at rest with AES-256-GCM. The encryption key is **derived from your passphrase
13
- and never written to disk**. You unlock the vault once into a small background
14
- **daemon**; it holds the key in memory and is the only process that ever
15
- touches secret values. Your coding agent talks to keymaxxer over MCP: it can see
16
- secret *names*, ask keymaxxer to run a command with those secrets injected as
17
- environment variables, and get back output with every secret value scrubbed out.
13
+ and never written to disk**. Each MCP server **one per coding-agent session**
14
+ unlocks the vault the first time it's needed and holds the key in its own memory
15
+ for the life of that session. Your coding agent talks to keymaxxer over MCP: it
16
+ sees secret *names*, asks keymaxxer to run a command with those secrets injected
17
+ as environment variables, and gets back output with every secret value scrubbed.
18
18
 
19
19
  ```
20
- youunlock once (passphrase) ─▶ keymaxxer daemon
21
- holds key in RAM, opens vault
22
-
23
- coding agent ─ run(cmd, secret names) ─▶ keymaxxer MCP server
24
- (no key; calls daemon)
25
-
26
- daemon: inject secret → run command → scrub output
27
-
20
+ coding agent run(cmd, secret names) ─▶ keymaxxer serve (one per session)
21
+ first use ─▶ asks you to unlock (passphrase dialog); key held in memory
22
+ inject secret → run command → scrub output
28
23
  coding agent ◀─ exit code + scrubbed output (never the secret)
24
+
25
+ session ends ─▶ key + approvals wiped
29
26
  ```
30
27
 
31
28
  ## Quick start
@@ -41,51 +38,60 @@ keymaxxer init
41
38
  keymaxxer set GITHUB_TOKEN --tag github
42
39
  keymaxxer list
43
40
  keymaxxer run --secrets GITHUB_TOKEN -- 'gh api /user'
44
-
45
- # wipe the key from memory; unlock again later with your passphrase
46
- keymaxxer lock
47
- keymaxxer unlock
48
41
  ```
49
42
 
50
- `keymaxxer init` also drops a `keymaxxer` MCP server into `.mcp.json` so Claude Code,
51
- Cursor, and other MCP clients pick it up automatically.
43
+ `keymaxxer init` drops a `keymaxxer` MCP server into the project's `.mcp.json`, so
44
+ Claude Code, Cursor, and other MCP clients pick it up **in that project**.
45
+
46
+ To make keymaxxer available in **every** project in Claude Code, register it once
47
+ at user scope instead (the package is global, but the MCP registration is per
48
+ scope):
49
+
50
+ ```bash
51
+ claude mcp add --scope user keymaxxer -- npx keymaxxer serve
52
+ ```
52
53
 
53
54
  ## How an agent uses it
54
55
 
55
- Two MCP tools, neither of which ever returns a secret value:
56
+ Three MCP tools, none of which ever returns a secret value:
56
57
 
57
58
  - **`keymaxxer_list`** → the names + attributes (provider, account, environment,
58
59
  access, tags) of available secrets, so the agent can choose the right one.
59
60
  - **`keymaxxer_run`** → run a shell command with named secrets injected as env vars.
60
61
  The agent writes `$NAME`; keymaxxer supplies the value to the child process only.
61
62
  Read-write/prod secrets prompt you for approval first.
63
+ - **`keymaxxer_add`** → ask you to add a missing secret. The agent suggests the
64
+ name and attributes; you review/edit them and type the **value** into a dialog —
65
+ the value goes straight to the vault and is never shared with the agent. (So the
66
+ agent never tells you to paste a secret into the chat.)
62
67
 
63
68
  ```jsonc
64
69
  // the agent calls:
65
70
  { "command": "curl -H \"Authorization: Bearer $OPENAI_KEY\" https://api.openai.com/v1/models",
66
71
  "secrets": ["OPENAI_KEY"] }
67
- // keymaxxer runs it inside the agent daemon and returns stdout/stderr with every
72
+ // keymaxxer runs it inside the MCP server and returns stdout/stderr with every
68
73
  // occurrence of the key replaced by ***
69
74
  ```
70
75
 
71
- If the vault is locked, the tools return "vault is locked run `keymaxxer unlock`",
72
- so the model never blocks on a secret it can't get.
76
+ If the vault is locked, the first tool call prompts **you** to unlock it (a native
77
+ passphrase dialog) the agent just makes the call and waits.
73
78
 
74
79
  ## CLI
75
80
 
76
81
  | Command | Description |
77
82
  | --- | --- |
78
83
  | `keymaxxer init` | Create the encrypted vault (prompts for a passphrase) |
79
- | `keymaxxer unlock [--timeout m]` | Unlock into the background agent (default 15 min idle) |
80
- | `keymaxxer lock` | Lock the vault and stop the agent |
81
- | `keymaxxer status` | Show whether the vault is unlocked |
82
- | `keymaxxer set <NAME> [attrs]` | Store a secret (value from **stdin**); attrs: `--provider --account --env --access --tag --description` |
84
+ | `keymaxxer set <NAME> [attrs]` | Store a secret (paste at a hidden prompt); attrs: `--provider --account --env --access --tag --description` |
83
85
  | `keymaxxer import <file>` | Import `KEY=VALUE` lines from a `.env`-style file |
84
86
  | `keymaxxer list` | List secret names + metadata (never values) |
85
87
  | `keymaxxer rm <NAME>` | Delete a secret |
86
88
  | `keymaxxer run --secrets a,b -- <cmd>` | Run a command with secrets injected as env vars |
87
89
  | `keymaxxer audit [--limit N]` | Show the recent secret-access log |
88
- | `keymaxxer serve` | Start the MCP server on stdio (proxies to the agent) |
90
+ | `keymaxxer serve` | Start the MCP server on stdio (holds the key for the session) |
91
+
92
+ Each command opens the vault on demand — it prompts for your passphrase, or reads
93
+ `KEYMAXXER_PASSPHRASE` / `KEYMAXXER_MASTER_KEY`. There's no separate unlock step
94
+ and no background daemon.
89
95
 
90
96
  ## Secret attributes
91
97
 
@@ -105,32 +111,46 @@ With those, an agent matches the provider/account a task targets, prefers the
105
111
  right environment, and prefers the least-privileged credential that can do the
106
112
  job. Rotating a value with `keymaxxer set` preserves the attributes.
107
113
 
108
- ## Approval on use
114
+ ## Approval & unlocking — without leaving your editor
115
+
116
+ Both interactions happen through a native dialog, so an agent can keep working
117
+ and you never drop to a terminal:
118
+
119
+ - **Locked vault.** The first time a session's tool call needs the vault,
120
+ keymaxxer pops a dialog asking for your passphrase and unlocks it in place.
121
+ (Agents are told *not* to ask you to unlock manually — the call itself prompts.)
122
+ - **Sensitive use.** Using a **read-write** or **production** secret is gated.
123
+ The dialog shows the secret and the exact command and offers **Deny**,
124
+ **Allow once**, or **Allow for the session**. *Allow for the session* remembers
125
+ that one secret **for that agent's session only** — it's never shared with other
126
+ sessions — so you aren't re-prompted on every call. Read-only / non-prod secrets
127
+ run with no prompt.
109
128
 
110
- Using a **read-write** or **production** secret is gated: when an agent calls
111
- `keymaxxer_run` with such a secret, the daemon asks **you** to approve it
112
- (a native dialog on macOS), showing the secret and the exact command. Read-only
113
- and non-prod secrets run without a prompt. This is the human-in-the-loop control
114
- that catches a command which would otherwise misuse a credential — see the
115
- threat model. Set `KEYMAXXER_APPROVE=allow|deny` to force a decision for headless/CI.
129
+ This is the human-in-the-loop control that catches a command which would
130
+ otherwise misuse a credential (see the threat model). For headless/CI, set
131
+ `KEYMAXXER_APPROVE=deny|once|session` and `KEYMAXXER_PASSPHRASE` (or
132
+ `KEYMAXXER_MASTER_KEY`) to run non-interactively.
116
133
 
117
134
  ## Where things live, and how access is controlled
118
135
 
119
- - **Vault:** one global `~/.keymaxxer/vault.db` per user, inside `~/.keymaxxer`
120
- (directory `0700`, socket `0600` — only you can reach the agent).
136
+ - **Vault:** one global `~/.keymaxxer/vault.db` per user, in `~/.keymaxxer`
137
+ (directory `0700` — only you can read it).
121
138
  - **Encryption key:** **stored nowhere.** It is derived from your passphrase
122
139
  with scrypt (a non-secret salt lives in `~/.keymaxxer/vault.meta.json`). Copying
123
140
  `vault.db` off the machine yields nothing — there is no key at rest.
124
- - **Who can unseal:** whoever knows the passphrase. Not your coding agent.
125
- - **Who can use a secret:** whoever can reach the unlocked agent's unix socket
126
- (`~/.keymaxxer/agent.sock`, `0600` same user); sensitive uses additionally
127
- require your interactive approval.
128
- - **When:** only while unlocked. The agent auto-locks after idle (default 15
129
- min), on `keymaxxer lock`, and on reboot. It's an explicit seal/unseal, not a
130
- standing grant.
131
- - **CI / headless:** set `KEYMAXXER_MASTER_KEY` (64-hex) and skip unlock entirely
132
- keymaxxer opens the vault directly with the key your platform supplies, exactly
133
- like the [Turso credentials gateway](https://turso.tech/blog/why-we-chose-turso-to-secure-ai-credentials).
141
+ - **Who holds the key:** each process that needs it, only while it runs — an MCP
142
+ server for its session, a CLI command for one invocation. There is **no shared
143
+ daemon**; nothing keeps the key after the process exits.
144
+ - **Who can unlock:** whoever knows the passphrase. Not your coding agent.
145
+ - **Who can use a secret:** any process that can derive the key (your passphrase,
146
+ or `KEYMAXXER_MASTER_KEY`); sensitive uses additionally require your interactive
147
+ approval, scoped to the requesting session.
148
+ - **When:** for the life of the session the key is wiped when the MCP server (or
149
+ CLI command) exits, so closing the session locks it. Optionally set
150
+ `KEYMAXXER_IDLE_MINUTES` to also re-lock a server after inactivity.
151
+ - **CI / headless:** set `KEYMAXXER_MASTER_KEY` (64-hex) — keymaxxer opens the
152
+ vault directly with the key your platform supplies, exactly like the
153
+ [Turso credentials gateway](https://turso.tech/blog/why-we-chose-turso-to-secure-ai-credentials).
134
154
 
135
155
  ## Threat model — read this
136
156
 
@@ -152,12 +172,11 @@ keeping the value out of the model's context, and **approval-on-use** for
152
172
  sensitive secrets (you see the command and can deny it).
153
173
 
154
174
  **It does not defend against** a fully malicious process running as the *same OS
155
- user while the vault is unlocked* — it can talk to the agent socket or read the
156
- agent's memory. That is irreducible without OS-level isolation; no local key
157
- store changes it. keymaxxer keeps secrets out of the model's context, makes access
158
- ephemeral and explicit, and puts a human in the loop for sensitive use; for
159
- stronger isolation, run the agent as a separate user or in a sandbox — it
160
- composes cleanly.
175
+ user while a session holds the key* — it can read that process's memory. That is
176
+ irreducible without OS-level isolation; no local key store changes it. keymaxxer
177
+ keeps secrets out of the model's context, makes access ephemeral (per session)
178
+ and explicit, and puts a human in the loop for sensitive use; for stronger
179
+ isolation, run the agent as a separate user or in a sandbox — it composes cleanly.
161
180
 
162
181
  ## Built with Turso
163
182
 
@@ -182,4 +201,4 @@ bun run test
182
201
  ```
183
202
 
184
203
  Workspace layout: `packages/sdk` (KDF, vault metadata, `SecretStore`, `Runner`,
185
- `Scrubber`) and `packages/cli` (commands, the agent daemon, and the MCP server).
204
+ `Scrubber`) and `packages/cli` (commands, approval gating, and the MCP server).