How it happens
The incident of 2026-10-22 (STA-303): an agent ranpnpm linear get (which wraps infisical run) and a ps listing
in the same command. The process table showed the arguments and environment of the child that held the injected
/agents and /ci values, and TURBO_TOKEN and the Uniwind token were printed into the transcript. The owner rolled
both.
Since then guard-bash and the OpenCode guard (shared rules in .agents/hooks/_rules.mjs) deny:
Run a listing as its own command, after the wrapper has exited:
ps -o pid,command and pgrep -fl <name> stay
allowed on their own. The guard is an accident guard, not a boundary, so the rule below still holds.
A value belongs in a child’s environment or stdin, never on its argv, where any ps shows it. The bot wrappers
(figentra bot gh, figentra pr merge) pass the token as GH_TOKEN in the child’s environment, figentra linear
reads its token from the environment, and figentra secrets import writes through NAME=@file. The exception today is mcp.sh:
it expands $NAME references from .mcp.yaml into the MCP server’s arguments (--api-key, header values), so
those servers carry a token on argv for as long as they run (STA-303 audit; STA-1617 moves them to environment
variables).
When a token was printed
1
Stop and tell the owner
Name the token (never its value), where it was printed and the command that printed it. An agent
stops its task here: it never rotates, reads or re-sets a value itself.
2
Revoke or roll at the vendor
The owner creates the new value and revokes the old one where it is issued (the self-hosted
Turborepo cache for
TURBO_TOKEN, Uniwind for UNIWIND_AUTH_TOKEN, the GitHub App for a bot
key). Where the vendor holds two values, follow the overlap in
Rotation; a token printed into a transcript is already public, so
keep the overlap short.3
Set the new value in Infisical
The owner writes it to
/ci or /agents, or drops it in the secrets inbox for pnpm secrets:import (ADR-0257).4
Prove it works
One real use of the new value: a CI run that hits the remote cache, a Quality Gate install, a
pnpm linear mine. Confirm by metadata, never by printing the value.5
Purge what still holds it
The CI run logs and artifacts that printed it. A transcript cannot be purged everywhere, which
is why the old value is revoked first.
6
Close the gap
Record the roll (name, environment, date, who) as in Rotation,
open a Linear issue for the command that leaked it, and add the pattern to
guard-bash with a
deny test in .agents/hooks/__tests__/guard-bash.test.mjs and
.opencode/plugins/guard/__tests__/decide.test.ts.Checks that back this page
.agents/hooks/_rules.mjs: the process-listing and/proc/<pid>/environrules, run byguard-bash.mjsand by the OpenCode guard (.opencode/plugins/guard/decide.ts).agents/hooks/__tests__/guard-bash.test.mjsand.opencode/plugins/guard/__tests__/decide.test.ts: the deny and allow cases of STA-303