Course: Course 2B — Securing & Attacking Harnesses and LLMs
Module: SDD-B11 — Tau: The Reference Harness to Attack and Harden
Duration: ~45 minutes (spoken at ~140 wpm)
Format: Verbatim transcript with [SLIDE N] cues. Read aloud or use as speaker notes.
[SLIDE 1 — Title]
Welcome to SDD-B11. Tau: The Reference Harness to Attack and Harden. For eight modules — B1 through B8 — we have been building controls in the abstract. The threat model, the injection-defense layers, the credential isolation, the sandbox, the observability. Every one of those controls is a missing feature in a real piece of software. This deep-dive is where we name that software, walk its surfaces, and map every control to the exact line of code where it attaches. Tau is the target. Every defense in B2 through B8 is a missing feature in tau today, and every one of them attaches at a clean, explicit extension point in roughly three thousand lines of readable Python. This deep-dive is the map.
[SLIDE 2 — What tau is — and why it is the target]
Tau is Hugging Face's educational reimplementation of Pi. Built by Alejandro AO under the Hugging Face organization, it is described as a small, readable terminal coding agent and a working example of how coding agents are built. The homepage is twotimespi dot dev. The code is at github dot com slash huggingface slash tau. The relationship matters. Pi, which Course 1 deep-dived in DD-01, is the minimal baseline — a four-tool, roughly twelve-hundred-line reference thin harness. Tau is Pi's educational clone. Same architecture, same philosophy, but with a three-layer package split that makes the extension points cleaner to teach.
The three-layer split. Tau AI is the provider and model streaming layer — provider-neutral, supporting OpenAI, Anthropic, Codex subscription auth, OpenRouter, Hugging Face, and local models. Tau agent is the reusable brain — the harness, the loop, the tools, the events, the session abstractions. Tau coding is the coding app — the CLI and Textual TUI, the read, write, edit, and bash tools, the skills, and the on-disk sessions. The load-bearing boundary is this: AgentHarness is the reusable brain. CodingSession is the coding-agent environment. The TUI is one possible frontend. The core — tau agent — 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 either in tau agent, where enforcement belongs, or in tau coding, where the attack surface lives.
[SLIDE 3 — Three properties that make tau ideal]
Why is tau the ideal teaching target for Course 2B? Three properties. First, it is real. Not a stub, not a simulation. A bash tool that actually runs asyncio dot create underscore subprocess shell. A read tool that actually reads files. A credential store that actually holds plaintext API keys on disk at dot-tau slash credentials dot json. When you attack tau, you are attacking a real system. Second, it is small. Roughly three thousand lines across three packages. A student can read the entire codebase in an afternoon and understand every line that matters for security. Third — and this is the decisive one — it is undefended. Tau has none of the controls that B2 through B8 build. This is the "before" picture: the raw, injectable, exfiltratable harness that the course teaches you to harden. 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."
[SLIDE 4 — B11.1 section title]
Sub-section one. The before picture: seven surfaces, zero defenses. Tau's attack surface mapped to B-modules.
[SLIDE 5 — Seven surfaces, all undefended today]
This is the core of the deep-dive. Each of tau's seven surfaces, the OWASP ASI risk it exposes, the module that covers it, and tau's current undefended state. Walk them in order.
Surface one — the agent loop. It lives in tau agent slash loop dot py, function run underscore agent loop. The loop streams provider responses, extracts tool calls, executes them, and appends results to the transcript. The critical execution path is underscore execute underscore tool underscore calls at loop dot py line one-ninety, which calls tool dot execute for each tool the model requests. Tau's current state: the loop has no interception point. Tool calls execute as soon as the model requests them. There is no pre-execution gate, no taint check, no scope enforcement at the loop level. The loop trusts the model's tool selection. The defense is B2 — taint tracking and injection detection. But — critically — you do not patch the loop. The loop delegates to tool dot execute, which delegates to tool dot executor. The clean wedge is at the executor layer, not in the loop itself. This is the architectural lesson that recurs through every surface: enforcement belongs in the tool, not in the loop.
Surface two — tool output. It lives at underscore tool underscore result underscore message at loop dot py line two-sixty. When a tool returns an AgentToolResult, the loop converts it to a ToolResultMessage and appends it to the transcript. That message then becomes part of the context the model sees on the next turn. Tau's current state: tool output enters the transcript unfiltered. A file the agent reads can contain injected instructions — the indirect-injection vector from B2 — and those instructions enter the model's context as if they were legitimate content. There is no untrusted tagging, no secondary-model injection check, no output sanitization. This is the InjecAgent roughly fifty-percent vulnerability in its concrete form. The defense is B2 and B4 — tag tool output as untrusted, run it through an injection detector, wrap it in untrusted tags before it enters the transcript. The wedge is at the executor wrapper, because the executor returns the AgentToolResult, so it owns the content before the transcript ever sees it.
Surface three — memory and sessions. It lives in tau coding slash session dot py — eighty-eight kilobytes, the largest file in the codebase. The session persists the transcript as append-only JSONL via SessionStorage at session dot py line one-eight-seven. The append site is underscore persist underscore messages underscore since at session dot py line one-three-seven-eight. Tau's current state: sessions are durable and inspectable. Every message, including every ToolResultMessage, is written to dot-tau slash sessions as JSONL. There is no memory-write gate. Nothing validates what enters a session. A poisoned tool result persists into the JSONL. When the session resumes or branches, the poison is loaded back into context. This is the sleeper attack from B3 in its concrete form: inject via a tool result in session one, the JSONL persists it, session two loads it. The defense is B3 — harness-managed writes. The wedge is at underscore persist underscore messages underscore since — the single chokepoint where every message becomes durable — or at the executor wrapper, which sees the AgentToolResult before it is converted to a ToolResultMessage.
Surface four — the provider. It lives in tau AI and in tau coding slash provider underscore config dot py. Tau's current state: the provider layer is the model API boundary, and tau has no provider-authorization check. Any tool the agent calls, including bash, can reach the provider's API endpoint. There is no scope enforcement between the agent and the provider. The defense is B0 and B1 — scope enforcement at the tool layer. The agent's tools are gated against a scope file that defines what the agent may reach. The wedge is at the tool-list construction site, session dot py line two-eight-six.
Surface five — credentials. It lives in tau coding slash credentials dot py — the FileCredentialStore, stored at dot-tau slash credentials dot json — and in tau coding slash oauth dot py. Tau's current state: API keys are stored as plaintext JSON at dot-tau slash credentials dot json. The file is chmod zero-six-zero-zero, but world-readable by ownership. The bash tool can run cat dot-tau slash credentials dot json and the agent has the keys. It can also run printenv and leak OpenAI underscore API underscore key, Anthropic underscore API underscore key, or OpenAI underscore Codex underscore access underscore token from the environment. This is the Excessive Agency and Broken Access Control risk in its sharpest form: the agent that can read its own credentials can exfiltrate them. The defense is B5 — credential isolation. Credentials never appear on disk in plaintext. The agent process cannot reach the credential store directly — only the provider layer reads from it, through a vault-backed CredentialReader. The wedge is at create underscore model underscore provider at provider underscore runtime dot py line forty-six, the single factory that constructs the credential store.
Surface six — the sandbox. It lives in tau coding slash tools dot py, function create underscore bash underscore tool underscore definition at tools dot py line four-three-three. The exact execution site is the inner execute closure at tools dot py line four-seven-zero. Tau's current state: the bash tool runs shell commands with zero isolation. No sandbox, no network restrictions, no command allowlist, no resource caps. It has process-group kill on cancel — a reliability feature, not a security control. This is the exact "Pi has no sandbox by default" critique that surfaced on Hacker News and Reddit when Pi launched, and tau inherits it because it is Pi's educational clone. The defense is B7 — default-deny network egress, command allowlist, resource caps. The wedge is at the create underscore bash underscore tool factory at tools dot py line five-seven-four.
Surface seven — observability. It lives in tau agent slash events dot py — the discriminated event union — and in AgentHarness dot subscribe at harness dot py line one-two-four. Tau's current state: the harness emits a rich event stream. ToolExecutionStartEvent carries the ToolCall with name and arguments. ToolExecutionEndEvent carries the AgentToolResult. MessageEndEvent, TurnStartEvent, TurnEndEvent. There is a subscription API. But no listener does security analysis. No session-level intent tracking, no action provenance, no anomaly detection. The event stream is for UI rendering, not attack detection. The defense is B8 — subscribe a session-level intent tracker to the event stream. Watch ToolExecutionStartEvent for what the model is asking for, ToolExecutionEndEvent for what happened, and flag when actions drift from the session's established intent. The wedge is harness dot subscribe — purely additive, no wrapping needed.
[SLIDE 6 — B11.2 section title]
Sub-section two. The five extension points. Where each control attaches — wrapping, replacing, subscribing.
[SLIDE 7 — Tau has no plugin system — and that is a feature]
This is the map the capstone and the plugin pack use. Tau has no plugin system. There is no register underscore tool, no hook framework, no middleware. I verified this 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.
Extension Point one — tool-executor wrapping, for B2 and B4. AgentTool at tau agent slash tools dot py line sixty-one is a frozen dataclass. ToolExecutor is a Protocol — any async callable matching the signature works. A security-gated tool wraps the executor. The wrapped tool goes into CodingSessionConfig dot tools, which flows to AgentHarnessConfig at session dot py line three-ten. The harness passes it to the loop unchanged. No monkey-patching, no subclassing, no framework registration. A wrapping function and a dataclass replace. This is how the taint gate from B2 and the injection detector from B2 Layer four attach.
Extension Point two — the bash tool factory, for B7. Create underscore bash underscore tool at tau coding slash tools dot py line five-seven-four is the factory that both bash paths go through. The first path: the harness tool list, where create underscore coding underscore tools at tools dot py line ninety-six calls create underscore bash underscore tool. The second path: the terminal-command bar, where CodingSession dot run underscore terminal underscore command at session dot py line one-one-eight-three constructs its own create underscore bash underscore tool and calls it directly at session dot py line one-one-eight-seven — outside the harness tool list. This second path is the two-bash-tool finding, and we will spend a full slide on it.
Extension Point three — credential store replacement, for B5. Create underscore model underscore provider at tau coding slash provider underscore runtime dot py line forty-six is the single factory that constructs the credential store. CredentialReader is a protocol with a single method — get of name returns string or none. A vault replaces FileCredentialStore by implementing this protocol, plus the OAuth methods get underscore oauth and set underscore oauth used by the Codex resolver. The vault is injected via create underscore model underscore provider with credential underscore store equals my underscore vault. Note: a second FileCredentialStore instance is created in CodingSession dot underscore underscore init underscore underscore at session dot py lines two-five-three to two-five-five. Both must be replaced for full coverage. And note the coupling: B5 secures credentials at rest and at the provider boundary. B7 blocks the exfiltration channel — the bash denylist denies cat dot-tau slash credentials dot json and printenv. B5 and B7 are coupled.
Extension Point four — event-stream subscription, for B8. AgentHarness dot subscribe at harness dot py line one-two-four registers an EventListener. The listener receives every event the harness emits. For B8 observability, you subscribe a session intent tracker. This is purely additive — no wrapping, no replacement. The listener cannot block a tool call. Events fire after the decision in underscore execute underscore tool underscore calls, so events are for observability — B8 — not enforcement — B2 and B7. Enforcement belongs in the executor wrapper.
Extension Point five — the tool-list construction site, for B0 and B1 scope enforcement. CodingSession dot load at session dot py lines two-eight-six to two-nine-three is where the tool list is built. If CodingSessionConfig dot tools is provided, it short-circuits create underscore coding underscore tools. This is the single place to inject hardened tools. Pass a list where every tool's executor is wrapped with the scope gate, the taint gate, and, for bash, the sandbox policy. The hardened list flows to AgentHarnessConfig and persists across reloads.
[SLIDE 8 — Extension Point 1: the executor wrapper]
Let me show you the code for Extension Point one, because it is the cleanest of the five and the one the others are modeled on. AgentTool is a frozen dataclass with slots. It has name, description, input underscore schema, executor — and that executor is the wedge — plus optional prompt snippet and prompt guidelines. ToolExecutor is a Protocol: a callable that takes arguments and an optional signal and returns an Awaitable of AgentToolResult. To wrap it, you define wrap underscore with underscore gate. It takes the inner tool and a gate object. Inside, you define an async function, gated, that calls gate dot inspect on the name and arguments — the pre-execution check — then awaits the inner executor, then calls gate dot sanitize on the result — the post-execution sanitization. You return replace of inner with executor equals gated. That is it. No monkey-patching. No subclassing. No framework registration. A wrapping function and a dataclass replace. The wrapped tool flows through the same path every other tool flows through. The harness does not know it is wrapped. The loop does not know it is wrapped. This is the architectural lesson made concrete: the control attaches at the seam the framework already provides, and the seam is the executor field of a frozen dataclass.
[SLIDE 9 — B11.3 section title]
Sub-section three. The two-bash-tool finding. Why a tool-list wedge is bypassable — and the factory-level fix.
[SLIDE 10 — The two-bash-tool finding]
This is the single most important implementation detail in tau for a security engineer, and it is the kind of finding you only get by reading the code. There are two bash tools in tau, and they both go through the same factory. Path one: the harness tool list. Create underscore coding underscore tools at tools dot py line ninety-six calls create underscore bash underscore tool. The resulting tool enters CodingSessionConfig dot tools, flows to AgentHarnessConfig, and the loop executes it via tool dot executor. Path two: the terminal-command bar. CodingSession dot run underscore terminal underscore command at session dot py line one-one-eight-three constructs its own create underscore bash underscore tool — with cwd equals self dot cwd and the shell command prefix — and calls it directly at session dot py line one-one-eight-seven. It never enters CodingSessionConfig dot tools. It is never seen by a tool-list-level gate.
The consequence is direct. A B7 sandbox that only wraps tools in the harness tool list — Extension Point one or Extension Point five — will be bypassed by the terminal-command path. The sandbox policy never runs for commands issued through the bar. The clean fix is to wedge at the factory level. Provide a hardened create underscore bash underscore tool replacement that both call sites use. The wedge point is the factory itself, not the individual tool instances.
There is one more trap here. The shell underscore command underscore prefix mechanism — underscore prefixed underscore shell underscore command at tools dot py line five-eight-six — is not a viable allowlist hook. It is a blind prepend. It does f-string prefix newline command. It is evaluated as a shell preamble. It cannot see or reject the command. Do not try to overload it as a security control. It is for setup — environment variables, sourcing — not for security. The security control is the executor wrapper inside the hardened factory.
This finding generalizes beyond bash. Any control that wedges at the tool-list layer — Extension Point one or Extension Point five — 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.
[SLIDE 11 — The hardened session: all five assembled]
The plugin pack — tau-plugins — assembles all five extension points into a single entry point: create underscore hardened underscore session of cwd. That session has every tool wrapped with the taint gate from B2. Bash wrapped with the sandbox policy from B7, at the factory level so both bash paths are covered. Credentials read from the vault, not plaintext, from B5. The intent tracker subscribed to the event stream, from B8. And the scope gate on every outbound action, from B0 and B1.
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 harness measures the before and after. Run the InjecAgent-style injection battery against unmodified tau, then against the hardened session. The delta is the defense scorecard — the same artifact Capstone B1 ships.
[SLIDE 12 — Anti-patterns this map prevents]
Five anti-patterns, each with a cure, and each is a mistake a senior engineer can make if they reach for the wrong abstraction. First, patching the loop instead of wrapping the executor. Trying to add a pre-execution gate by modifying run underscore agent underscore loop or underscore execute underscore tool underscore calls. Cure: the loop delegates to tool dot execute, which delegates to tool dot executor. Wrap the executor. The loop stays untouched. Second, wrapping only the harness bash tool. Adding a sandbox to the bash tool in the harness list but missing run underscore terminal underscore command, which constructs its own bash tool outside the list. Cure: wedge at the factory level. Third, overloading shell underscore command underscore prefix as an allowlist. It is a blind prepend. It cannot see or reject the command. Cure: wrap the executor. The prefix is for setup, not security. Fourth, trying to register a plugin. Looking for a register underscore tool or plugin-discovery hook. Tau has none. Cure: construct a custom CodingSessionConfig with wrapped tools and a vault-backed provider, programmatically. The entry point is CodingSession dot load. Fifth, using the event listener for enforcement. Subscribing a listener that tries to block a tool call. The listener fires after the decision. It is read-only. Cure: events are for observability — B8. Enforcement belongs in the executor wrapper — Extension Point one.
[SLIDE 13 — Lab and what's next]
The lab has you clone tau, run an injection battery against the unmodified codebase using planted injection content, identify each surface's extension point, and map every finding to a B-module control. Python, type hints, no GPU required. You will plant injection content in files the agent reads, in tool results that enter the transcript, and in sessions that persist. You will observe which defenses are absent and which extension point closes each gap. This lab is the prerequisite for Capstone B1 — harden tau, where you install the plugins from this map — and Capstone B2 — red-team a tau deployment, where you attack a partially-hardened instance. The delta between the before and the after is the scorecard. Tau is the reference harness. This deep-dive is the map. Let's build it.
# Teaching Script — SDD-B11: Tau: The Reference Harness to Attack and Harden **Course**: Course 2B — Securing & Attacking Harnesses and LLMs **Module**: SDD-B11 — Tau: The Reference Harness to Attack and Harden **Duration**: ~45 minutes (spoken at ~140 wpm) **Format**: Verbatim transcript with `[SLIDE N]` cues. Read aloud or use as speaker notes. --- [SLIDE 1 — Title] Welcome to SDD-B11. Tau: The Reference Harness to Attack and Harden. For eight modules — B1 through B8 — we have been building controls in the abstract. The threat model, the injection-defense layers, the credential isolation, the sandbox, the observability. Every one of those controls is a missing feature in a real piece of software. This deep-dive is where we name that software, walk its surfaces, and map every control to the exact line of code where it attaches. Tau is the target. Every defense in B2 through B8 is a missing feature in tau today, and every one of them attaches at a clean, explicit extension point in roughly three thousand lines of readable Python. This deep-dive is the map. [SLIDE 2 — What tau is — and why it is the target] Tau is Hugging Face's educational reimplementation of Pi. Built by Alejandro AO under the Hugging Face organization, it is described as a small, readable terminal coding agent and a working example of how coding agents are built. The homepage is twotimespi dot dev. The code is at github dot com slash huggingface slash tau. The relationship matters. Pi, which Course 1 deep-dived in DD-01, is the minimal baseline — a four-tool, roughly twelve-hundred-line reference thin harness. Tau is Pi's educational clone. Same architecture, same philosophy, but with a three-layer package split that makes the extension points cleaner to teach. The three-layer split. Tau AI is the provider and model streaming layer — provider-neutral, supporting OpenAI, Anthropic, Codex subscription auth, OpenRouter, Hugging Face, and local models. Tau agent is the reusable brain — the harness, the loop, the tools, the events, the session abstractions. Tau coding is the coding app — the CLI and Textual TUI, the read, write, edit, and bash tools, the skills, and the on-disk sessions. The load-bearing boundary is this: AgentHarness is the reusable brain. CodingSession is the coding-agent environment. The TUI is one possible frontend. The core — tau agent — 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 either in tau agent, where enforcement belongs, or in tau coding, where the attack surface lives. [SLIDE 3 — Three properties that make tau ideal] Why is tau the ideal teaching target for Course 2B? Three properties. First, it is real. Not a stub, not a simulation. A bash tool that actually runs asyncio dot create underscore subprocess shell. A read tool that actually reads files. A credential store that actually holds plaintext API keys on disk at dot-tau slash credentials dot json. When you attack tau, you are attacking a real system. Second, it is small. Roughly three thousand lines across three packages. A student can read the entire codebase in an afternoon and understand every line that matters for security. Third — and this is the decisive one — it is undefended. Tau has none of the controls that B2 through B8 build. This is the "before" picture: the raw, injectable, exfiltratable harness that the course teaches you to harden. 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." [SLIDE 4 — B11.1 section title] Sub-section one. The before picture: seven surfaces, zero defenses. Tau's attack surface mapped to B-modules. [SLIDE 5 — Seven surfaces, all undefended today] This is the core of the deep-dive. Each of tau's seven surfaces, the OWASP ASI risk it exposes, the module that covers it, and tau's current undefended state. Walk them in order. Surface one — the agent loop. It lives in tau agent slash loop dot py, function run underscore agent loop. The loop streams provider responses, extracts tool calls, executes them, and appends results to the transcript. The critical execution path is underscore execute underscore tool underscore calls at loop dot py line one-ninety, which calls tool dot execute for each tool the model requests. Tau's current state: the loop has no interception point. Tool calls execute as soon as the model requests them. There is no pre-execution gate, no taint check, no scope enforcement at the loop level. The loop trusts the model's tool selection. The defense is B2 — taint tracking and injection detection. But — critically — you do not patch the loop. The loop delegates to tool dot execute, which delegates to tool dot executor. The clean wedge is at the executor layer, not in the loop itself. This is the architectural lesson that recurs through every surface: enforcement belongs in the tool, not in the loop. Surface two — tool output. It lives at underscore tool underscore result underscore message at loop dot py line two-sixty. When a tool returns an AgentToolResult, the loop converts it to a ToolResultMessage and appends it to the transcript. That message then becomes part of the context the model sees on the next turn. Tau's current state: tool output enters the transcript unfiltered. A file the agent reads can contain injected instructions — the indirect-injection vector from B2 — and those instructions enter the model's context as if they were legitimate content. There is no untrusted tagging, no secondary-model injection check, no output sanitization. This is the InjecAgent roughly fifty-percent vulnerability in its concrete form. The defense is B2 and B4 — tag tool output as untrusted, run it through an injection detector, wrap it in untrusted tags before it enters the transcript. The wedge is at the executor wrapper, because the executor returns the AgentToolResult, so it owns the content before the transcript ever sees it. Surface three — memory and sessions. It lives in tau coding slash session dot py — eighty-eight kilobytes, the largest file in the codebase. The session persists the transcript as append-only JSONL via SessionStorage at session dot py line one-eight-seven. The append site is underscore persist underscore messages underscore since at session dot py line one-three-seven-eight. Tau's current state: sessions are durable and inspectable. Every message, including every ToolResultMessage, is written to dot-tau slash sessions as JSONL. There is no memory-write gate. Nothing validates what enters a session. A poisoned tool result persists into the JSONL. When the session resumes or branches, the poison is loaded back into context. This is the sleeper attack from B3 in its concrete form: inject via a tool result in session one, the JSONL persists it, session two loads it. The defense is B3 — harness-managed writes. The wedge is at underscore persist underscore messages underscore since — the single chokepoint where every message becomes durable — or at the executor wrapper, which sees the AgentToolResult before it is converted to a ToolResultMessage. Surface four — the provider. It lives in tau AI and in tau coding slash provider underscore config dot py. Tau's current state: the provider layer is the model API boundary, and tau has no provider-authorization check. Any tool the agent calls, including bash, can reach the provider's API endpoint. There is no scope enforcement between the agent and the provider. The defense is B0 and B1 — scope enforcement at the tool layer. The agent's tools are gated against a scope file that defines what the agent may reach. The wedge is at the tool-list construction site, session dot py line two-eight-six. Surface five — credentials. It lives in tau coding slash credentials dot py — the FileCredentialStore, stored at dot-tau slash credentials dot json — and in tau coding slash oauth dot py. Tau's current state: API keys are stored as plaintext JSON at dot-tau slash credentials dot json. The file is chmod zero-six-zero-zero, but world-readable by ownership. The bash tool can run cat dot-tau slash credentials dot json and the agent has the keys. It can also run printenv and leak OpenAI underscore API underscore key, Anthropic underscore API underscore key, or OpenAI underscore Codex underscore access underscore token from the environment. This is the Excessive Agency and Broken Access Control risk in its sharpest form: the agent that can read its own credentials can exfiltrate them. The defense is B5 — credential isolation. Credentials never appear on disk in plaintext. The agent process cannot reach the credential store directly — only the provider layer reads from it, through a vault-backed CredentialReader. The wedge is at create underscore model underscore provider at provider underscore runtime dot py line forty-six, the single factory that constructs the credential store. Surface six — the sandbox. It lives in tau coding slash tools dot py, function create underscore bash underscore tool underscore definition at tools dot py line four-three-three. The exact execution site is the inner execute closure at tools dot py line four-seven-zero. Tau's current state: the bash tool runs shell commands with zero isolation. No sandbox, no network restrictions, no command allowlist, no resource caps. It has process-group kill on cancel — a reliability feature, not a security control. This is the exact "Pi has no sandbox by default" critique that surfaced on Hacker News and Reddit when Pi launched, and tau inherits it because it is Pi's educational clone. The defense is B7 — default-deny network egress, command allowlist, resource caps. The wedge is at the create underscore bash underscore tool factory at tools dot py line five-seven-four. Surface seven — observability. It lives in tau agent slash events dot py — the discriminated event union — and in AgentHarness dot subscribe at harness dot py line one-two-four. Tau's current state: the harness emits a rich event stream. ToolExecutionStartEvent carries the ToolCall with name and arguments. ToolExecutionEndEvent carries the AgentToolResult. MessageEndEvent, TurnStartEvent, TurnEndEvent. There is a subscription API. But no listener does security analysis. No session-level intent tracking, no action provenance, no anomaly detection. The event stream is for UI rendering, not attack detection. The defense is B8 — subscribe a session-level intent tracker to the event stream. Watch ToolExecutionStartEvent for what the model is asking for, ToolExecutionEndEvent for what happened, and flag when actions drift from the session's established intent. The wedge is harness dot subscribe — purely additive, no wrapping needed. [SLIDE 6 — B11.2 section title] Sub-section two. The five extension points. Where each control attaches — wrapping, replacing, subscribing. [SLIDE 7 — Tau has no plugin system — and that is a feature] This is the map the capstone and the plugin pack use. Tau has no plugin system. There is no register underscore tool, no hook framework, no middleware. I verified this 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. Extension Point one — tool-executor wrapping, for B2 and B4. AgentTool at tau agent slash tools dot py line sixty-one is a frozen dataclass. ToolExecutor is a Protocol — any async callable matching the signature works. A security-gated tool wraps the executor. The wrapped tool goes into CodingSessionConfig dot tools, which flows to AgentHarnessConfig at session dot py line three-ten. The harness passes it to the loop unchanged. No monkey-patching, no subclassing, no framework registration. A wrapping function and a dataclass replace. This is how the taint gate from B2 and the injection detector from B2 Layer four attach. Extension Point two — the bash tool factory, for B7. Create underscore bash underscore tool at tau coding slash tools dot py line five-seven-four is the factory that both bash paths go through. The first path: the harness tool list, where create underscore coding underscore tools at tools dot py line ninety-six calls create underscore bash underscore tool. The second path: the terminal-command bar, where CodingSession dot run underscore terminal underscore command at session dot py line one-one-eight-three constructs its own create underscore bash underscore tool and calls it directly at session dot py line one-one-eight-seven — outside the harness tool list. This second path is the two-bash-tool finding, and we will spend a full slide on it. Extension Point three — credential store replacement, for B5. Create underscore model underscore provider at tau coding slash provider underscore runtime dot py line forty-six is the single factory that constructs the credential store. CredentialReader is a protocol with a single method — get of name returns string or none. A vault replaces FileCredentialStore by implementing this protocol, plus the OAuth methods get underscore oauth and set underscore oauth used by the Codex resolver. The vault is injected via create underscore model underscore provider with credential underscore store equals my underscore vault. Note: a second FileCredentialStore instance is created in CodingSession dot underscore underscore init underscore underscore at session dot py lines two-five-three to two-five-five. Both must be replaced for full coverage. And note the coupling: B5 secures credentials at rest and at the provider boundary. B7 blocks the exfiltration channel — the bash denylist denies cat dot-tau slash credentials dot json and printenv. B5 and B7 are coupled. Extension Point four — event-stream subscription, for B8. AgentHarness dot subscribe at harness dot py line one-two-four registers an EventListener. The listener receives every event the harness emits. For B8 observability, you subscribe a session intent tracker. This is purely additive — no wrapping, no replacement. The listener cannot block a tool call. Events fire after the decision in underscore execute underscore tool underscore calls, so events are for observability — B8 — not enforcement — B2 and B7. Enforcement belongs in the executor wrapper. Extension Point five — the tool-list construction site, for B0 and B1 scope enforcement. CodingSession dot load at session dot py lines two-eight-six to two-nine-three is where the tool list is built. If CodingSessionConfig dot tools is provided, it short-circuits create underscore coding underscore tools. This is the single place to inject hardened tools. Pass a list where every tool's executor is wrapped with the scope gate, the taint gate, and, for bash, the sandbox policy. The hardened list flows to AgentHarnessConfig and persists across reloads. [SLIDE 8 — Extension Point 1: the executor wrapper] Let me show you the code for Extension Point one, because it is the cleanest of the five and the one the others are modeled on. AgentTool is a frozen dataclass with slots. It has name, description, input underscore schema, executor — and that executor is the wedge — plus optional prompt snippet and prompt guidelines. ToolExecutor is a Protocol: a callable that takes arguments and an optional signal and returns an Awaitable of AgentToolResult. To wrap it, you define wrap underscore with underscore gate. It takes the inner tool and a gate object. Inside, you define an async function, gated, that calls gate dot inspect on the name and arguments — the pre-execution check — then awaits the inner executor, then calls gate dot sanitize on the result — the post-execution sanitization. You return replace of inner with executor equals gated. That is it. No monkey-patching. No subclassing. No framework registration. A wrapping function and a dataclass replace. The wrapped tool flows through the same path every other tool flows through. The harness does not know it is wrapped. The loop does not know it is wrapped. This is the architectural lesson made concrete: the control attaches at the seam the framework already provides, and the seam is the executor field of a frozen dataclass. [SLIDE 9 — B11.3 section title] Sub-section three. The two-bash-tool finding. Why a tool-list wedge is bypassable — and the factory-level fix. [SLIDE 10 — The two-bash-tool finding] This is the single most important implementation detail in tau for a security engineer, and it is the kind of finding you only get by reading the code. There are two bash tools in tau, and they both go through the same factory. Path one: the harness tool list. Create underscore coding underscore tools at tools dot py line ninety-six calls create underscore bash underscore tool. The resulting tool enters CodingSessionConfig dot tools, flows to AgentHarnessConfig, and the loop executes it via tool dot executor. Path two: the terminal-command bar. CodingSession dot run underscore terminal underscore command at session dot py line one-one-eight-three constructs its own create underscore bash underscore tool — with cwd equals self dot cwd and the shell command prefix — and calls it directly at session dot py line one-one-eight-seven. It never enters CodingSessionConfig dot tools. It is never seen by a tool-list-level gate. The consequence is direct. A B7 sandbox that only wraps tools in the harness tool list — Extension Point one or Extension Point five — will be bypassed by the terminal-command path. The sandbox policy never runs for commands issued through the bar. The clean fix is to wedge at the factory level. Provide a hardened create underscore bash underscore tool replacement that both call sites use. The wedge point is the factory itself, not the individual tool instances. There is one more trap here. The shell underscore command underscore prefix mechanism — underscore prefixed underscore shell underscore command at tools dot py line five-eight-six — is not a viable allowlist hook. It is a blind prepend. It does f-string prefix newline command. It is evaluated as a shell preamble. It cannot see or reject the command. Do not try to overload it as a security control. It is for setup — environment variables, sourcing — not for security. The security control is the executor wrapper inside the hardened factory. This finding generalizes beyond bash. Any control that wedges at the tool-list layer — Extension Point one or Extension Point five — 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. [SLIDE 11 — The hardened session: all five assembled] The plugin pack — tau-plugins — assembles all five extension points into a single entry point: create underscore hardened underscore session of cwd. That session has every tool wrapped with the taint gate from B2. Bash wrapped with the sandbox policy from B7, at the factory level so both bash paths are covered. Credentials read from the vault, not plaintext, from B5. The intent tracker subscribed to the event stream, from B8. And the scope gate on every outbound action, from B0 and B1. 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 harness measures the before and after. Run the InjecAgent-style injection battery against unmodified tau, then against the hardened session. The delta is the defense scorecard — the same artifact Capstone B1 ships. [SLIDE 12 — Anti-patterns this map prevents] Five anti-patterns, each with a cure, and each is a mistake a senior engineer can make if they reach for the wrong abstraction. First, patching the loop instead of wrapping the executor. Trying to add a pre-execution gate by modifying run underscore agent underscore loop or underscore execute underscore tool underscore calls. Cure: the loop delegates to tool dot execute, which delegates to tool dot executor. Wrap the executor. The loop stays untouched. Second, wrapping only the harness bash tool. Adding a sandbox to the bash tool in the harness list but missing run underscore terminal underscore command, which constructs its own bash tool outside the list. Cure: wedge at the factory level. Third, overloading shell underscore command underscore prefix as an allowlist. It is a blind prepend. It cannot see or reject the command. Cure: wrap the executor. The prefix is for setup, not security. Fourth, trying to register a plugin. Looking for a register underscore tool or plugin-discovery hook. Tau has none. Cure: construct a custom CodingSessionConfig with wrapped tools and a vault-backed provider, programmatically. The entry point is CodingSession dot load. Fifth, using the event listener for enforcement. Subscribing a listener that tries to block a tool call. The listener fires after the decision. It is read-only. Cure: events are for observability — B8. Enforcement belongs in the executor wrapper — Extension Point one. [SLIDE 13 — Lab and what's next] The lab has you clone tau, run an injection battery against the unmodified codebase using planted injection content, identify each surface's extension point, and map every finding to a B-module control. Python, type hints, no GPU required. You will plant injection content in files the agent reads, in tool results that enter the transcript, and in sessions that persist. You will observe which defenses are absent and which extension point closes each gap. This lab is the prerequisite for Capstone B1 — harden tau, where you install the plugins from this map — and Capstone B2 — red-team a tau deployment, where you attack a partially-hardened instance. The delta between the before and the after is the scorecard. Tau is the reference harness. This deep-dive is the map. Let's build it.