Module: SDD-B11 — Tau: The Reference Harness to Attack and Harden Diagram count: 5 Tool: Mermaid (primary). Each diagram validated in Mermaid Live Editor.
Type: Architecture / boundary map
Purpose: The foundational visual. Tau is Hugging Face's educational reimplementation of Pi, split into three packages with a load-bearing boundary: tau_ai (provider/model streaming), tau_agent (the reusable brain: harness, loop, tools, events, sessions), and tau_coding (the coding app: CLI/TUI, read/write/edit/bash tools, skills, on-disk sessions). The core does not know about Textual, Rich, local config paths, slash commands, or rendering. Frontends consume events. This separation is what makes tau a clean target: security controls attach in tau_agent (enforcement) or tau_coding (where the attack surface lives). The diagram is the map every extension point and every B-module wedge refers back to.
Reading the diagram: Three stacked layers. The provider-neutral streaming layer at the top; the reusable brain in the middle (where the loop, tools, events, and harness live); the coding app at the bottom (where the filesystem, credentials, sessions, and bash tool live). The teal boundary between tau_agent and tau_coding is the load-bearing one — the line enforcement belongs above, attack surface below.
flowchart TB
subgraph AI["tau_ai — PROVIDER/MODEL STREAMING (provider-neutral)"]
direction TB
A1["OpenAI · Anthropic · Codex subscription<br/>OpenRouter · Hugging Face · local models"]
A2["create_model_provider()<br/>provider_runtime.py:46<br/>CredentialReader protocol → provider config"]
end
subgraph AGENT["tau_agent — THE REUSABLE BRAIN"]
direction TB
G1["AgentHarness (harness.py)<br/>subscribe(listener) — EventListener · events.py<br/>run_agent_loop — loop.py"]
G2["_execute_tool_calls (loop.py:190)<br/>tool.execute → tool.executor<br/>_tool_result_message (loop.py:260)"]
G3["AgentTool (tools.py:61) — frozen dataclass<br/>executor: ToolExecutor (Protocol)<br/>→ EXTENSION POINT 1 (B2/B4)"]
G4["events: TurnStart · ToolExecutionStart<br/>ToolExecutionEnd (carries result) · MessageEnd<br/>→ EXTENSION POINT 4 (B8, read-only)"]
end
subgraph CODING["tau_coding — THE CODING APP"]
direction TB
C1["CodingSession (session.py, 88KB)<br/>run_terminal_command (session.py:1183)<br/>load() tool-list construction (session.py:286)"]
C2["create_bash_tool (tools.py:574)<br/>create_bash_tool_definition (tools.py:433)<br/>asyncio.create_subprocess_shell (tools.py:470)"]
C3["SessionStorage JSONL (session.py:187)<br/>_persist_messages_since (session.py:1378)<br/>~/.tau/sessions/"]
C4["FileCredentialStore (credentials.py)<br/>~/.tau/credentials.json (plaintext 0600)<br/>oauth.py"]
end
AI --> AGENT
AGENT -->|"load-bearing boundary —<br/>enforcement lives ABOVE, attack surface BELOW"| BOUND["BOUNDARY<br/>tau_agent ↔ tau_coding"]:::teal
BOUND --> CODING
FE["FRONTENDS (Textual TUI, CLI)<br/>consume events — never in the trusted path"]:::muted
AGENT -.->|"events"| FE
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef muted fill:#14141f,stroke:#9494a0,stroke-width:1px,color:#9494a0
style AI fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style AGENT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style CODING fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style A1 fill:#101018,stroke:#5eead4,color:#e4e4e8
style A2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G1 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G3 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G4 fill:#101018,stroke:#5eead4,color:#e4e4e8
style C1 fill:#101018,stroke:#f08080,color:#e4e4e8
style C2 fill:#101018,stroke:#f08080,color:#e4e4e8
style C3 fill:#101018,stroke:#f08080,color:#e4e4e8
style C4 fill:#101018,stroke:#f08080,color:#e4e4e8
style FE fill:#14141f,stroke:#9494a0,color:#9494a0
Note: The three properties that make tau the ideal teaching target: it is real (a
bashtool that actually runsasyncio.create_subprocess_shell; a credential store that actually holds plaintext API keys on disk), it is small (~3,000 LOC across three packages, readable in an afternoon), and it is undefended (none of the controls B2–B8 build exist today). Every control you add is a net-new feature, and its absence is the vulnerability you are closing. If DD-01 was "here is the simplest harness that works," SDD-B11 is "here is the simplest harness we can break and fix."
Type: Surface map / vulnerability matrix Purpose: The core of the deep-dive. Each of tau's seven attack surfaces from B1's threat model, the OWASP ASI risk it exposes, the module that covers it, tau's current undefended state, and the control that closes it. This is the "before" picture — the raw, injectable, exfiltratable harness that Course 2B teaches you to harden. Every surface is currently undefended; every defense is a missing feature that attaches at a clean, explicit extension point. The delta between this diagram and the hardened session (Diagram 5) is the scorecard the capstone measures. Reading the diagram: Seven rows, one per surface. Each surface (danger) is undefended today; the control that closes it (teal) attaches at the named extension point on the right. Note the three patterns that recur: tool-output is unfiltered and reaches the transcript (Surfaces 1, 2, 3); the bash tool runs with zero isolation and reads plaintext credentials (Surfaces 5, 6); the event stream is emitted but not analyzed (Surface 7).
flowchart LR
subgraph SURF["TAU'S 7 SURFACES (B1) — ALL UNDEFENDED TODAY"]
direction TB
S1["SURFACE 1 — Agent loop (loop.py:190)<br/>no interception · executes on request<br/>ASI09 Agent Leftover"]:::danger
S2["SURFACE 2 — Tool output (loop.py:260)<br/>unfiltered into transcript<br/>ASI01 Injection"]:::danger
S3["SURFACE 3 — Sessions (session.py:1378)<br/>no memory-write gate · JSONL poison persists<br/>ASI06 Memory"]:::danger
S4["SURFACE 4 — Provider (tau_ai, provider_config.py)<br/>no provider-authz check · any tool reaches API<br/>ASI03 Excessive Agency"]:::danger
S5["SURFACE 5 — Credentials (credentials.py)<br/>plaintext 0600 · bash can cat/printenv<br/>ASI10 Broken Access Control"]:::danger
S6["SURFACE 6 — Sandbox (tools.py:470)<br/>zero isolation · no egress/allowlist/caps<br/>ASI03 Excessive Agency"]:::danger
S7["SURFACE 7 — Observability (events.py, harness.py:124)<br/>events emitted · no listener analyzes<br/>ASI11 Cross-session"]:::danger
end
subgraph CTRL["THE CONTROL THAT CLOSES IT"]
direction TB
D1["B2 — Taint gate + injection detector<br/>wrap the executor"]:::teal
D2["B2/B4 — Tag tool output untrusted<br/>sanitize at the executor wrapper"]:::teal
D3["B3 — Harness-managed writes<br/>validate at _persist_messages_since"]:::teal
D4["B0/B1 — Scope enforcement<br/>gated tool list at construction"]:::teal
D5["B5 — Credential vault<br/>replace FileCredentialStore"]:::teal
D6["B7 — Default-deny egress + allowlist<br/>wrap the bash factory"]:::teal
D7["B8 — Session intent tracker<br/>subscribe a listener"]:::teal
end
S1 -->|"Extension Point 1"| D1
S2 -->|"Extension Point 1"| D2
S3 -->|"Extension Point 1 or _persist chokepoint"| D3
S4 -->|"Extension Point 5"| D4
S5 -->|"Extension Point 3"| D5
S6 -->|"Extension Point 2"| D6
S7 -->|"Extension Point 4 (additive)"| D7
classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
style SURF fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style CTRL fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style S1 fill:#101018,stroke:#f08080,color:#e4e4e8
style S2 fill:#101018,stroke:#f08080,color:#e4e4e8
style S3 fill:#101018,stroke:#f08080,color:#e4e4e8
style S4 fill:#101018,stroke:#f08080,color:#e4e4e8
style S5 fill:#101018,stroke:#f08080,color:#e4e4e8
style S6 fill:#101018,stroke:#f08080,color:#e4e4e8
style S7 fill:#101018,stroke:#f08080,color:#e4e4e8
style D1 fill:#101018,stroke:#5eead4,color:#5eead4
style D2 fill:#101018,stroke:#5eead4,color:#5eead4
style D3 fill:#101018,stroke:#5eead4,color:#5eead4
style D4 fill:#101018,stroke:#5eead4,color:#5eead4
style D5 fill:#101018,stroke:#5eead4,color:#5eead4
style D6 fill:#101018,stroke:#5eead4,color:#5eead4
style D7 fill:#101018,stroke:#5eead4,color:#5eead4
Note: The recurring architectural lesson: enforcement belongs in the tool, not in the loop. The loop delegates to
tool.execute, which delegates totool.executor. You do not patchrun_agent_loopor_execute_tool_callsto add a pre-execution gate — you wrap the executor (Extension Point 1) and the loop stays untouched. The only control that wedges higher than the tool layer is B3's memory-write gate at_persist_messages_since(session.py:1378), the single chokepoint where every message becomes durable.
Type: Control-attachment map
Purpose: This is the map the capstone and the plugin pack use. Tau has no plugin system — no register_tool, no hook framework, no middleware (verified by grepping the entire source). Every control attaches by wrapping or replacing objects at explicit, named sites. This is a feature, not a limitation: it makes every wedge point auditable and teachable. Five extension points, five B-modules, no monkey-patching required for four of the five. The diagram shows exactly where each control attaches and what it replaces.
Reading the diagram: Five lanes, top to bottom. Each lane is an extension point: where it lives in the code, what it wraps or replaces, and which B-module's control attaches there. The teal lanes are wrapping (no code modification); the warn lane (Extension Point 3) is replacement (the vault replaces FileCredentialStore by implementing the CredentialReader protocol). Note that four of the five are additive or wrapping; only the credential store is a straight replacement.
flowchart TB
subgraph EP1["EXTENSION POINT 1 — Tool-executor wrapping (B2, B4)"]
direction TB
P1A["AgentTool (tools.py:61) — frozen dataclass<br/>ToolExecutor is a Protocol (async callable)"]
P1B["wrap_with_gate(inner, gate)<br/>dataclasses.replace(inner, executor=gated)<br/>flows to CodingSessionConfig.tools → AgentHarnessConfig"]
P1C["NO monkey-patching · NO subclassing<br/>NO framework registration"]:::teal
P1A --> P1B --> P1C
end
subgraph EP2["EXTENSION POINT 2 — The bash tool factory (B7)"]
direction TB
P2A["create_bash_tool (tools.py:574) — BOTH bash paths:<br/>(1) create_coding_tools() harness list (tools.py:96)<br/>(2) run_terminal_command bar (session.py:1183)"]
P2B["create_hardened_bash_tool(cwd, prefix, policy)<br/>wraps executor returned by create_bash_tool_definition<br/>policy.check(command) → allowlist/denylist/egress"]
P2C["wedge at the FACTORY, not the instance<br/>or run_terminal_command bypasses you"]:::warn
P2A --> P2B --> P2C
end
subgraph EP3["EXTENSION POINT 3 — Credential store replacement (B5)"]
direction TB
P3A["create_model_provider (provider_runtime.py:46)<br/>FileCredentialStore() default at line 56<br/>CredentialReader protocol: get(name) → str | None"]
P3B["inject vault: create_model_provider(credential_store=my_vault)<br/>ALSO replace the second FileCredentialStore<br/>in CodingSession.__init__ (session.py:253-255)"]
P3C["B5 secures at rest + provider boundary<br/>B7 blocks the exfiltration channel (coupled)"]:::warn
P3A --> P3B --> P3C
end
subgraph EP4["EXTENSION POINT 4 — Event-stream subscription (B8)"]
direction TB
P4A["AgentHarness.subscribe(listener) (harness.py:124)<br/>EventListener = Callable[[AgentEvent], Awaitable|None]"]
P4B["attach_intent_tracker(harness)<br/>ToolExecutionStartEvent → observe_request<br/>ToolExecutionEndEvent → observe_result"]
P4C["purely ADDITIVE · read-only<br/>cannot block — enforcement is EP1"]:::teal
P4A --> P4B --> P4C
end
subgraph EP5["EXTENSION POINT 5 — Tool-list construction (B0/B1)"]
direction TB
P5A["CodingSession.load (session.py:286-293)<br/>config.tools if provided, else create_coding_tools()"]
P5B["inject hardened tools list — every executor wrapped:<br/>scope gate (B0/B1) + taint gate (B2) + sandbox (B7)<br/>flows to AgentHarnessConfig(tools=...) at session.py:310"]
P5C["single injection site · persists across reloads<br/>the entry point for the hardened session"]:::teal
P5A --> P5B --> P5C
end
classDef teal fill:#101018,stroke:#5eead4,color:#5eead4
classDef warn fill:#101018,stroke:#f0a868,color:#f0a868
style EP1 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP2 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP3 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP4 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP5 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style P1A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P1B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P2A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P2B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P3A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P3B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P4A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P4B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P5A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P5B fill:#101018,stroke:#5eead4,color:#e4e4e8
Note: The anti-patterns this map prevents: (1) patching the loop instead of wrapping the executor — the loop delegates to
tool.executor, wrap there; (2) wrapping only the harness bash tool and missingrun_terminal_command— wedge at the factory; (3) overloadingshell_command_prefixas an allowlist — it is a blind prepend (f"{prefix}\n{command}") that cannot see or reject the command; (4) looking for aregister_toolhook — there is none, construct a customCodingSessionConfig; (5) using the event listener for enforcement — events fire after the decision, they are read-only observability.
Type: Bypass flow / factory-level fix
Purpose: The single most important implementation detail in tau for a security engineer. A B7 sandbox that only wraps tools in the harness tool list (Extension Point 1) will be bypassed by the terminal-command bar, which constructs its own bash tool outside the list and calls it directly. Both paths go through create_bash_tool (tools.py:574), but only the harness path is reachable from the tool-list injection site. The clean fix is to wedge at the factory level — provide a hardened create_bash_tool replacement that both call sites use. The wedge point is the factory, not the individual tool instances.
Reading the diagram: Two parallel bash paths from the same factory. The danger path (terminal command bar) bypasses any tool-list-level control because it never enters CodingSessionConfig.tools. The fix (teal) is the hardened factory that replaces create_bash_tool itself, so both paths receive the sandbox policy regardless of how they were constructed.
flowchart TB
FACT["create_bash_tool factory<br/>tools.py:574"]:::teal
subgraph PATH1["PATH 1 — HARNESS TOOL LIST (reachable from EP1/EP5)"]
direction TB
H1["create_coding_tools()<br/>tools.py:96"]
H2["create_bash_tool(cwd=root, shell_command_prefix=...)<br/>→ enters CodingSessionConfig.tools"]
H3["flows to AgentHarnessConfig(tools=...)<br/>→ loop executes via tool.executor"]
H1 --> H2 --> H3
end
subgraph PATH2["PATH 2 — TERMINAL COMMAND BAR (BYPASS)"]
direction TB
T1["CodingSession.run_terminal_command<br/>session.py:1183"]
T2["constructs its OWN create_bash_tool<br/>(cwd=self.cwd, shell_command_prefix=...)<br/>calls it directly at session.py:1187"]
T3["NEVER enters CodingSessionConfig.tools<br/>NEVER seen by a tool-list-level gate"]:::danger
T1 --> T2 --> T3
end
FACT --> PATH1
FACT --> PATH2
PATH1 -.->|"tool-list wrap (EP1) covers this"| COVERED1["SANDBOX APPLIES<br/>if you wrap EP1"]:::good
PATH2 -.->|"tool-list wrap (EP1) does NOT cover this"| BYPASSED["SANDBOX BYPASSED<br/>tool-list gate never sees it"]:::danger
FIX["THE FIX — wedge at the FACTORY<br/>replace create_bash_tool itself<br/>both call sites receive the sandbox policy"]:::teal
FACT ==>|"hardened factory replacement"| FIX
FIX -.->|"covers PATH 1"| COVERED1
FIX -.->|"covers PATH 2"| COVERED2["SANDBOX APPLIES<br/>both paths"]:::good
NOTE["shell_command_prefix is NOT an allowlist hook<br/>it is a blind prepend: f'{prefix}\\n{command}'<br/>evaluated as a shell preamble — cannot see/reject the command"]:::warn
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
style FACT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
style PATH1 fill:#14141f,stroke:#82e0aa,stroke-width:1px,color:#e4e4e8
style PATH2 fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style H1 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style H2 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style H3 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style T1 fill:#101018,stroke:#f08080,color:#e4e4e8
style T2 fill:#101018,stroke:#f08080,color:#e4e4e8
style T3 fill:#101018,stroke:#f08080,color:#f08080
style COVERED1 fill:#101018,stroke:#82e0aa,color:#82e0aa
style COVERED2 fill:#101018,stroke:#82e0aa,color:#82e0aa
style BYPASSED fill:#101018,stroke:#f08080,color:#f08080
style FIX fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
style NOTE fill:#14141f,stroke:#f0a868,stroke-width:1px,color:#f0a868
Note: This finding generalizes: any control that wedges at the tool-list layer (Extension Point 1 or 5) is bypassable by any code path that constructs its own tool outside the list. The factory is the chokepoint. When you audit a harness for a control's coverage, do not ask "is the tool in the list?" — ask "is there any code path that constructs this tool outside the list?" The factory-level wedge is the only one that answers both.
Type: Composition / assembled architecture
Purpose: The "after" picture. The plugin pack assembles all five extension points into a single entry point (create_hardened_session). This diagram shows the composition: every tool wrapped with the taint gate (B2) and the scope gate (B0/B1); bash wrapped with the sandbox policy (B7) at the factory level so both paths are covered; credentials read from the vault, not plaintext (B5); the intent tracker subscribed to the event stream (B8). The scorecard harness measures the before/after: run the InjecAgent-style injection battery against unmodified tau, then against this hardened session. The delta is the defense scorecard.
Reading the diagram: The five extension points from Diagram 3, assembled. The teal boxes are the controls now in place; the danger boxes are the surfaces they close (each surface from Diagram 2 now has its control attached). The single entry point at the top is create_hardened_session(cwd=...) — the composition that the capstone installs and the scorecard measures.
flowchart TB
ENTRY["create_hardened_session(cwd='/my/project')<br/>tau_plugins.hardened_session — single entry point"]:::teal
subgraph COMP["THE COMPOSITION — ALL 5 EXTENSION POINTS ASSEMBLED"]
direction TB
subgraph C1["EP5 + EP1 — Tool-list + executor wrapping"]
direction TB
E5["inject hardened tools at CodingSession.load (session.py:286)<br/>EVERY tool's executor wrapped:"]
E5A["scope gate (B0/B1) — every outbound action gated"]:::teal
E5B["taint gate (B2) — pre-execution inspect + post-execution sanitize"]:::teal
E5C["tool output tagged <untrusted> before transcript"]:::teal
E5 --> E5A --> E5B --> E5C
end
subgraph C2["EP2 — Factory-level bash sandbox"]
direction TB
E2["hardened create_bash_tool REPLACES the factory<br/>BOTH paths (harness list + run_terminal_command) covered"]
E2A["default-deny network egress"]:::teal
E2B["command allowlist / denylist"]:::teal
E2C["resource caps (CPU, memory, time)"]:::teal
E2 --> E2A --> E2B --> E2C
end
subgraph C3["EP3 — Credential vault"]
direction TB
E3["vault replaces FileCredentialStore in BOTH sites:<br/>create_model_provider (provider_runtime.py:46)<br/>CodingSession.__init__ (session.py:253)"]
E3A["credentials never plaintext on disk"]:::teal
E3B["agent process cannot reach the store<br/>only the provider layer reads via CredentialReader"]:::teal
E3C["bash denylist blocks cat ~/.tau/credentials.json + printenv"]:::teal
E3 --> E3A --> E3B --> E3C
end
subgraph C4["EP4 — Intent tracker subscription"]
direction TB
E4["harness.subscribe(intent_tracker)<br/>purely additive, read-only"]
E4A["ToolExecutionStartEvent → observe_request"]:::teal
E4B["ToolExecutionEndEvent → observe_result"]:::teal
E4C["drift detection → flag / halt / alert"]:::teal
E4 --> E4A --> E4B --> E4C
end
end
SCORECARD["THE SCORECARD<br/>InjecAgent-style injection battery:<br/>unmodified tau (before) vs hardened session (after)<br/>the delta is the defense score — the artifact Capstone B1 ships"]:::good
ENTRY --> COMP
COMP --> SCORECARD
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
style ENTRY fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
style COMP fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style C1 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C2 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C3 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C4 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style E5 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E5A fill:#101018,stroke:#5eead4,color:#5eead4
style E5B fill:#101018,stroke:#5eead4,color:#5eead4
style E5C fill:#101018,stroke:#5eead4,color:#5eead4
style E2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E2A fill:#101018,stroke:#5eead4,color:#5eead4
style E2B fill:#101018,stroke:#5eead4,color:#5eead4
style E2C fill:#101018,stroke:#5eead4,color:#5eead4
style E3 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E3A fill:#101018,stroke:#5eead4,color:#5eead4
style E3B fill:#101018,stroke:#5eead4,color:#5eead4
style E3C fill:#101018,stroke:#5eead4,color:#5eead4
style E4 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E4A fill:#101018,stroke:#5eead4,color:#5eead4
style E4B fill:#101018,stroke:#5eead4,color:#5eead4
style E4C fill:#101018,stroke:#5eead4,color:#5eead4
style SCORECARD fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
Note: The composition is the point. No single extension point is sufficient — B5 secures credentials at rest but the agent can still
catthem without B7; B7 blocks exfiltration but the bash tool still runs injected commands without B2; B2 taints tool output but the JSONL still persists poison without B3. The hardened session composes all five because the surfaces overlap and the controls are coupled. The scorecard measures the delta, not any individual control — and that delta is what Capstone B1 (harden tau) and Capstone B2 (red-team tau) trade against each other.
# Diagrams — SDD-B11: Tau: The Reference Harness to Attack and Harden
**Module**: SDD-B11 — Tau: The Reference Harness to Attack and Harden
**Diagram count**: 5
**Tool**: Mermaid (primary). Each diagram validated in [Mermaid Live Editor](https://mermaid.live).
---
## Diagram 1 — tau's Three-Layer Architecture
**Type**: Architecture / boundary map
**Purpose**: The foundational visual. Tau is Hugging Face's educational reimplementation of Pi, split into three packages with a load-bearing boundary: `tau_ai` (provider/model streaming), `tau_agent` (the reusable brain: harness, loop, tools, events, sessions), and `tau_coding` (the coding app: CLI/TUI, read/write/edit/bash tools, skills, on-disk sessions). The core does not know about Textual, Rich, local config paths, slash commands, or rendering. Frontends consume events. This separation is what makes tau a clean target: security controls attach in `tau_agent` (enforcement) or `tau_coding` (where the attack surface lives). The diagram is the map every extension point and every B-module wedge refers back to.
**Reading the diagram**: Three stacked layers. The provider-neutral streaming layer at the top; the reusable brain in the middle (where the loop, tools, events, and harness live); the coding app at the bottom (where the filesystem, credentials, sessions, and bash tool live). The teal boundary between `tau_agent` and `tau_coding` is the load-bearing one — the line enforcement belongs above, attack surface below.
```mermaid
flowchart TB
subgraph AI["tau_ai — PROVIDER/MODEL STREAMING (provider-neutral)"]
direction TB
A1["OpenAI · Anthropic · Codex subscription<br/>OpenRouter · Hugging Face · local models"]
A2["create_model_provider()<br/>provider_runtime.py:46<br/>CredentialReader protocol → provider config"]
end
subgraph AGENT["tau_agent — THE REUSABLE BRAIN"]
direction TB
G1["AgentHarness (harness.py)<br/>subscribe(listener) — EventListener · events.py<br/>run_agent_loop — loop.py"]
G2["_execute_tool_calls (loop.py:190)<br/>tool.execute → tool.executor<br/>_tool_result_message (loop.py:260)"]
G3["AgentTool (tools.py:61) — frozen dataclass<br/>executor: ToolExecutor (Protocol)<br/>→ EXTENSION POINT 1 (B2/B4)"]
G4["events: TurnStart · ToolExecutionStart<br/>ToolExecutionEnd (carries result) · MessageEnd<br/>→ EXTENSION POINT 4 (B8, read-only)"]
end
subgraph CODING["tau_coding — THE CODING APP"]
direction TB
C1["CodingSession (session.py, 88KB)<br/>run_terminal_command (session.py:1183)<br/>load() tool-list construction (session.py:286)"]
C2["create_bash_tool (tools.py:574)<br/>create_bash_tool_definition (tools.py:433)<br/>asyncio.create_subprocess_shell (tools.py:470)"]
C3["SessionStorage JSONL (session.py:187)<br/>_persist_messages_since (session.py:1378)<br/>~/.tau/sessions/"]
C4["FileCredentialStore (credentials.py)<br/>~/.tau/credentials.json (plaintext 0600)<br/>oauth.py"]
end
AI --> AGENT
AGENT -->|"load-bearing boundary —<br/>enforcement lives ABOVE, attack surface BELOW"| BOUND["BOUNDARY<br/>tau_agent ↔ tau_coding"]:::teal
BOUND --> CODING
FE["FRONTENDS (Textual TUI, CLI)<br/>consume events — never in the trusted path"]:::muted
AGENT -.->|"events"| FE
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef muted fill:#14141f,stroke:#9494a0,stroke-width:1px,color:#9494a0
style AI fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style AGENT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style CODING fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style A1 fill:#101018,stroke:#5eead4,color:#e4e4e8
style A2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G1 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G3 fill:#101018,stroke:#5eead4,color:#e4e4e8
style G4 fill:#101018,stroke:#5eead4,color:#e4e4e8
style C1 fill:#101018,stroke:#f08080,color:#e4e4e8
style C2 fill:#101018,stroke:#f08080,color:#e4e4e8
style C3 fill:#101018,stroke:#f08080,color:#e4e4e8
style C4 fill:#101018,stroke:#f08080,color:#e4e4e8
style FE fill:#14141f,stroke:#9494a0,color:#9494a0
```
> **Note**: The three properties that make tau the ideal teaching target: it is real (a `bash` tool that actually runs `asyncio.create_subprocess_shell`; a credential store that actually holds plaintext API keys on disk), it is small (~3,000 LOC across three packages, readable in an afternoon), and it is undefended (none of the controls B2–B8 build exist today). Every control you add is a net-new feature, and its absence is the vulnerability you are closing. If DD-01 was "here is the simplest harness that works," SDD-B11 is "here is the simplest harness we can break and fix."
---
## Diagram 2 — The "Before" Picture: tau's 7 Surfaces Mapped to B-Modules
**Type**: Surface map / vulnerability matrix
**Purpose**: The core of the deep-dive. Each of tau's seven attack surfaces from B1's threat model, the OWASP ASI risk it exposes, the module that covers it, tau's current undefended state, and the control that closes it. This is the "before" picture — the raw, injectable, exfiltratable harness that Course 2B teaches you to harden. Every surface is currently undefended; every defense is a missing feature that attaches at a clean, explicit extension point. The delta between this diagram and the hardened session (Diagram 5) is the scorecard the capstone measures.
**Reading the diagram**: Seven rows, one per surface. Each surface (danger) is undefended today; the control that closes it (teal) attaches at the named extension point on the right. Note the three patterns that recur: tool-output is unfiltered and reaches the transcript (Surfaces 1, 2, 3); the bash tool runs with zero isolation and reads plaintext credentials (Surfaces 5, 6); the event stream is emitted but not analyzed (Surface 7).
```mermaid
flowchart LR
subgraph SURF["TAU'S 7 SURFACES (B1) — ALL UNDEFENDED TODAY"]
direction TB
S1["SURFACE 1 — Agent loop (loop.py:190)<br/>no interception · executes on request<br/>ASI09 Agent Leftover"]:::danger
S2["SURFACE 2 — Tool output (loop.py:260)<br/>unfiltered into transcript<br/>ASI01 Injection"]:::danger
S3["SURFACE 3 — Sessions (session.py:1378)<br/>no memory-write gate · JSONL poison persists<br/>ASI06 Memory"]:::danger
S4["SURFACE 4 — Provider (tau_ai, provider_config.py)<br/>no provider-authz check · any tool reaches API<br/>ASI03 Excessive Agency"]:::danger
S5["SURFACE 5 — Credentials (credentials.py)<br/>plaintext 0600 · bash can cat/printenv<br/>ASI10 Broken Access Control"]:::danger
S6["SURFACE 6 — Sandbox (tools.py:470)<br/>zero isolation · no egress/allowlist/caps<br/>ASI03 Excessive Agency"]:::danger
S7["SURFACE 7 — Observability (events.py, harness.py:124)<br/>events emitted · no listener analyzes<br/>ASI11 Cross-session"]:::danger
end
subgraph CTRL["THE CONTROL THAT CLOSES IT"]
direction TB
D1["B2 — Taint gate + injection detector<br/>wrap the executor"]:::teal
D2["B2/B4 — Tag tool output untrusted<br/>sanitize at the executor wrapper"]:::teal
D3["B3 — Harness-managed writes<br/>validate at _persist_messages_since"]:::teal
D4["B0/B1 — Scope enforcement<br/>gated tool list at construction"]:::teal
D5["B5 — Credential vault<br/>replace FileCredentialStore"]:::teal
D6["B7 — Default-deny egress + allowlist<br/>wrap the bash factory"]:::teal
D7["B8 — Session intent tracker<br/>subscribe a listener"]:::teal
end
S1 -->|"Extension Point 1"| D1
S2 -->|"Extension Point 1"| D2
S3 -->|"Extension Point 1 or _persist chokepoint"| D3
S4 -->|"Extension Point 5"| D4
S5 -->|"Extension Point 3"| D5
S6 -->|"Extension Point 2"| D6
S7 -->|"Extension Point 4 (additive)"| D7
classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
style SURF fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style CTRL fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style S1 fill:#101018,stroke:#f08080,color:#e4e4e8
style S2 fill:#101018,stroke:#f08080,color:#e4e4e8
style S3 fill:#101018,stroke:#f08080,color:#e4e4e8
style S4 fill:#101018,stroke:#f08080,color:#e4e4e8
style S5 fill:#101018,stroke:#f08080,color:#e4e4e8
style S6 fill:#101018,stroke:#f08080,color:#e4e4e8
style S7 fill:#101018,stroke:#f08080,color:#e4e4e8
style D1 fill:#101018,stroke:#5eead4,color:#5eead4
style D2 fill:#101018,stroke:#5eead4,color:#5eead4
style D3 fill:#101018,stroke:#5eead4,color:#5eead4
style D4 fill:#101018,stroke:#5eead4,color:#5eead4
style D5 fill:#101018,stroke:#5eead4,color:#5eead4
style D6 fill:#101018,stroke:#5eead4,color:#5eead4
style D7 fill:#101018,stroke:#5eead4,color:#5eead4
```
> **Note**: The recurring architectural lesson: enforcement belongs in the tool, not in the loop. The loop delegates to `tool.execute`, which delegates to `tool.executor`. You do not patch `run_agent_loop` or `_execute_tool_calls` to add a pre-execution gate — you wrap the executor (Extension Point 1) and the loop stays untouched. The only control that wedges higher than the tool layer is B3's memory-write gate at `_persist_messages_since` (session.py:1378), the single chokepoint where every message becomes durable.
---
## Diagram 3 — The Five Extension Points Map
**Type**: Control-attachment map
**Purpose**: This is the map the capstone and the plugin pack use. Tau has no plugin system — no `register_tool`, no hook framework, no middleware (verified by grepping the entire source). Every control attaches by wrapping or replacing objects at explicit, named sites. This is a feature, not a limitation: it makes every wedge point auditable and teachable. Five extension points, five B-modules, no monkey-patching required for four of the five. The diagram shows exactly where each control attaches and what it replaces.
**Reading the diagram**: Five lanes, top to bottom. Each lane is an extension point: where it lives in the code, what it wraps or replaces, and which B-module's control attaches there. The teal lanes are wrapping (no code modification); the warn lane (Extension Point 3) is replacement (the vault replaces `FileCredentialStore` by implementing the `CredentialReader` protocol). Note that four of the five are additive or wrapping; only the credential store is a straight replacement.
```mermaid
flowchart TB
subgraph EP1["EXTENSION POINT 1 — Tool-executor wrapping (B2, B4)"]
direction TB
P1A["AgentTool (tools.py:61) — frozen dataclass<br/>ToolExecutor is a Protocol (async callable)"]
P1B["wrap_with_gate(inner, gate)<br/>dataclasses.replace(inner, executor=gated)<br/>flows to CodingSessionConfig.tools → AgentHarnessConfig"]
P1C["NO monkey-patching · NO subclassing<br/>NO framework registration"]:::teal
P1A --> P1B --> P1C
end
subgraph EP2["EXTENSION POINT 2 — The bash tool factory (B7)"]
direction TB
P2A["create_bash_tool (tools.py:574) — BOTH bash paths:<br/>(1) create_coding_tools() harness list (tools.py:96)<br/>(2) run_terminal_command bar (session.py:1183)"]
P2B["create_hardened_bash_tool(cwd, prefix, policy)<br/>wraps executor returned by create_bash_tool_definition<br/>policy.check(command) → allowlist/denylist/egress"]
P2C["wedge at the FACTORY, not the instance<br/>or run_terminal_command bypasses you"]:::warn
P2A --> P2B --> P2C
end
subgraph EP3["EXTENSION POINT 3 — Credential store replacement (B5)"]
direction TB
P3A["create_model_provider (provider_runtime.py:46)<br/>FileCredentialStore() default at line 56<br/>CredentialReader protocol: get(name) → str | None"]
P3B["inject vault: create_model_provider(credential_store=my_vault)<br/>ALSO replace the second FileCredentialStore<br/>in CodingSession.__init__ (session.py:253-255)"]
P3C["B5 secures at rest + provider boundary<br/>B7 blocks the exfiltration channel (coupled)"]:::warn
P3A --> P3B --> P3C
end
subgraph EP4["EXTENSION POINT 4 — Event-stream subscription (B8)"]
direction TB
P4A["AgentHarness.subscribe(listener) (harness.py:124)<br/>EventListener = Callable[[AgentEvent], Awaitable|None]"]
P4B["attach_intent_tracker(harness)<br/>ToolExecutionStartEvent → observe_request<br/>ToolExecutionEndEvent → observe_result"]
P4C["purely ADDITIVE · read-only<br/>cannot block — enforcement is EP1"]:::teal
P4A --> P4B --> P4C
end
subgraph EP5["EXTENSION POINT 5 — Tool-list construction (B0/B1)"]
direction TB
P5A["CodingSession.load (session.py:286-293)<br/>config.tools if provided, else create_coding_tools()"]
P5B["inject hardened tools list — every executor wrapped:<br/>scope gate (B0/B1) + taint gate (B2) + sandbox (B7)<br/>flows to AgentHarnessConfig(tools=...) at session.py:310"]
P5C["single injection site · persists across reloads<br/>the entry point for the hardened session"]:::teal
P5A --> P5B --> P5C
end
classDef teal fill:#101018,stroke:#5eead4,color:#5eead4
classDef warn fill:#101018,stroke:#f0a868,color:#f0a868
style EP1 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP2 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP3 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP4 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style EP5 fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style P1A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P1B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P2A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P2B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P3A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P3B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P4A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P4B fill:#101018,stroke:#5eead4,color:#e4e4e8
style P5A fill:#101018,stroke:#5eead4,color:#e4e4e8
style P5B fill:#101018,stroke:#5eead4,color:#e4e4e8
```
> **Note**: The anti-patterns this map prevents: (1) patching the loop instead of wrapping the executor — the loop delegates to `tool.executor`, wrap there; (2) wrapping only the harness bash tool and missing `run_terminal_command` — wedge at the factory; (3) overloading `shell_command_prefix` as an allowlist — it is a blind prepend (`f"{prefix}\n{command}"`) that cannot see or reject the command; (4) looking for a `register_tool` hook — there is none, construct a custom `CodingSessionConfig`; (5) using the event listener for enforcement — events fire after the decision, they are read-only observability.
---
## Diagram 4 — The Two-Bash-Tool Finding
**Type**: Bypass flow / factory-level fix
**Purpose**: The single most important implementation detail in tau for a security engineer. A B7 sandbox that only wraps tools in the harness tool list (Extension Point 1) will be bypassed by the terminal-command bar, which constructs its own bash tool outside the list and calls it directly. Both paths go through `create_bash_tool` (tools.py:574), but only the harness path is reachable from the tool-list injection site. The clean fix is to wedge at the factory level — provide a hardened `create_bash_tool` replacement that both call sites use. The wedge point is the factory, not the individual tool instances.
**Reading the diagram**: Two parallel bash paths from the same factory. The danger path (terminal command bar) bypasses any tool-list-level control because it never enters `CodingSessionConfig.tools`. The fix (teal) is the hardened factory that replaces `create_bash_tool` itself, so both paths receive the sandbox policy regardless of how they were constructed.
```mermaid
flowchart TB
FACT["create_bash_tool factory<br/>tools.py:574"]:::teal
subgraph PATH1["PATH 1 — HARNESS TOOL LIST (reachable from EP1/EP5)"]
direction TB
H1["create_coding_tools()<br/>tools.py:96"]
H2["create_bash_tool(cwd=root, shell_command_prefix=...)<br/>→ enters CodingSessionConfig.tools"]
H3["flows to AgentHarnessConfig(tools=...)<br/>→ loop executes via tool.executor"]
H1 --> H2 --> H3
end
subgraph PATH2["PATH 2 — TERMINAL COMMAND BAR (BYPASS)"]
direction TB
T1["CodingSession.run_terminal_command<br/>session.py:1183"]
T2["constructs its OWN create_bash_tool<br/>(cwd=self.cwd, shell_command_prefix=...)<br/>calls it directly at session.py:1187"]
T3["NEVER enters CodingSessionConfig.tools<br/>NEVER seen by a tool-list-level gate"]:::danger
T1 --> T2 --> T3
end
FACT --> PATH1
FACT --> PATH2
PATH1 -.->|"tool-list wrap (EP1) covers this"| COVERED1["SANDBOX APPLIES<br/>if you wrap EP1"]:::good
PATH2 -.->|"tool-list wrap (EP1) does NOT cover this"| BYPASSED["SANDBOX BYPASSED<br/>tool-list gate never sees it"]:::danger
FIX["THE FIX — wedge at the FACTORY<br/>replace create_bash_tool itself<br/>both call sites receive the sandbox policy"]:::teal
FACT ==>|"hardened factory replacement"| FIX
FIX -.->|"covers PATH 1"| COVERED1
FIX -.->|"covers PATH 2"| COVERED2["SANDBOX APPLIES<br/>both paths"]:::good
NOTE["shell_command_prefix is NOT an allowlist hook<br/>it is a blind prepend: f'{prefix}\\n{command}'<br/>evaluated as a shell preamble — cannot see/reject the command"]:::warn
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef danger fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#f08080
classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
classDef warn fill:#14141f,stroke:#f0a868,stroke-width:1.5px,color:#f0a868
style FACT fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
style PATH1 fill:#14141f,stroke:#82e0aa,stroke-width:1px,color:#e4e4e8
style PATH2 fill:#14141f,stroke:#f08080,stroke-width:1.5px,color:#e4e4e8
style H1 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style H2 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style H3 fill:#101018,stroke:#82e0aa,color:#e4e4e8
style T1 fill:#101018,stroke:#f08080,color:#e4e4e8
style T2 fill:#101018,stroke:#f08080,color:#e4e4e8
style T3 fill:#101018,stroke:#f08080,color:#f08080
style COVERED1 fill:#101018,stroke:#82e0aa,color:#82e0aa
style COVERED2 fill:#101018,stroke:#82e0aa,color:#82e0aa
style BYPASSED fill:#101018,stroke:#f08080,color:#f08080
style FIX fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
style NOTE fill:#14141f,stroke:#f0a868,stroke-width:1px,color:#f0a868
```
> **Note**: This finding generalizes: any control that wedges at the tool-list layer (Extension Point 1 or 5) is bypassable by any code path that constructs its own tool outside the list. The factory is the chokepoint. When you audit a harness for a control's coverage, do not ask "is the tool in the list?" — ask "is there any code path that constructs this tool outside the list?" The factory-level wedge is the only one that answers both.
---
## Diagram 5 — The Hardened Session Composition
**Type**: Composition / assembled architecture
**Purpose**: The "after" picture. The plugin pack assembles all five extension points into a single entry point (`create_hardened_session`). This diagram shows the composition: every tool wrapped with the taint gate (B2) and the scope gate (B0/B1); bash wrapped with the sandbox policy (B7) at the factory level so both paths are covered; credentials read from the vault, not plaintext (B5); the intent tracker subscribed to the event stream (B8). The scorecard harness measures the before/after: run the InjecAgent-style injection battery against unmodified tau, then against this hardened session. The delta is the defense scorecard.
**Reading the diagram**: The five extension points from Diagram 3, assembled. The teal boxes are the controls now in place; the danger boxes are the surfaces they close (each surface from Diagram 2 now has its control attached). The single entry point at the top is `create_hardened_session(cwd=...)` — the composition that the capstone installs and the scorecard measures.
```mermaid
flowchart TB
ENTRY["create_hardened_session(cwd='/my/project')<br/>tau_plugins.hardened_session — single entry point"]:::teal
subgraph COMP["THE COMPOSITION — ALL 5 EXTENSION POINTS ASSEMBLED"]
direction TB
subgraph C1["EP5 + EP1 — Tool-list + executor wrapping"]
direction TB
E5["inject hardened tools at CodingSession.load (session.py:286)<br/>EVERY tool's executor wrapped:"]
E5A["scope gate (B0/B1) — every outbound action gated"]:::teal
E5B["taint gate (B2) — pre-execution inspect + post-execution sanitize"]:::teal
E5C["tool output tagged <untrusted> before transcript"]:::teal
E5 --> E5A --> E5B --> E5C
end
subgraph C2["EP2 — Factory-level bash sandbox"]
direction TB
E2["hardened create_bash_tool REPLACES the factory<br/>BOTH paths (harness list + run_terminal_command) covered"]
E2A["default-deny network egress"]:::teal
E2B["command allowlist / denylist"]:::teal
E2C["resource caps (CPU, memory, time)"]:::teal
E2 --> E2A --> E2B --> E2C
end
subgraph C3["EP3 — Credential vault"]
direction TB
E3["vault replaces FileCredentialStore in BOTH sites:<br/>create_model_provider (provider_runtime.py:46)<br/>CodingSession.__init__ (session.py:253)"]
E3A["credentials never plaintext on disk"]:::teal
E3B["agent process cannot reach the store<br/>only the provider layer reads via CredentialReader"]:::teal
E3C["bash denylist blocks cat ~/.tau/credentials.json + printenv"]:::teal
E3 --> E3A --> E3B --> E3C
end
subgraph C4["EP4 — Intent tracker subscription"]
direction TB
E4["harness.subscribe(intent_tracker)<br/>purely additive, read-only"]
E4A["ToolExecutionStartEvent → observe_request"]:::teal
E4B["ToolExecutionEndEvent → observe_result"]:::teal
E4C["drift detection → flag / halt / alert"]:::teal
E4 --> E4A --> E4B --> E4C
end
end
SCORECARD["THE SCORECARD<br/>InjecAgent-style injection battery:<br/>unmodified tau (before) vs hardened session (after)<br/>the delta is the defense score — the artifact Capstone B1 ships"]:::good
ENTRY --> COMP
COMP --> SCORECARD
classDef teal fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#5eead4
classDef good fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
style ENTRY fill:#14141f,stroke:#5eead4,stroke-width:2px,color:#5eead4
style COMP fill:#14141f,stroke:#5eead4,stroke-width:1.5px,color:#e4e4e8
style C1 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C2 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C3 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style C4 fill:#14141f,stroke:#5eead4,stroke-width:1px,color:#e4e4e8
style E5 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E5A fill:#101018,stroke:#5eead4,color:#5eead4
style E5B fill:#101018,stroke:#5eead4,color:#5eead4
style E5C fill:#101018,stroke:#5eead4,color:#5eead4
style E2 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E2A fill:#101018,stroke:#5eead4,color:#5eead4
style E2B fill:#101018,stroke:#5eead4,color:#5eead4
style E2C fill:#101018,stroke:#5eead4,color:#5eead4
style E3 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E3A fill:#101018,stroke:#5eead4,color:#5eead4
style E3B fill:#101018,stroke:#5eead4,color:#5eead4
style E3C fill:#101018,stroke:#5eead4,color:#5eead4
style E4 fill:#101018,stroke:#5eead4,color:#e4e4e8
style E4A fill:#101018,stroke:#5eead4,color:#5eead4
style E4B fill:#101018,stroke:#5eead4,color:#5eead4
style E4C fill:#101018,stroke:#5eead4,color:#5eead4
style SCORECARD fill:#14141f,stroke:#82e0aa,stroke-width:1.5px,color:#82e0aa
```
> **Note**: The composition is the point. No single extension point is sufficient — B5 secures credentials at rest but the agent can still `cat` them without B7; B7 blocks exfiltration but the bash tool still runs injected commands without B2; B2 taints tool output but the JSONL still persists poison without B3. The hardened session composes all five because the surfaces overlap and the controls are coupled. The scorecard measures the delta, not any individual control — and that delta is what Capstone B1 (harden tau) and Capstone B2 (red-team tau) trade against each other.