The Defence-in-Depth Harness

a Rust agent where the OS boundary is the architecture

LANGUAGE
Rust · Tokio
SOURCE LOC
~1.08M (1.39M with tests)
CRATES
134 Cargo members
BUILT-IN TOOLS
~32 + hosted
PROVIDERS
5 — Responses API only
OS SANDBOX
Seatbelt / bwrap+seccomp / Win
All 31 blocks

Surfaces & entry

The turn

Tools

Context & memory

Model layer

Safety

Extensibility

State

Isometric plate: the 31 subsystems of Codex CLIBlocks are grouped into districts by hatching and tint; block height is a rough proxy for code mass. Lines trace the data flow of a single turn. The block index beside this diagram carries the same information as text.ABDCEJFKMGNHLIUOVXPQYWRZSTAAABACADAE

Hover to read · drag to pan · ⌘/ctrl + scroll to zoom

How to read this plate

Each block is one subsystem of Codex CLI. Its height is a rough proxy for code mass, calibrated the same way across every harness so the plates are comparable. Hatching and tint group blocks into districts — surfaces, the turn, tools, context, the model layer, safety, extensibility and state.

Lines trace the real data flow of a single turn plus the major supporting links. Pick any block for a plain description and the files it was read from, or trace the turn to walk the path a request actually takes.

What this is

Codex is the only harness here where the OS sandbox is load-bearing. Four independent gates sit on every exec: Starlark-defined execpolicy prefix rules, an LLM "guardian" auto-approver that fails closed, the platform sandbox itself, and a full MITM HTTPS proxy with per-host allow/deny/ask decisions. Writable roots carry read-only subpaths so .git/hooks and .codex stay immutable inside a directory you can otherwise write.

It has also made the sharpest protocol bet: Responses API only. Chat Completions was removed — the wire-API enum has one variant and the string "chat" deserializes to a hard error. Even Ollama and LM Studio are wired as Responses providers, and a Responses-over-WebSocket transport is reused across a turn's requests.

The system prompt is not a repo constant. It arrives from a remote model catalog as a per-model instructions template, alongside context window, auto-compact limits, truncation policy, which edit tool to use and which shell tool to use — so prompt text, tool selection and compaction thresholds are all server-tunable without shipping a binary.

Context is handled as a diff. Roughly fifteen independently-versioned "world states" — AGENTS.md, environment, permissions, tools, model, personality, plugins, collaboration mode — are recomputed each turn and only the changed fragments are injected, so switching sandbox mode mid-thread costs one small fragment rather than a re-prefix.

Implementation

Submission queue. The whole agent is driven by an OpEventMsg protocol (~95 event variants) over an async channel. TurnInput carries a oneshot reply so the app-server can preserve caller ordering; Interrupt, Compact, Review, ThreadRollback, InterAgentCommunication and approval responses all ride the same queue.

Shell. unified_exec is a persistent PTY session manager. A command that doesn't finish inside its yield window returns a session id; write_stdin continues it, and an empty write polls. On a sandbox denial the escalation server re-runs the same command with elevated permissions for that one call, after an approval round trip.

Tool exposure tiers. Every registry entry carries Direct | Deferred | CodeModeOnly | DirectModelOnly | Hidden. Large MCP servers register as deferred and are reachable only through a tool_search meta-tool, which is how hundreds of tools stay out of the prompt. The extreme is code mode: the model emits JavaScript executed in an embedded V8 isolate where tools are ordinary functions.

Persistence. Rollouts are JSONL under $CODEX_HOME/sessions/YYYY/MM/DD/, transparently zstd-compressed by a background worker, with a tail-first scanner for fast resume. Alongside them sit two SQLite databases — a state/audit DB and a paginated thread-history projection — with a migration path from legacy JSONL.