You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
docs: say the agent never sees the secret, not that nodkey gives the agent a secret #159
Some docs describe nodkey as a tool that "gives an AI coding agent a secret". That is not how the owner describes it, and it is not what a reader should take away.
The owner's words (2026-11-06): "The agent never sees the secret but the secret is on the agents machine due to technical limitations. Where possible it is not (e.g. ssh keys)".
So the plain description is: nodkey lets an AI agent use a secret-protected service only after a person approves, and the agent never sees the secret. For API secrets, the approved value goes only into the environment of the one command that asked (it is on the agent's machine, which THREAT-MODEL.md already lists as a limit). For SSH, the private key never leaves the broker; the client gets only a signature.
Places that say it the old way:
AGENTS.md line 3: "nodkey gives an AI coding agent a secret or an SSH signature only after a person approves..."
.claude/agents/security-reviewer.md line 8: same sentence.
Search the rest of the docs (README, DESIGN.md, docs/) for "gives ... a secret", "gets the secret" and similar, and keep the technical lines that are accurate (for example README: "The command gets the secret in GH_TOKEN" is about the command, which is right).
Done when:
AGENTS.md and .claude/agents/security-reviewer.md describe nodkey as above: access after approval, the agent never sees the secret.
Any other summary sentence in README, DESIGN.md or docs/ that says the agent gets or is given the secret is reworded the same way.
The technical limit stays stated honestly where it matters: an API secret is in the approved command's environment on the agent's machine; SSH private keys never leave the broker.
New evidence, 2026-11-07: at a short public talk about nodkey, people did not understand that the secret goes to the command the agent runs, not to the agent. So the problem is bigger than AGENTS.md: the first lines a new reader sees must make this clear.
Add to this issue:
README.md line 3 and DESIGN.md line 5: "lets an AI coding agent use a secret" reads as "the agent gets the secret". Say who gets what: the agent asks, a person approves on the phone, the value goes only into the environment of that one command, and it never enters the agent's chat or context. For SSH the agent gets only a signature.
A small picture near the top of the README: who asks, who approves, where the value goes (broker → client → the command's environment), and what comes back to the agent (only the command's output). Generated from committed source, not drawn by hand.
Keep the limit right next to that claim, in plain words: the agent writes the command, so a bad agent can write a command that prints or sends the value. The tap is where you check the command.
The README warning "the agent can read or keep that secret": keep the meaning, but say how — through the command it chose.
Done when (in addition to the list above):
The first paragraph of the README says the secret goes to the command, not to the agent.
The README has a generated "who sees the secret" picture near the top.
The limit (the agent chooses the command) is stated next to that claim, not only in THREAT-MODEL.md.
A full read of README.md (main at ecc1e60) for the same confusion. Short form: the first sentence misleads, then about 25 lines of warning come first, and the main idea ("the value goes only into that one command's environment") appears only at lines 42–45.
One correction to my comment above: do not write "the secret never enters the agent's chat" as a fact. It is true only when the command does not print it. Write "nodkey never puts it there", and give the limit next to it.
Proposed opening (in place of lines 3–4, before the warning)
An AI agent asks. You approve on your phone. The secret goes to one command, not to the agent.
The agent writes a command, for example nodkey run --secret GH_TOKEN=github-token -- gh release create v1.2.0.
You see the request on Telegram and tap Approve.
nodkey puts the value only into the environment of that command. It never puts it into the agent's chat. The agent sees the command's output.
SSH keys stay on the broker. The client gets only signatures.
The limit: the agent chooses the command. A bad agent can write one that prints or sends the secret, and an approved SSH login is a shell. So read the command before you tap.
Then the picture, then a shorter warning box that points to the limit.
The picture (generated from source, looked at before commit): boxes "Agent (chat)", "nodkey client + the command", "Broker (keys and store access)", "Your phone". Arrows: agent → command "writes it"; client → broker "asks"; broker → phone "asks you"; phone → broker "Approve"; broker → command "the value, into its environment only"; command → agent "output only". A mark on the agent box: "nodkey puts no value here". A note on the command box: "a bad command can print it". A second row for SSH: broker → client "signature only".
Other lines
L11–14 "the agent can read or keep that secret": keep the meaning, say how: the agent writes the command, so it can make the command print, save or send the value.
L14 "use that SSH login for other things": add "(it is a shell)".
L16 "the command and the reason are only what the agent claims": keep. It is the point of the tap.
L40 "The agent's machine gets only the nodkey client": "agent's machine" and "agent" blur here. Say what the agent never gets: no key and no store token.
L42: "When the agent needs a secret, it runs …" → "The agent writes this command, and nodkey runs it with the secret added:". Add "The agent sees only the command's output."
L45: add "the agent chose the command, so check it before you tap".
L46–47 SSH: add "the client gets only the signature".
L48–52 the phone message: say that the command and reason shown are what the agent claims (the term DESIGN.md uses), while the machine, the secret name and the SSH target are checked by the broker.
L238 "The command gets the secret in GH_TOKEN, and prints only its length": keep, add "so the agent's screen shows only that length".
Try it
The demo hides the value but does not say who sees what. Say that terminal 2 plays the agent's screen: it shows only got N characters, never the value. Then show the limit honestly: a second run with sh -c 'echo "$DEMO_TOKEN"' prints the fake value. "This is the limit: the command can print it."
Also
DESIGN.md L8–9 "The agent never holds them": true for keys and store access; add that a secret value goes only into one command.
DESIGN.md L34 goal 1 "The agent can use a secret …" → "A command the agent runs gets a secret only when …".
Done when (in addition):
The README opening, the "Try it" text and L238 say who sees what, as above.
The phone message text in the README says which parts the broker checked and which the agent claims.
AGENTS.md and .claude/agents/security-reviewer.md: access after approval; nodkey never puts the secret into the agent's chat; the value goes only into the approved command's environment; SSH keys stay on the broker. The limit is next to it.
Other summary lines: DESIGN.md opening and goal 1, SECURITY.md scope line.
Limits stay honest: README "The limits" bullet and warning, THREAT-MODEL.md section 5 item 1.
First comment:
README first lines: "The secret goes to one command, not to the agent."
Generated picture: docs/diagrams/who-sees-the-secret.svg from docs/diagrams/src/who-sees-the-secret.py (Schemdraw), right under the opening.
The limit (the agent chooses the command) is in the opening list, next to the claim.
Second comment:
README opening, "Try it" (terminal 2 plays the agent's screen, plus the echo "$DEMO_TOKEN" run that shows the limit) and the GH_TOKEN line say who sees what.
Phone message text: machine, secret or key and SSH target are verified by the broker; command and reason are what the agent claims.
DESIGN.md L8–9 and L34 reworded.
Extra, from the security review: a second limit is now stated. The command runs as the agent's user, so a bad agent can read the value from /proc/PID/environ while the command runs. THREAT-MODEL.md section 5 item 1 no longer says this needs root.
CI was green on #167 (test go.mod and stable, lint, vuln, end-to-end).
Activity
groupsky commentedon Nov 8, 2026
New evidence, 2026-11-07: at a short public talk about nodkey, people did not understand that the secret goes to the command the agent runs, not to the agent. So the problem is bigger than
AGENTS.md: the first lines a new reader sees must make this clear.Add to this issue:
README.mdline 3 andDESIGN.mdline 5: "lets an AI coding agent use a secret" reads as "the agent gets the secret". Say who gets what: the agent asks, a person approves on the phone, the value goes only into the environment of that one command, and it never enters the agent's chat or context. For SSH the agent gets only a signature.Done when (in addition to the list above):
groupsky commentedon Nov 8, 2026
A full read of README.md (main at
ecc1e60) for the same confusion. Short form: the first sentence misleads, then about 25 lines of warning come first, and the main idea ("the value goes only into that one command's environment") appears only at lines 42–45.One correction to my comment above: do not write "the secret never enters the agent's chat" as a fact. It is true only when the command does not print it. Write "nodkey never puts it there", and give the limit next to it.
Proposed opening (in place of lines 3–4, before the warning)
Then the picture, then a shorter warning box that points to the limit.
The picture (generated from source, looked at before commit): boxes "Agent (chat)", "nodkey client + the command", "Broker (keys and store access)", "Your phone". Arrows: agent → command "writes it"; client → broker "asks"; broker → phone "asks you"; phone → broker "Approve"; broker → command "the value, into its environment only"; command → agent "output only". A mark on the agent box: "nodkey puts no value here". A note on the command box: "a bad command can print it". A second row for SSH: broker → client "signature only".
Other lines
Try it
The demo hides the value but does not say who sees what. Say that terminal 2 plays the agent's screen: it shows only
got N characters, never the value. Then show the limit honestly: a second run withsh -c 'echo "$DEMO_TOKEN"'prints the fake value. "This is the limit: the command can print it."Also
DESIGN.mdL8–9 "The agent never holds them": true for keys and store access; add that a secret value goes only into one command.DESIGN.mdL34 goal 1 "The agent can use a secret …" → "A command the agent runs gets a secret only when …".Done when (in addition):
DESIGN.mdL8–9 and L34 are reworded the same way.docs: add the who-sees-the-secret picture
docs(readme): say who sees the secret, and the limit next to it
docs: point to the limit, and state it in the reviewer brief
docs: state the second limit: the agent can read the command's enviro…
docs(design): fix a broken reference in the opening
docs: exact wording for what the agent sees
groupsky commentedon Nov 10, 2026
Done in #167 (squash-merged on 2026-11-10).
Issue body:
AGENTS.mdand.claude/agents/security-reviewer.md: access after approval; nodkey never puts the secret into the agent's chat; the value goes only into the approved command's environment; SSH keys stay on the broker. The limit is next to it.DESIGN.mdopening and goal 1,SECURITY.mdscope line.THREAT-MODEL.mdsection 5 item 1.First comment:
docs/diagrams/who-sees-the-secret.svgfromdocs/diagrams/src/who-sees-the-secret.py(Schemdraw), right under the opening.Second comment:
echo "$DEMO_TOKEN"run that shows the limit) and theGH_TOKENline say who sees what.DESIGN.mdL8–9 and L34 reworded.Extra, from the security review: a second limit is now stated. The command runs as the agent's user, so a bad agent can read the value from
/proc/PID/environwhile the command runs.THREAT-MODEL.mdsection 5 item 1 no longer says this needs root.CI was green on #167 (test go.mod and stable, lint, vuln, end-to-end).