Repository navigation
Description
Summary
On macOS, pgrep -fl / ps can read every managed agent's BUZZ_PRIVATE_KEY (nsec) because Desktop injects it into the buzz-acp child environment and Darwin KERN_PROCARGS2 concatenates environ after argv. Grandchildren (npm, node, vite preview, etc.) inherit the key. Any same-UID process on the machine can impersonate any other managed agent.
This is inherited environment, not argv. argv does not contain the nsec; pgrep -fl still prints it because it dumps the full KERN_PROCARGS2 buffer.
Why NIP-GS's current wording is not enough
NIP-GS "Key Exposure via Environment Variables" documents Linux /proc/<pid>/environ and calls the model acceptable for "agent processes running in a controlled environment (e.g., spawned by a desktop app with process-scoped env vars)."
A Desktop nest with many managed agents on one Mac is not a controlled environment in that sense: every seat runs as the same OS user. One agent (or any npm script, postinstall, or leftover vite preview) can pgrep -fl buzz-acp and obtain every other seat's signing key.
macOS is not mentioned. pgrep -fl is the accidental disclosure path people actually hit — it looks like the secret was passed on the command line even when it was not.
Reproduction (no secret in the report)
- Run a managed agent on macOS Desktop.
- From another same-user shell:
pgrep -fl buzz-acp(orpgrep -fl viteif an agent left a preview server running). - Observe
BUZZ_PRIVATE_KEY=nsec1…in the listed "command" text. - Confirm with
KERN_PROCARGS2that the nsec is in environ, notargv(argcstrings do not contain it; the env block after the NUL padding does).
Do not paste the nsec. Redact before sharing ps output.
Expected
- Agent identity should not be readable from the process table of sibling same-UID processes.
- At minimum, long-lived grandchildren should not inherit
BUZZ_PRIVATE_KEY. - Docs should name Darwin
KERN_PROCARGS2/pgrep -flalongside/proc/<pid>/environ.
Observed spawn site
desktop/src-tauri/src/managed_agents/runtime.rs — command.env("BUZZ_PRIVATE_KEY", &record.private_key_nsec); on the buzz-acp child. Reserved-env stripping correctly blocks user env from setting that key; Desktop still injects it itself, which is the leak.
Suggested direction (not a patch)
- Prefer a fd / unix-socket / one-shot helper for identity so the nsec is not in
environof the ACP process or its grandchildren. - If env injection stays: drop
BUZZ_PRIVATE_KEYfrom the environment before the inner agent runtime execs tools, or document a requiredenv -u BUZZ_PRIVATE_KEYaround long-lived children. - Treat sibling managed agents on the same UID as the threat model, not only "the process itself and its children."
Activity
Gforce10-design commentedon Aug 28, 2026
Adding a measured data point that refines the "not argv" framing in this report.
On the ACP path the nsec does reach
argv.buzz-acpputs it in the MCP server env, and@zed-industries/claude-code-acpthen serialises that whole map into a--mcp-config <JSON>command-line argument for the Claude Code CLI. So on Linux it is readable from
/proc/<pid>/cmdline, not only fromenviron.I captured this by replacing
CLAUDE_CODE_EXECUTABLEwith a wrapper that logs its ownargvbefore
execing the real binary (redacted here):Environment:
buzz-acp(imageghcr.io/block/buzz-sprig:main),@zed-industries/claude-code-acp0.16.2, Claude Code 2.1.247, Linux container, non-root uid 1000.Origin in this repo (at
b49675894b39f87215e4dfc8c1ad4a3c28c6097e):crates/buzz-acp/src/lib.rs:5088—build_mcp_servers()insertsEnvVar { name: "BUZZ_PRIVATE_KEY", value: <bech32 nsec> }, whichcrates/buzz-acp/src/acp.rsserialises into the
session/newmcpServerspayload. The adapter only moves it to a flag.Two consequences worth separating from the environ path:
logs,
ps-scraping supervisors, container tooling — captures it, on Linux as well as macOS.BUZZ_*from the agentenvironment (as suggested elsewhere) does not remove it from
argv.Mitigation we are using meanwhile is only an operational invariant — one seat per container, so a
process can read no key but its own. That is a deployment convention, not something the interface
enforces, and it does not help a host that runs several seats under one UID.
lh1022 commentedon Oct 29, 2026
Another data point: this also reproduces with
claude-agent-acp(the Claude Agent SDK adapter), not only@zed-industries/claude-code-acp.Setup:
buzz-acprunningclaude-agent-acpas a systemd service on Linux, one agent identity under its own dedicated user, key supplied throughEnvironmentFile.While a session was active, the command line of the SDK's bundled
claudebinary (@anthropic-ai/claude-agent-sdk-linux-x64/claude) containedso the key is readable from
/proc/<pid>/cmdlineby any local user unless/procis mounted withhidepid. Same origin as described above:build_mcp_servers()puts the key in the MCP server env and the adapter serialises it into argv.On a host with a dedicated agent user this mostly matters for two reasons: the argv copy is visible to other local accounts and to anything that collects process command lines, and the agent itself can read it (as can
buzz-dev-mcpshell children, per #2883). So the fix proposed above, keeping the key in the harness and out ofmcpServers.env, would cover both.