# Defense-in-Depth Score: Pi coding agent

**Repo:** https://github.com/earendil-works/pi (`packages/coding-agent`) · **Commit:** `4c6fb7cfe8c538a668726f6f8b3554098c39faee` · **Reviewed:** 2026-10-03
**What it is:** Minimal extensible terminal coding agent (pi monorepo)
**Category:** Coding
**Scored configuration:** Interactive `pi` CLI (package version 1.0.1), no flags, fresh install: default tools read/bash/edit/write, defaultProjectTrust "ask", no extensions or MCP servers configured.
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory no · untrusted input yes · third party extensions opt-in · sub agents no · external communication yes

## Score: 1.2 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C2 | Approval gates | L0 | L0 | L0 | L0 | 0.00 | C2-POWERBYPASS | **0.00** | High |
| C3 | Tool & action scoping | L0 | L1 | L1 | L0 | 0.12 | — | **0.12** | High |
| C4 | Code-execution isolation | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |
| C5 | Untrusted input blast radius | L0 | L0 | L0 | L0 | 0.00 | C5-WORSTCASE | **0.00** | High |
| C6 | Memory, context & configuration integrity | L2 | L2 | L2 | L1 | 0.45 | G2 | **0.25** | High |
| C7 | Third-party extensions | L1 | L0 | L1 | L0 | 0.12 | — | **0.12** | High |
| C8 | Secrets & sensitive-data protection | L1 | L1 | L1 | L0 | 0.20 | — | **0.20** | High |
| C9 | Audit & traceability | L2 | L2 | L1 | L2 | 0.45 | — | **0.45** | Medium |
| C10 | Limits & kill switch | L0 | L0 | L0 | L0 | 0.00 | — | **0.00** | High |


Pi runs every model-chosen shell command and file edit immediately, as you, on your machine, with your full environment and no sandbox, approval step, or run limits. A prompt injection in any file it reads can therefore exfiltrate credentials and take irreversible actions unattended. Its one real control is project trust, which keeps a repo's .pi settings, extensions and MCP servers from loading until you approve, but instruction files still load silently and the model can rewrite the trust store. The project's own docs say to run Pi inside a container or VM; do that.

## Critical gaps
- The bash tool, active by default, runs model-chosen commands with no approval gate. (ASI02, ASI09; C2) — [packages/coding-agent/src/core/settings-manager.ts:215](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L215); [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3)
- A hijacked session can both exfiltrate secrets and take irreversible actions with no human involved (C5-WORSTCASE). (ASI01, LLM01; C5) — [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118)
- The model can bypass project trust at runtime by writing ~/.pi/agent/trust.json or user-scope extensions, since file tools and bash are not path-contained (G2). (ASI06; C6) — [packages/coding-agent/src/core/tools/path-utils.ts:48-50](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/path-utils.ts#L48-L50); [packages/coding-agent/src/core/project-trust.ts:77-95](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/project-trust.ts#L77-L95)
- Pi acts with the user's full ambient authority: every shell command gets the complete environment. (ASI03; C1) — [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/tools/bash.ts:193](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L193)
- Commands run directly on the host with home-directory access and credentials in the environment; there is no sandbox. (ASI05; C4) — [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); [packages/coding-agent/docs/security.md:99](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L99)
- Extensions run in-process with all of Pi's credentials, and MCP stdio servers inherit the full environment. (ASI04; C7) — [packages/coding-agent/src/core/extensions/loader.ts:576-581](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/extensions/loader.ts#L576-L581); [packages/mcp/src/transports/stdio.ts:94](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/mcp/src/transports/stdio.ts#L94)

## Criterion details

### C1 Identity & least privilege — 0.00 (high)

Pi runs as the user who launched it and makes no attempt to narrow that authority. Every shell command the model runs receives a full copy of the user's environment, including provider API keys and any cloud or GitHub tokens set there, and the file tools accept any absolute or home-relative path. There is no authorization layer that checks a request before it runs, so a hijacked session can use everything the user can: SSH keys, cloud credentials, gh auth, and the stored Pi credentials themselves.

- **S L0:** Ambient OS-user authority: the bash tool spawns with the full process environment and no scoped credential. — [packages/coding-agent/src/core/tools/bash.ts:193](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L193); [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118) (verified)
  - *To reach the next level:* No dedicated or scoped identity; credentials are not narrowed per tool or capability.
- **C L0:** No authorization check exists on any tool path; bash, file tools, extensions and MCP stdio servers all run with ambient authority. — [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); [packages/coding-agent/src/extensions/mcp/runtime.ts:110-120](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/extensions/mcp/runtime.ts#L110-L120); [packages/mcp/src/transports/stdio.ts:94](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/mcp/src/transports/stdio.ts#L94); searched `rg -n -i 'requireApproval|requiresApproval|needsApproval|askPermission|confirmTool'` in `packages/coding-agent/src` → 0 hits (No built-in approval primitive on the tool path.) (verified)
  - *To reach the next level:* No main-path authorization check at all; even L1 needs one checked path.
- **D L0:** Default install runs with the user's full privilege and environment; narrowing requires external isolation per the project's own docs. — [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3); [packages/coding-agent/src/core/settings-manager.ts:215](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L215) (verified)
  - *To reach the next level:* No narrower default; least privilege requires manual hardening outside Pi.
- **B L0:** A hijacked session reaches the user's whole account: home directory, ~/.ssh, cloud credential files, env tokens and Pi's own auth.json. — [packages/coding-agent/src/core/auth-storage.ts:52](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/auth-storage.ts#L52); [packages/coding-agent/src/core/tools/path-utils.ts:48-50](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/path-utils.ts#L48-L50); [packages/coding-agent/src/utils/paths.ts:103-106](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/paths.ts#L103-L106); [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149) (verified)
  - *To reach the next level:* Nothing limits reach below the full user account.
- **Cap:** none

### C2 Approval gates — 0.00 (high)

Pi does not ask before running tools. Shell commands, file writes and edits execute as soon as the model emits them; the project's security doc says so directly. An extension hook can block tool calls, and an example permission-gate extension exists, but nothing ships enabled. There is no checkpoint or undo for file changes, so a bad command (for example rm -rf or a force-push with the user's credentials) is not recoverable by Pi.

- **S L0:** No approval step exists; tool calls execute immediately. — [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3); searched `rg -n -i 'requireApproval|requiresApproval|needsApproval|askPermission|confirmTool'` in `packages/coding-agent/src` → 0 hits (No built-in approval primitive on the tool path.) (verified)
  - *To reach the next level:* No per-call human approval of any kind.
- **C L0:** The most powerful tool, bash, is ungated along with every other tool. — [packages/coding-agent/src/core/settings-manager.ts:215](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L215); [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); searched `rg -n -i 'requireApproval|requiresApproval|needsApproval|askPermission|confirmTool'` in `packages/coding-agent/src` → 0 hits (No built-in approval primitive on the tool path.) (verified)
  - *To reach the next level:* Bash and all mutating tools would need to cross a gate.
- **D L0:** Approval is not even opt-in as a shipped feature; only an extension hook primitive and sample code exist. — [packages/coding-agent/src/core/extensions/types.ts:1413-1415](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/extensions/types.ts#L1413-L1415); [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3) (verified)
  - *To reach the next level:* No default-on approval.
- **B L0:** Irreversible actions (delete, force-push, external API calls through the shell) run with no checkpoint or rollback. — searched `rg -n -i 'checkpoint|rollback|git stash'` in `packages/coding-agent/src/core/tools packages/coding-agent/src/core/agent-session.ts` → 0 hits (No filesystem checkpoint or undo for tool mutations.); [packages/coding-agent/src/core/tools/write.ts:65-82](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/write.ts#L65-L82) (verified)
  - *To reach the next level:* No reversible-by-default path (checkpoints, trash) for file or code state.
- **Cap:** C2-POWERBYPASS — The bash tool, the single most powerful action path, is active by default and runs without any approval gate.
- **Notes:** Extensions can return {block:true} from a tool_call hook; examples/extensions/permission-gate.ts shows a regex-based confirm for a few bash patterns. Neither is loaded by default, so neither is credited.

### C3 Tool & action scoping — 0.12 (high)

The default tool set is read, bash, edit and write. Bash takes an arbitrary command string, and the file tools resolve any absolute or ~ path without confining it to the project folder. Validation is limited to typed schemas and a bounds check on the optional bash timeout. Users can restrict tools with --tools or --no-tools, but out of the box the model has general-purpose shell and whole-machine file write.

- **S L0:** Raw passthrough: bash runs an arbitrary shell string and file paths are not contained. — [packages/coding-agent/src/core/tools/bash.ts:40-43](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L40-L43); [packages/coding-agent/src/core/tools/path-utils.ts:48-50](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/path-utils.ts#L48-L50); [packages/coding-agent/src/utils/paths.ts:103-106](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/paths.ts#L103-L106) (verified)
  - *To reach the next level:* No allowlist validation or path containment; the general shell is the primary tool.
- **C L1:** Only a few inputs are validated (bash timeout bounds, typed schemas); paths and commands are not. — [packages/coding-agent/src/core/tools/bash.ts:27-37](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L27-L37); [packages/coding-agent/src/core/tools/write.ts:65-82](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/write.ts#L65-L82) (verified)
  - *To reach the next level:* Most built-in tools would need real input validation (path containment, command policy).
- **D L1:** Write and exec tools are on by default but can be individually disabled with --tools / --no-tools or the defaultTools setting. — [packages/coding-agent/src/core/settings-manager.ts:215](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L215); [packages/coding-agent/src/cli/args.ts:309-311](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/cli/args.ts#L309-L311) (verified)
  - *To reach the next level:* Tool groups exist but the default still includes write and exec; no read-only default.
- **B L0:** A misused tool can run any command or write any file the user can, across the machine. — [packages/coding-agent/src/core/tools/bash.ts:40-43](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L40-L43); [packages/coding-agent/src/core/tools/write.ts:65-82](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/write.ts#L65-L82) (verified)
  - *To reach the next level:* No workspace scoping of tool reach.
- **Cap:** none

### C4 Code-execution isolation — 0.00 (high)

Model-generated commands run directly on the host as the user, in a bash subprocess that inherits the full environment. Pi ships no sandbox; its security doc calls the lack of a built-in sandbox expected behaviour and recommends running the whole process in a container or VM. A sandbox exists only as example extension code that users must copy and install. Anything a command does (including running repo scripts, package installs and MCP stdio servers) reaches the user's whole machine and network.

- **S L0:** No isolation primitive: same-user host subprocess. — [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); searched `rg -n -i 'bwrap|bubblewrap|seatbelt|sandbox-exec|landlock|seccomp|firecracker|gvisor'` in `packages/coding-agent/src` → 0 hits (No OS sandbox primitive in shipped source (the only sandbox is an opt-in example under examples/).); [packages/coding-agent/docs/security.md:99](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L99) (verified)
  - *To reach the next level:* No OS-level separation of any kind for executed commands.
- **C L0:** No execution path is sandboxed (bash, user ! commands, package installs, MCP stdio servers, extensions). — [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); [packages/coding-agent/src/core/package-manager.ts:1865](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/package-manager.ts#L1865); [packages/coding-agent/src/extensions/mcp/runtime.ts:110-120](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/extensions/mcp/runtime.ts#L110-L120) (verified)
  - *To reach the next level:* The main exec tool would need to be sandboxed.
- **D L0:** Isolation is not provided by Pi in any configuration; users must supply container/VM isolation themselves. — [packages/coding-agent/docs/security.md:99](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L99); searched `rg -n -i 'bwrap|bubblewrap|seatbelt|sandbox-exec|landlock|seccomp|firecracker|gvisor'` in `packages/coding-agent/src` → 0 hits (No OS sandbox primitive in shipped source (the only sandbox is an opt-in example under examples/).) (verified)
  - *To reach the next level:* No sandbox on by default.
- **B L0:** Host-equivalent: home directory, credentials in environment, full network. — [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/tools/bash.ts:193](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L193) (verified)
  - *To reach the next level:* Commands would need to run without home-directory access or env credentials.
- **Cap:** none
- **Notes:** examples/extensions/sandbox (bubblewrap/sandbox-exec via @anthropic-ai/sandbox-runtime) and the Gondolin micro-VM example are sample code, not shipped features, so they are not scored as an opt-in alternative.

### C5 Untrusted input blast radius — 0.00 (high)

Pi reads repository files, command output, instruction files from every ancestor folder, and (when configured) MCP results straight into the model's context with no provenance tracking or taint handling. Once hijacked, the same session can read secrets from the environment or disk and send them out through the shell, and can also delete or push, all without a human in the loop. Nothing in the code limits what a hijacked session can do.

- **S L0:** Nothing structurally limits a hijacked agent; no taint tracking, no approval after untrusted reads. — searched `rg -n -i 'untrusted|prompt.injection|taint'` in `packages/coding-agent/src/core` → 1 hits (Single hit is a comment about forcing untrusted project settings during the trust bootstrap; nothing tracks untrusted content.); [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3) (verified)
  - *To reach the next level:* Dangerous capabilities would need to require approval once untrusted content is read.
- **C L0:** Untrusted sources are not distinguished; tool results and instruction files enter context with full standing. — [packages/coding-agent/src/core/system-prompt.ts:164](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/system-prompt.ts#L164); [packages/coding-agent/docs/security.md:57](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L57) (verified)
  - *To reach the next level:* At least one untrusted source would need to be handled.
- **D L0:** No control exists to be on by default. — searched `rg -n -i 'untrusted|prompt.injection|taint'` in `packages/coding-agent/src/core` → 1 hits (Single hit is a comment about forcing untrusted project settings during the trust bootstrap; nothing tracks untrusted content.) (verified)
  - *To reach the next level:* No default-on untrusted-input control.
- **B L0:** A successful injection can both exfiltrate secrets (full env, auth.json, network via bash) and take irreversible actions, unattended. — [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/tools/bash.ts:112-118](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L112-L118); [packages/coding-agent/docs/security.md:3](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L3) (verified)
  - *To reach the next level:* Exfiltration or irreversible actions would need human approval.
- **Cap:** C5-WORSTCASE — B is L0: leak plus irreversible action with no human involved in the default configuration.

### C6 Memory, context & configuration integrity — 0.25 (high)

Pi has a real project-trust gate: project settings, MCP servers, extensions, skills, prompts and system-prompt files under .pi only load after an explicit trust decision, and non-interactive modes default to not loading them. Instruction files (AGENTS.md, CLAUDE.md) from the working folder and every parent still load silently into the system prompt, and the project sessionDir setting is read before trust. Because the model can write anywhere the user can, a hijacked session can grant trust in ~/.pi/agent/trust.json, drop an extension into ~/.pi/agent/extensions, or plant files in an already-trusted project, which then load in every later session.

- **S L2:** Security-relevant project config needs an explicit trust decision, but instruction files load silently as high-priority context. — [packages/coding-agent/src/core/trust-manager.ts:30-39](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/trust-manager.ts#L30-L39); [packages/coding-agent/src/core/project-trust.ts:77-95](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/project-trust.ts#L77-L95); [packages/coding-agent/src/core/resource-loader.ts:183-185](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/resource-loader.ts#L183-L185); [packages/coding-agent/src/core/system-prompt.ts:164](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/system-prompt.ts#L164) (verified)
  - *To reach the next level:* Instruction files would need a trust decision and model-writable config/extension locations would need protection.
- **C L2:** Settings, MCP, extensions, skills and prompts are gated; AGENTS.md/CLAUDE.md from all ancestors and the pre-trust sessionDir are not. — [packages/coding-agent/src/core/resource-loader.ts:250-264](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/resource-loader.ts#L250-L264); [packages/coding-agent/docs/security.md:57](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L57); [packages/coding-agent/src/main.ts:688-693](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/main.ts#L688-L693); [packages/coding-agent/docs/security.md:31](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L31) (verified)
  - *To reach the next level:* All auto-loaded files and settings would need to be covered.
- **D L2:** Single-user CLI with per-cwd session namespaces in the user's agent dir; trust default comes from global settings only. — [packages/coding-agent/src/core/session-manager.ts:589-593](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/session-manager.ts#L589-L593); [packages/coding-agent/src/core/settings-manager.ts:1100-1101](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L1100-L1101) (verified)
  - *To reach the next level:* The model would need to be unable to write the trust store, other namespaces, or user-scope config.
- **B L1:** Poisoned instruction files or a planted extension persist across the user's sessions and can drive tool use. — [packages/coding-agent/src/core/resource-loader.ts:183-185](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/resource-loader.ts#L183-L185); [packages/coding-agent/src/core/extensions/loader.ts:576-581](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/extensions/loader.ts#L576-L581) (verified)
  - *To reach the next level:* Persistence would need to be limited to text output or gated actions.
- **Cap:** G2 — The model, through write/bash with no path containment, can edit ~/.pi/agent/trust.json or add user-scope extensions, bypassing the project-trust gate at runtime.
- **Notes:** A repository's .pi/settings.json sessionDir is read by an untrusted-by-default SettingsManager before trust resolution (documented). It can redirect where transcripts are written or which sessions --continue resumes.

### C7 Third-party extensions — 0.12 (high)

Nothing third-party is enabled out of the box, and project-supplied packages and MCP servers only load after the project-trust prompt. Once added, extensions run in the Pi process with all its credentials, packages install from npm or git without integrity checks (pinned only if the user gives an exact version), npm installs run lifecycle scripts, and missing configured packages are installed automatically after the generic trust prompt. MCP stdio servers inherit the full environment by default.

- **S L1:** User-chosen npm/git sources; pinned only when the user supplies an exact version; no hash or signature check. — [packages/coding-agent/src/core/package-manager.ts:1490](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/package-manager.ts#L1490); searched `rg -n -i 'integrity|sha256|sha512|signature|verifySig'` in `packages/coding-agent/src/core/package-manager.ts packages/coding-agent/src/core/extensions/loader.ts packages/coding-agent/src/extensions/mcp` → 4 hits (All four hits hash names/paths for cache keys (install dir, MCP tool names, OAuth lock keys); none verifies package or server integrity.) (verified)
  - *To reach the next level:* Versions would need to be pinned by default.
- **C L0:** No extension type (extensions, packages, MCP servers) is integrity-verified. — searched `rg -n -i 'integrity|sha256|sha512|signature|verifySig'` in `packages/coding-agent/src/core/package-manager.ts packages/coding-agent/src/core/extensions/loader.ts packages/coding-agent/src/extensions/mcp` → 4 hits (All four hits hash names/paths for cache keys (install dir, MCP tool names, OAuth lock keys); none verifies package or server integrity.); [packages/coding-agent/src/extensions/mcp/runtime.ts:110-120](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/extensions/mcp/runtime.ts#L110-L120) (verified)
  - *To reach the next level:* At least one extension type would need verification.
- **D L1:** Nothing enabled by default, but a trusted project's configured packages are installed automatically behind the generic project-trust prompt, which does not list them. — [packages/coding-agent/src/core/package-manager.ts:1302-1312](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/package-manager.ts#L1302-L1312); [packages/coding-agent/src/core/project-trust.ts:24-26](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/project-trust.ts#L24-L26) (verified)
  - *To reach the next level:* Explicit per-package install showing what will run, for every scope.
- **B L0:** Extensions are imported in-process via jiti with all of Pi's credentials; MCP stdio servers get the full environment. — [packages/coding-agent/src/core/extensions/loader.ts:576-581](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/extensions/loader.ts#L576-L581); [packages/mcp/src/transports/stdio.ts:94](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/mcp/src/transports/stdio.ts#L94) (verified)
  - *To reach the next level:* Extensions would need at least a separate process.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.20 (high)

Provider credentials are stored in plaintext ~/.pi/agent/auth.json with owner-only permissions, and the whole user environment, including API keys, is passed to every shell command and MCP stdio server. Redaction exists only for the user-initiated bug report. Install telemetry is on by default but sends only the version; analytics are opt-in. Session transcripts can contain any secret the model reads and are written unencrypted.

- **S L1:** Secrets from env vars and a 0600 plaintext file; masking only in the bug-report path. — [packages/coding-agent/src/core/auth-storage.ts:25](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/auth-storage.ts#L25); [packages/coding-agent/src/core/bug-report.ts:19-20](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/bug-report.ts#L19-L20); searched `rg -n redact` in `packages/coding-agent/src/core` → 15 hits (14 hits are in bug-report.ts (redaction of the user-initiated bug report); 1 is a doc comment in extensions/types.ts. No redaction on logs, transcripts, subprocess env, or model-bound messages.) (verified)
  - *To reach the next level:* Type-level masking or log/transcript redaction on main paths.
- **C L1:** One path (bug reports) is redacted; transcripts, subprocess env and model-bound messages are not. — searched `rg -n redact` in `packages/coding-agent/src/core` → 15 hits (14 hits are in bug-report.ts (redaction of the user-initiated bug report); 1 is a doc comment in extensions/types.ts. No redaction on logs, transcripts, subprocess env, or model-bound messages.); [packages/coding-agent/src/core/tools/bash.ts:193](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L193) (verified)
  - *To reach the next level:* Logs and transcripts would need redaction.
- **D L1:** Install telemetry is on by default and content-free. — [packages/coding-agent/src/core/settings-manager.ts:1141-1142](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/settings-manager.ts#L1141-L1142); [packages/coding-agent/src/modes/interactive/interactive-mode.ts:1356](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/modes/interactive/interactive-mode.ts#L1356) (verified)
  - *To reach the next level:* Telemetry would need to be opt-in.
- **B L0:** Long-lived provider keys and ambient tokens are reachable by the model and every subprocess. — [packages/coding-agent/src/utils/shell.ts:138-149](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L138-L149); [packages/coding-agent/src/core/auth-storage.ts:52](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/auth-storage.ts#L52) (verified)
  - *To reach the next level:* Keys would need to be kept out of subprocess env and model reach.
- **Cap:** none

### C9 Audit & traceability — 0.45 (medium)

Every message, tool call and tool result is appended to a JSONL session file under ~/.pi/agent/sessions as it happens, giving a replayable local trajectory. It is outside the project folder by default, but the agent's own shell can edit it, and a repository's .pi/settings.json can relocate it before trust is decided. There is no actor attribution beyond message roles, no approval records (there are no approvals), and no tamper evidence or export.

- **S L2:** Structured per-message transcript including tool calls, results and timestamps. — [packages/coding-agent/src/core/agent-session.ts:1115-1133](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/agent-session.ts#L1115-L1133); [packages/coding-agent/src/core/session-manager.ts:1172-1188](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/session-manager.ts#L1172-L1188) (verified)
  - *To reach the next level:* No actor/approver attribution or correlation IDs.
- **C L2:** All tools, including extension and MCP tools, flow through the same message stream. — [packages/coding-agent/src/core/agent-session.ts:1115-1133](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/agent-session.ts#L1115-L1133) (verified)
  - *To reach the next level:* Approvals and denials are not recorded because there is no gate.
- **D L1:** On by default outside the workspace, but a repo-controlled sessionDir (read pre-trust) can move it into the workspace, and bash can edit it. — [packages/coding-agent/src/core/session-manager.ts:589-593](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/session-manager.ts#L589-L593); [packages/coding-agent/src/main.ts:688-693](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/main.ts#L688-L693); [packages/coding-agent/docs/security.md:31](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/docs/security.md#L31) (verified)
  - *To reach the next level:* Location would need to be outside workspace control and model-writable paths.
- **B L2:** Entries are appended synchronously per message; the agent loop awaits event handlers, so records are flushed per action (inferred from the awaited emit chain). — [packages/coding-agent/src/core/session-manager.ts:1172-1188](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/session-manager.ts#L1172-L1188); [packages/agent/src/agent-loop.ts:454](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/agent/src/agent-loop.ts#L454) (inferred)
  - *To reach the next level:* No fail-closed behaviour; actions do not depend on the record being written.
- **Cap:** none

### C10 Limits & kill switch — 0.00 (high)

There are no limits on turns, wall-clock time or spend, and the bash tool has no default timeout. Stopping works: abort cancels the agent run and kills the shell's process group with SIGKILL, and shutdown signals kill tracked children. But a runaway session can loop and spend indefinitely until a human intervenes.

- **S L0:** No step, time, or cost limit; a working abort exists (process-group kill) but nothing bounds a run. — searched `rg -n -i 'maxTurns|maxSteps|maxIterations|maxCost|costLimit'` in `packages/coding-agent/src` → 0 hits (No step, iteration, or cost cap anywhere in the CLI.); [packages/coding-agent/src/core/tools/bash.ts:42](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L42); [packages/coding-agent/src/core/agent-session.ts:2387-2397](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/agent-session.ts#L2387-L2397); [packages/coding-agent/src/utils/shell.ts:206-208](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L206-L208) (verified)
  - *To reach the next level:* An iteration cap plus a wall-clock or token/cost cap enforced in code.
- **C L0:** No limits exist to apply to anything. — searched `rg -n -i 'maxTurns|maxSteps|maxIterations|maxCost|costLimit'` in `packages/coding-agent/src` → 0 hits (No step, iteration, or cost cap anywhere in the CLI.) (verified)
  - *To reach the next level:* Limits would need to cover at least the top-level loop.
- **D L0:** Unlimited by default. — searched `rg -n -i 'maxTurns|maxSteps|maxIterations|maxCost|costLimit'` in `packages/coding-agent/src` → 0 hits (No step, iteration, or cost cap anywhere in the CLI.); [packages/coding-agent/src/core/tools/bash.ts:42](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/core/tools/bash.ts#L42) (verified)
  - *To reach the next level:* Sensible default limits.
- **B L0:** No ceiling: a runaway can act and spend indefinitely; stopping does kill in-flight shell process groups. — searched `rg -n -i 'maxTurns|maxSteps|maxIterations|maxCost|costLimit'` in `packages/coding-agent/src` → 0 hits (No step, iteration, or cost cap anywhere in the CLI.); [packages/coding-agent/src/utils/shell.ts:206-208](https://github.com/earendil-works/pi/blob/4c6fb7cfe8c538a668726f6f8b3554098c39faee/packages/coding-agent/src/utils/shell.ts#L206-L208) (verified)
  - *To reach the next level:* Any time or cost ceiling.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Repo files via read/bash and AGENTS.md/CLAUDE.md from all ancestors (packages/coding-agent/src/core/resource-loader.ts:183) · [B] sensitive data/systems: Full user env passed to every shell command (packages/coding-agent/src/utils/shell.ts:146) and ~/.pi/agent/auth.json · [C] state change / egress: Ungated bash with network and file write (packages/coding-agent/src/core/tools/bash.ts:112) · Same default session? Yes

## Highest-impact improvements
1. Ship a default-on approval gate for bash/write/edit that shows the exact command or diff, with a parsed read-only allowlist. — C2 S L0→L3, +0.225 before caps (Playbook 5)
2. Run bash through an OS sandbox (bubblewrap/Seatbelt) by default, workspace-only writes and network off, using the existing sandbox example as the basis. — C4 S L0→L3, +0.225 before caps (Playbook 3 step 1)
3. Pass shell commands a scrubbed environment (drop provider keys and *_TOKEN/*_KEY variables) instead of the full process.env. — C1 C L0→L1, +0.075 before caps (Playbook 4)
4. Add default turn and wall-clock limits and a default bash timeout. — C10 S L0→L2, +0.150 before caps (Playbook 3 step 3)
5. Deny tool writes to ~/.pi/agent (trust.json, extensions, settings) and to .pi/ inside the project, and gate AGENTS.md/CLAUDE.md behind project trust. — C6 S L2→L3, +0.075 before caps (Playbook 2)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- Only packages/coding-agent was reviewed. Behaviour of sibling monorepo packages (packages/agent loop, packages/mcp transport, packages/ai providers) was consulted only where cited and otherwise not examined.
- The experimental server/radius/vacation/durable code under src/experimental is excluded from the published npm package (package.json files list) and was not scored.
- Example extensions (permission-gate, sandbox, protected-paths, git-checkpoint) are sample code that users must copy and install; they were not credited as shipped controls.
- No release tag points at the pinned commit; package.json reports version 1.0.1.
- C9 B is inferred from the awaited event chain rather than traced through every agent-core listener path.
- No reviewer-steering text was found in AGENTS.md, README, or docs.
