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.
- package/README.md +73 -54
- package/dist/cli.mjs +530 -484
- 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**.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
secret *names*,
|
|
17
|
-
environment variables, and
|
|
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
|
-
|
|
21
|
-
|
|
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`
|
|
51
|
-
Cursor, and other MCP clients pick it up
|
|
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
|
-
|
|
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
|
|
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
|
|
72
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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,
|
|
120
|
-
(directory `0700
|
|
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
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- **
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
- **
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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,
|
|
204
|
+
`Scrubber`) and `packages/cli` (commands, approval gating, and the MCP server).
|