This page provides a high-level architectural overview of the Codex system, organized into five major subsystems:
codex-core)ModelClient that communicates with OpenAI and other providersThe architecture follows a queue-based submission/event protocol (Op/EventMsg) where user operations are submitted asynchronously and results stream back as events. This decouples request initiation from execution and enables interruptible workflows.
For installation procedures, see Installation and Setup. For crate-level organization, see Repository Structure. For protocol message details, see Protocol Layer (Submission/Event System).
The following diagram shows the major subsystems and their relationships. User interfaces communicate with the core agent via a queue-based protocol, the agent coordinates model calls and tool execution, and all state is persisted to JSONL rollout files.
Diagram: Codex Ecosystem Overview
Sources: codex-rs/cli/src/main.rs1-100 codex-rs/core/src/codex.rs274-520 codex-rs/core/src/client.rs175-285 codex-rs/core/src/tools/spec.rs1-200 codex-rs/core/src/unified_exec.rs1-150
The Codex struct (codex.rs274) exposes a queue-based interface:
Key Protocol Types:
Submission (protocol.rs74): Wraps Op with unique IDOp (protocol.rs124): Operations enum (UserTurn, Interrupt, Shutdown, etc.)Event (protocol.rs664): Wraps EventMsg with submission IDEventMsg (protocol.rs683): Events enum (TurnStarted, AgentMessageDelta, ExecCommandBegin, etc.)The Codex::submit() method (codex.rs470-475) generates a unique ID and sends the submission. The submission_loop (codex.rs2680-2870) runs as a Tokio task and dispatches operations to the appropriate handler (e.g., handle_user_turn, handle_interrupt).
Sources: codex-rs/core/src/codex.rs274-520 codex-rs/core/src/codex.rs470-475 codex-rs/core/src/codex.rs2680-2870 codex-rs/protocol/src/protocol.rs74-750
The following diagram shows how user input flows from UI layers through the core agent to model APIs, and how events stream back.
Diagram: Core Agent Event Loop
Sources: codex-rs/tui/src/chatwidget.rs1-300 codex-rs/core/src/codex.rs274-520 codex-rs/core/src/codex.rs1500-1800 codex-rs/core/src/client.rs175-600 codex-rs/core/src/tools/mod.rs1-200
The UI layer provides three distinct user-facing modes, each optimized for different use cases. All modes communicate with codex-core using the same Codex queue interface.
| Mode | Entry Point | Primary Use Case | Event Processing |
|---|---|---|---|
| TUI | codex-tui binary, App struct | Interactive terminal sessions | ChatWidget maintains UI state, renders cells |
| Exec | codex exec subcommand | CI/CD pipelines, scripts | EventProcessorWithHumanOutput or EventProcessorWithJsonOutput |
| App Server | codex-app-server binary | IDE extensions (VS Code, Cursor) | CodexMessageProcessor translates to JSON-RPC notifications |
The TUI mode uses App (app.rs518) as the top-level coordinator. It manages multiple ChatWidget instances (one per thread) and handles thread switching, config persistence, and external editor integration.
Diagram: TUI Component Hierarchy
Key TUI Components:
App (app.rs518-633): Multiplexes TuiEvent (key presses, ticks) and AppEvent (file search, rate limits); manages ThreadEventChannel buffering for inactive threadsChatWidget (chatwidget.rs515-665): Handles EventMsg variants, builds HistoryCell instances, manages streaming state (active_cell, stream_controller)BottomPane (bottom_pane/mod.rs145-360): Routes keys to active view (popup) or composer; manages view stack (approval overlays, selection lists)ChatComposer (chat_composer.rs200-500): TextArea editing, paste burst detection, slash command popup, file search popup, skill/app mention popupSources: codex-rs/tui/src/app.rs518-633 codex-rs/tui/src/chatwidget.rs515-665 codex-rs/tui/src/bottom_pane/mod.rs145-360 codex-rs/tui/src/bottom_pane/chat_composer.rs200-500
Exec mode is headless and synchronous: it submits a turn, processes events linearly, and exits with a status code. Output format is controlled by the --json flag.
Diagram: Exec Mode Event Processing
Key Exec Components:
EventProcessor trait (event_processor.rs20-60): Defines handle_event(), take_status(), final_output_schema() interfaceEventProcessorWithHumanOutput (event_processor_with_human_output.rs50-800): Renders ANSI-colored text, displays streaming agent messages, exec command summaries, errorsEventProcessorWithJsonOutput (event_processor_with_jsonl_output.rs30-200): Emits one JSON object per line for machine consumptionExec mode auto-cancels elicitations (approval requests) when not running interactively (event_processor_with_human_output.rs400-450).
Sources: codex-rs/exec/src/lib.rs100-300 codex-rs/exec/src/event_processor.rs20-60 codex-rs/exec/src/event_processor_with_human_output.rs50-800 codex-rs/exec/src/event_processor_with_jsonl_output.rs30-200
The app-server exposes a JSON-RPC 2.0 API over stdio (default) or WebSocket. IDE extensions send ClientRequest messages and receive ServerNotification messages. The CodexMessageProcessor translates between protocol v2 and core events via bespoke_event_handling.
Diagram: App Server Protocol Translation
Key App Server Components:
CodexMessageProcessor (codex_message_processor.rs200-3500): Dispatches ClientRequest to handlers (thread/start, turn/start, config/read, etc.)bespoke_event_handling (bespoke_event_handling.rs50-2000): Translates core EventMsg to protocol v2 notifications (item/started, item/delta, turn/completed)ThreadWatchManager (thread_status.rs100-300): Tracks per-thread status (idle, busy, waiting_for_input)OutgoingMessageSender (outgoing_message.rs100-400): Multiplexes responses and notifications over transportThe app-server supports two protocol versions (v1 and v2); v2 is the current default and uses camelCase naming conventions (app-server-protocol/src/protocol/v2.rs1-2000).
Sources: codex-rs/app-server/src/codex_message_processor.rs200-3500 codex-rs/app-server/src/bespoke_event_handling.rs50-2000 codex-rs/app-server/src/thread_status.rs100-300 codex-rs/app-server/src/outgoing_message.rs100-400 codex-rs/app-server-protocol/src/protocol/v2.rs1-2000
The core layer is implemented in codex-core and provides the shared business logic for all frontends. The entry point is the Codex struct, which owns a Session and exposes the submission/event interface.
The Codex struct (codex.rs274) provides the submission/event queue interface:
pub struct Codex {
pub(crate) tx_sub: Sender<Submission>,
pub(crate) rx_event: Receiver<Event>,
pub(crate) agent_status: watch::Receiver<AgentStatus>,
pub(crate) session: Arc<Session>,
}
Public Methods:
Codex::spawn() (codex.rs300-467): Creates new session, spawns submission_loop Tokio task, returns CodexSpawnOk with thread IDsubmit(op: Op) (codex.rs470-475): Generates UUID, wraps in Submission, sends to tx_subnext_event() (codex.rs487-493): Blocks on rx_event.recv() until next eventsteer_input(items, expected_turn_id) (codex.rs496-502): Injects user input during an active turn (used for agent steering)The submission_loop (codex.rs2680-2870) runs as a Tokio task and dispatches operations:
Op::UserTurn → handle_user_turn() → spawns RegularTaskOp::Interrupt → cancels active taskOp::Shutdown → cleans up and exits loopSources: codex-rs/core/src/codex.rs274-285 codex-rs/core/src/codex.rs300-467 codex-rs/core/src/codex.rs470-502 codex-rs/core/src/codex.rs2680-2870
The Session struct (codex.rs525-539) manages conversation state and coordinates subsystems. It is created once per thread and is immutable for the thread's lifetime.
Diagram: Session Initialization and State
SessionState (state.rs50-200) contains mutable per-turn state:
context_manager: ContextManager: Conversation history, token usage tracking (context_manager.rs1-500)session_configuration: SessionConfiguration: CWD, approval/sandbox policies, model (codex.rs708-800)rollout_recorder: RolloutRecorder: JSONL persistence (rollout/mod.rs1-300)pending_elicitations: HashMap<...>: Tracks approval requests awaiting user responseSessionServices (state.rs100-250) holds shared services:
auth_manager: Arc<AuthManager>: Handles authentication (ChatGPT, API key)models_manager: Arc<ModelsManager>: Model metadata, version resolutionexec_policy: Arc<ExecPolicyManager>: Per-directory execution rulesskills_manager: Arc<SkillsManager>: Loads and tracks project skillsfile_watcher: Arc<FileWatcher>: Watches for SKILL.md changesSources: codex-rs/core/src/codex.rs300-467 codex-rs/core/src/codex.rs525-539 codex-rs/core/src/codex.rs967-1200 codex-rs/core/src/state.rs50-250 codex-rs/core/src/context_manager.rs1-500
When a UserTurn operation is submitted, the session creates a RegularTask and executes it via SessionTask::execute().
Diagram: Turn Execution Flow
Key Turn Structures:
TurnContext (codex.rs543-705): Immutable turn config (model info, CWD, approval/sandbox policies, ToolsConfig, features, JsReplHandle)SessionTask trait (tasks/mod.rs20-50): Defines execute() for different task typesRegularTask (tasks/regular.rs50-800): Standard user turn execution (prompt → stream → tools → completion)ReviewTask (tasks/review.rs1-500): Specialized task for code review with review-specific promptsSources: codex-rs/core/src/codex.rs1500-1800 codex-rs/core/src/codex.rs543-705 codex-rs/core/src/tasks/mod.rs20-50 codex-rs/core/src/tasks/regular.rs50-800
The model client uses a two-level abstraction: session-scoped ModelClient and turn-scoped ModelClientSession. This design enables WebSocket connection reuse across requests within a turn while keeping session configuration isolated.
Diagram: Model Client Architecture
ModelClient (client.rs175-284):
ModelClient::new() (client.rs228-254)ModelProviderInfo), auth manager, conversation ID, WebSocket version preferenceModelClientSession via new_session() (client.rs260-268)ModelClientSession (client.rs192-210):
websocket_last_request for incremental response.append optimizationx-codex-turn-state token from server (used for sticky routing across retries)Transport Selection (client.rs674-850):
responses_websocket_version is set (v1 or v2)disable_websockets flagStreaming Flow (client.rs400-600):
stream(prompt, model_info, effort, otel) on ModelClientSessionPrompt (messages, tools, output schema, reasoning config)ResponseEvent items (messages, tool calls, reasoning deltas, completion)Sources: codex-rs/core/src/client.rs138-850 codex-rs/core/src/client_common.rs1-200 codex-rs/core/src/model_provider_info.rs1-150
Tools are registered per turn based on model capabilities and enabled features. The ToolOrchestrator enforces approval and sandbox policies before routing tool calls to runtime-specific handlers.
Diagram: Tool Execution Pipeline
Tool Registration (tools/spec.rs200-500):
ToolsConfig::new() filters tool registry based on Features and ModelInfoToolSpec for each enabled tool (function-call or freeform format)PromptTool Approval (tools/orchestrator.rs50-400):
ToolOrchestrator evaluates ExecApprovalRequirement per tool callapproval_policy (Never, OnRequest, UnlessTrusted, OnFailure)ExecApprovalRequestEvent if approval needed; waits for Op::ExecApprovalSandboxing (tools/sandboxing/mod.rs1-300):
SandboxPolicy controls read/write/network accessUnified Exec (unified_exec.rs50-800):
exec_command spawns new process, write_stdin sends input, read_output streams stdout/stderrSources: codex-rs/core/src/tools/spec.rs200-500 codex-rs/core/src/tools/orchestrator.rs50-400 codex-rs/core/src/tools/sandboxing/mod.rs1-300 codex-rs/core/src/unified_exec.rs50-800
Configuration flows through multiple layers (defaults, user config, project config, CLI overrides) and is resolved once per session. Session state is persisted to JSONL rollout files.
Diagram: Configuration Flow
Configuration Resolution (config/mod.rs50-500):
~/.codex/config.toml (if exists).codex/config.toml files (project config)--profile)--set, --feature)Config Structure (config/mod.rs50-300):
model: Option<String>: Model slug (e.g., "gpt-4o")model_reasoning_effort: Option<ReasoningEffortConfig>: Reasoning effort (low/medium/high)permissions: Permissions: Approval/sandbox policiesfeatures: Features: Immutable feature togglesmcp_servers: HashMap<String, McpServerConfig>: MCP server configurationsSessionConfiguration (codex.rs708-800):
Rollout Persistence (rollout/mod.rs1-300):
RolloutLine per lineRolloutLine::Turn: Complete turn with all eventsRolloutLine::SessionMeta: Thread name, updated timestampInitialHistory::ResumedSources: codex-rs/core/src/config/mod.rs50-500 codex-rs/core/src/config/default.rs1-200 codex-rs/core/src/config_loader.rs100-800 codex-rs/core/src/codex.rs708-800 codex-rs/core/src/state.rs50-250 codex-rs/core/src/rollout/mod.rs1-300
The ThreadManager (thread_manager.rs1-500) coordinates multiple conversations. Each thread is a CodexThread (codex_thread.rs1-300) that owns a Codex instance and tracks metadata.
Diagram: ThreadManager Architecture
ThreadManager Key Methods:
create_thread() (thread_manager.rs200-300): Spawns new Codex with InitialHistory::Newresume_thread(thread_id) (thread_manager.rs350-450): Loads JSONL rollout, creates Codex with InitialHistory::Resumedfork_thread(source_id, fork_from_turn_id) (thread_manager.rs500-600): Copies rollout to new file, creates Codex with InitialHistory::Forkedlist_threads(cursor, page_size, sort_key) (thread_manager.rs700-850): Reads session index, returns paginated listThreadEventStore (in TUI App): When a thread is inactive, events are buffered in a bounded queue so switching threads can replay recent events without re-reading the rollout file.
Sources: codex-rs/core/src/thread_manager.rs50-850 codex-rs/core/src/codex_thread.rs50-300 codex-rs/tui/src/app.rs245-320
The following sequence shows a typical user turn with tool execution and approval.
Diagram: Typical Turn Data Flow
Sources: codex-rs/tui/src/chatwidget.rs1-300 codex-rs/core/src/codex.rs274-520 codex-rs/core/src/codex.rs2680-2870 codex-rs/core/src/client.rs400-600 codex-rs/core/src/tools/mod.rs150-350 codex-rs/core/src/unified_exec.rs200-500
Sources: All files in codex-rs/core/src/ codex-rs/tui/src/ codex-rs/exec/src/ codex-rs/app-server/src/
Refresh this wiki