agent-cockpit
A local evidence-chain whiteboard plus an MCP server for AI research agents. Every experiment run, claim and code change is recorded and hash-checked, so each claim traces back to the run and code that produced it.
Why it's held
Touches SSH keys. Touches cloud credentials. Touches saved tokens or passwords. MCP tool cockpit_run executes arbitrary shell commands, and the docs recommend auto-approving it. cockpit_run is an unrestricted shell exposed to agents, though the docs present it as a restriction. Docs and init output tell users to auto-approve all cockpit MCP tools in Codex.
What it can touch
| files | write, including SSH keys (~/.ssh), cloud credentials (~/.aws, gcloud, kube), saved tokens (.npmrc, .netrc, git credentials), .gemini/settings.json, .claude/settings.json, AGENTS.md, CLAUDE.md |
|---|---|
| network | answer-me-with-html.com, api.elevenlabs.io, evil.example, github.com, nodejs.org, openfontlicense.org, raw.githubusercontent.com, tailscale.com, www.apache.org |
| shell | runs commands |
| secrets | COLOR_TOKENS |
What it actually does
The code matches the description. `cockpit init` writes MCP server entries into the research repo's agent configs (.mcp.json, .cursor/mcp.json, .vscode/mcp.json, .gemini/settings.json). It also writes a rules block into AGENTS.md, an @AGENTS.md import into CLAUDE.md, a Claude Code Stop hook in .claude/settings.json, and a git pre-commit hook. All of these block finishing a turn or committing while file changes are unreported. The MCP server exposes cockpit_* and canvas_* tools. These tools append events to .cockpit/events.jsonl and a SQLite database, hash files, parse Python/JS into a call graph, and run a regex-based code review. cockpit_run executes an arbitrary agent-supplied command string through `$SHELL -c` in the repo. The tool optionally spawns graphify or ocr if they are on PATH, and runs a vendored, minified answer-me-with-html CLI to render HTML briefs. `cockpit serve` runs a web board bound to 127.0.0.1 and accepts POSTs only from its own origin. I found no outbound network calls, credential reads or exfiltration in the readable source.
What the check found
highChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
src/init.mjs:46 if (!cOld.includes('@AGENTS.md')) { writeFileSync(claude, `${cOld}${cOld ? '\n' : ''}@AGENTS.md\n`); written.push('CLAUDE.md'); }
highTouches SSH keys
Reads private SSH key files or points the agent at them. Very few agent tools have a reason to.
vendor/answer-me-with-html/am.mjs:9272 var SECRET_DIR = /* @__PURE__ */ new Set([".git", ".ssh", ".aws", ".azure", ".gnupg", ".kube", ".docker", ".password-store"]);
vendor/answer-me-with-html/am.mjs:12114 - Files that hold keys by convention (.env, *.pem, id_rsa, .ssh/, .git/ \u2026) and files with anything that looks like a key or a token are refused.
highTouches cloud credentials
Reads cloud login files such as ~/.aws/credentials, gcloud or kube config, or points the agent at them.
vendor/answer-me-with-html/am.mjs:9272 var SECRET_DIR = /* @__PURE__ */ new Set([".git", ".ssh", ".aws", ".azure", ".gnupg", ".kube", ".docker", ".password-store"]);
highTouches saved tokens or passwords
Reads files where other tools keep login tokens, such as .npmrc, .netrc or git credentials.
vendor/answer-me-with-html/am.mjs:9268 "^(?:\\.netrc|\\.npmrc|\\.yarnrc(?:\\.yml)?|\\.pypirc|\\.pgpass|\\.my\\.cnf|\\.git-credentials|\\.htpasswd)$",
highMCP tool cockpit_run executes arbitrary shell commands, and the docs recommend auto-approving it
cockpit_run passes the agent-supplied command string straight to `$SHELL -c` with no allowlist. The tutorial presents leaving Bash out of `--allowedTools` as a restriction, but cockpit_run is a full shell, so that restriction is illusory. The docs and the init output also tell Codex users to set `default_tools_approval_mode="approve"` for this server. That would let any prompt-injected agent run any command without asking the user. The behavior fits the stated purpose (running experiments), but users should understand it is unrestricted shell access.
src/run.mjs:36 const res = spawnSync(process.env.SHELL || '/bin/sh', ['-c', cmd], { cwd:root, encoding:'utf8', stdio:capture ? 'pipe' : 'inherit', maxBuffer:32 << 20 });
src/mcp.mjs:90 const { run, log } = recordRun(cockpit, command, { agent:agent(), capture:true });
docs/tutorial.md:144 Leaving out Bash is deliberate: the agent can only run code through `cockpit_run`, so every run is recorded.
highcockpit_run is an unrestricted shell exposed to agents, though the docs present it as a restriction
The cockpit_run MCP tool takes any command string and runs it with `$SHELL -c` in the repo root. There is no allow-list and no check that the command is an experiment. The tutorial tells users to remove Bash from Claude Code and says the agent "can only run code through cockpit_run". That gives a false sense of sandboxing: an agent, or a prompt injection reaching the agent, can still run anything, including network or credential-reading commands. The commands are logged, but this is still full shell access.
src/run.mjs:36 const res = spawnSync(process.env.SHELL || '/bin/sh', ['-c', cmd], { cwd:root, encoding:'utf8', stdio:capture ? 'pipe' : 'inherit', maxBuffer:32 << 20 });
src/mcp.mjs:90 const { run, log } = recordRun(cockpit, command, { agent:agent(), capture:true });
docs/tutorial.md:144 Leaving out Bash is deliberate: the agent can only run code through `cockpit_run`, so every run is recorded.
highDocs and init output tell users to auto-approve all cockpit MCP tools in Codex
The tutorial and the text printed by `cockpit init` tell users to set default_tools_approval_mode="approve" for the cockpit server. One option is to add it to the global ~/.codex/config.toml. Combined with cockpit_run being an arbitrary shell, this lets Codex run any shell command without asking the user. The tool does not edit the home directory itself; it only prints the instruction.
docs/tutorial.md:158 -c 'mcp_servers.cockpit.default_tools_approval_mode="approve"' \
src/init.mjs:69 (codex exec 要能呼叫 cockpit 的工具,請在 ~/.codex/config.toml 的 [mcp_servers.cockpit] 加上 default_tools_approval_mode = "approve";
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
README.md:69 `init` only writes inside your repo. Open the repo in your agent and it reads `AGENTS.md` and the MCP config. The [tutorial](docs/tutorial.md) walks through eve
mediumHas a hidden comment that speaks to the agent
HTML comments don't show when Markdown is displayed, but agents read them.
docs/live-run/codex-run.log:52 <!-- agent-cockpit:start -->
docs/live-run/codex-run.log:66 <!-- agent-cockpit:end -->
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
docs/live-run/events.jsonl:1 {"id":1,"ts":"2026-10-26T16:32:25.508Z","agent":"git","type":"file_changed","path":".claude/settings.json","hash":"f8ae943a5e34","funcs":[],"add":0,"del":0,"del
docs/live-run/events.jsonl:2 {"id":2,"ts":"2026-10-26T16:32:25.540Z","agent":"git","type":"file_changed","path":".cursor/mcp.json","hash":"431d66f80c66","funcs":[],"add":0,"del":0,"deleted"
docs/live-run/events.jsonl:3 {"id":3,"ts":"2026-10-26T16:32:25.572Z","agent":"git","type":"file_changed","path":".gemini/settings.json","hash":"21d8abf33b2f","funcs":[],"add":0,"del":0,"del
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
docs/tutorial.md:159 --sandbox workspace-write "Follow the rules in AGENTS.md. Task: ..."
docs/tutorial.md:168 The seven rules in `AGENTS.md`, enforced by the write gate:
mediumChanges the agent's instructions or settings
Editing CLAUDE.md, AGENTS.md, memory files, hooks or MCP settings lets a tool keep steering the agent even after it's removed.
docs/tutorial.zh-TW.md:159 --sandbox workspace-write "照 AGENTS.md 的規範工作。任務:……"
mediumcockpit_run executes arbitrary shell commands supplied by the agent
The cockpit_run MCP tool passes the agent's command string straight to `$SHELL -c` in the repo root. This is the tool's core purpose (running and recording experiments), but it gives the agent full shell access. The tutorial says leaving out Bash means the agent can only run code through cockpit_run. That is true, but cockpit_run is itself a general shell, so withholding Bash does not restrict what can run. It only ensures the commands get logged.
src/run.mjs:36 const res = spawnSync(process.env.SHELL || '/bin/sh', ['-c', cmd], { cwd:root, encoding:'utf8', stdio:capture ? 'pipe' : 'inherit', maxBuffer:32 << 20 });
docs/tutorial.md:144 Leaving out Bash is deliberate: the agent can only run code through `cockpit_run`, so every run is recorded.
mediumDocs and init output recommend auto-approving all cockpit MCP tools in Codex
The tutorial and the `init` output tell users to set default_tools_approval_mode="approve" for the cockpit server. Combined with cockpit_run, this lets a headless Codex run arbitrary shell commands without per-call approval. The setting is opt-in and clearly shown, but users should understand what it grants.
docs/tutorial.md:158 -c 'mcp_servers.cockpit.default_tools_approval_mode="approve"' \
src/init.mjs:69 (codex exec 要能呼叫 cockpit 的工具,請在 ~/.codex/config.toml 的 [mcp_servers.cockpit] 加上 default_tools_approval_mode = "approve";
mediuminit writes agent configs, rule files and instructions for every agent
`cockpit init` adds the cockpit MCP server to four agents' config files, writes rules into AGENTS.md, and adds an @AGENTS.md import to CLAUDE.md. Every agent opened in the repo will therefore follow the cockpit rules. This is documented and is the tool's core purpose. The writes stay inside the repo and merge with existing configs rather than overwriting them.
src/init.mjs:25 merge('.mcp.json', v => { v.mcpServers = { ...v.mcpServers, cockpit:server }; }); // Claude Code
src/init.mjs:43 writeFileSync(agents, old.includes(START) ? old.replace(new RegExp(`${START}[\\s\\S]*?${END}`), () => block) : `${old}${old ? '\n\n' : ''}${block}\n`);
src/init.mjs:46 if (!cOld.includes('@AGENTS.md')) { writeFileSync(claude, `${cOld}${cOld ? '\n' : ''}@AGENTS.md\n`); written.push('CLAUDE.md'); }
mediumShips a large minified vendored CLI that runs with the full environment
vendor/answer-me-with-html/am.mjs (about 980 KB, minified) is described as an unmodified upstream build. It could not be reviewed here. Automated rules flagged references to SSH keys, cloud credentials and token files inside it. The board server spawns it to render briefs and passes the whole process.env to it. Update checks and auto-open are disabled. A reviewer should diff this file against the upstream release.
src/adapters.mjs:49 const AM = fileURLToPath(new URL('../vendor/answer-me-with-html/am.mjs', import.meta.url));
src/adapters.mjs:56 env:{ ...process.env, AM_HOME:path.join(root, '.cockpit', '.am'), AM_NO_UPDATE_CHECK:'1', AM_NO_OPEN:'1' } });
Keep an eye on it
This check is of one commit. Watch agent-cockpit and we'll email you if a later commit changes what it can touch.
About this check
Checked November 2, 2026 at commit e502e4ba2e80, 83 files. Rules for known red flags, then two Claude reviews, one of them told to assume the author is malicious. A later commit hasn't been checked. How checks work.