Skip to content

Latest commit

 

History

History
424 lines (319 loc) · 13.3 KB

File metadata and controls

424 lines (319 loc) · 13.3 KB

OpenAI Codex CLI Source Code Analysis

Based on https://github.com/openai/codex latest source, analyzed 2026-03 Goal: Help command center operators better orchestrate Codex Sessions


1. Overall Architecture

Codex CLI is a Rust Cargo Workspace with 80+ crates. Core architecture:

codex (binary entry)
├── cli/          # CLI entry (MultitoolCli), routes to subcommands
├── tui/          # Interactive fullscreen TUI (Ratatui-based)
├── exec/         # Non-interactive headless execution engine
├── core/         # Core business logic (Config, Session, tool execution, sandbox)
├── mcp-server/   # Exposes Codex as MCP Server (stdio)
├── protocol/     # Protocol type definitions (Event, Op, SandboxPolicy, etc.)
├── sandboxing/   # Cross-platform sandbox manager (macOS/Linux/Windows dispatch)
├── linux-sandbox/ # Linux sandbox (bubblewrap + seccomp + Landlock)
├── network-proxy/ # Network proxy (HTTP/SOCKS5, domain allowlist/blocklist)
├── config/       # Config loading, constraints, requirements system
├── plugin/       # Plugin system (Skills, MCP, Connectors)
├── app-server/   # App Server (backend for IDE extensions and desktop app)
└── sdk/          # TypeScript + Python SDK (for external integrations)

Three Runtime Modes

Mode Entry Scenario
Interactive (TUI) codex [PROMPT] Interactive fullscreen terminal, human approval
Exec (Headless) codex exec PROMPT Automation/CI, default approval_policy=Never
MCP Server codex mcp-server Called as MCP Tool by other agents

Plus an important subcommand:

  • codex review -- Non-interactive code review (uncommitted/branch diff/single commit)

2. Sandbox System (Key: Why It Fails on Some Servers)

2.1 Three Sandbox Modes

sandbox_mode = "read-only"          # Default: read-only filesystem
sandbox_mode = "workspace-write"    # Write to cwd and TMPDIR (full-auto mode)
sandbox_mode = "danger-full-access" # No sandbox (dangerous! only in already-isolated containers)

2.2 Linux Sandbox Implementation

Three layers:

  1. bubblewrap (bwrap): Filesystem isolation

    • Prefers system-installed bwrap (PATH lookup)
    • Falls back to vendored bwrap (compiled-in C code)
    • Builds read-only root filesystem view, explicitly mounts writable directories
    • Default read-only paths: /bin, /sbin, /usr, /etc, /lib, /lib64, /nix/store
  2. seccomp: System call filtering

    • After bwrap establishes filesystem view, inner process applies PR_SET_NO_NEW_PRIVS + seccomp
    • Two-phase execution: outer bwrap -> re-enter own binary --apply-seccomp-then-exec
  3. Landlock: Legacy fallback (--use-legacy-landlock)

2.3 Common bwrap Failure Causes

Cause 1: /proc mount fails (container environments)

  • Symptom: Can't mount proc on /newroot/proc: Operation not permitted
  • Codex response: Auto preflight detection, fallback to --no-proc mode
  • Logic: Fork child process running /bin/true, check stderr for "Can't mount proc"

Cause 2: No user namespace permission

  • bwrap depends on --unshare-user --unshare-pid
  • Some kernels set kernel.unprivileged_userns_clone=0
  • Docker containers may lack CAP_SYS_ADMIN

Cause 3: Vendored bwrap build missing libcap

  • Compiling vendored bwrap needs libcap headers (pkg-config)
  • Fix: apt install libcap-dev

Solutions (in recommended order):

  1. Install system bwrap: apt install bubblewrap
  2. Enable user namespace: sysctl kernel.unprivileged_userns_clone=1
  3. In containers: --sandbox danger-full-access (ensure container itself has isolation)
  4. Network issues: --no-proc flag

2.4 Network Sandbox

BwrapNetworkMode::FullAccess    # Full network
BwrapNetworkMode::Isolated      # Complete isolation (--unshare-net)
BwrapNetworkMode::ProxyOnly     # Proxy only (via pipe route bridge)

3. MCP Server Integration

3.1 As MCP Client (Connecting External MCP Servers)

[mcp_servers.my-server]
command = "npx"
args = ["-y", "@my/mcp-server"]
# Or remote connection
# url = "https://example.com/mcp"

enabled = true
required = false                 # true: exec mode exits on init failure
startup_timeout_sec = 30.0
tool_timeout_sec = 60.0
enabled_tools = ["tool1", "tool2"]  # Tool allowlist
disabled_tools = ["tool3"]          # Tool blocklist

env = { "API_KEY" = "<YOUR_API_KEY>" }
env_vars = ["HOME", "PATH"]     # Pass from host

# OAuth
scopes = ["read", "write"]

Supports two transports:

  • stdio: command + args (spawn subprocess)
  • Streamable HTTP: url (remote, optional bearer_token)

3.2 As MCP Server (Exposing codex/codex-reply Tools)

Start: codex mcp-server

Exposes two tools:

codex Tool -- Start new Codex Session

{
  "prompt": "required - initial prompt",
  "model": "optional - model name",
  "profile": "optional - config profile name",
  "cwd": "optional - working directory",
  "approval-policy": "untrusted|on-failure|on-request|never",
  "sandbox": "read-only|workspace-write|danger-full-access",
  "config": {},
  "base-instructions": "optional - replace default instructions",
  "developer-instructions": "optional - inject developer instructions",
  "compact-prompt": "optional - prompt for compacting conversation"
}

codex-reply Tool -- Continue existing Session

{
  "threadId": "required - thread UUID",
  "prompt": "required - follow-up prompt"
}

Return structure:

{
  "threadId": "UUID",
  "content": "Agent final response text"
}

3.3 Approval Flow (MCP Context)

When MCP Server mode encounters a command needing approval:

  1. Send elicitation/create request to MCP Client
  2. Include command content, working directory, parsed command structure
  3. Client returns ReviewDecision (Approved/Denied)
  4. Codex executes or rejects accordingly
  5. Deserialization failure defaults to Denied (conservative policy)

3.4 Per-tool Approval Configuration

[mcp_servers.docs.tools.search]
approval_mode = "approve"   # auto / prompt / approve

4. Approval System

4.1 Four Approval Policies

Policy Meaning Use Case
untrusted Only "known safe" read-only commands auto-pass High security
on-failure Auto-execute in sandbox, escalate on failure (deprecated) Legacy compat
on-request Auto-execute in sandbox, request approval outside sandbox TUI default
never All auto-execute, no asking exec mode default

4.2 Granular Approval (Experimental)

{
  "askForApproval": "granular",
  "sandbox_approval": true,
  "rules": true,
  "skill_approval": true,
  "request_permissions": false,
  "mcp_elicitations": true
}

4.3 --full-auto Shortcut

--full-auto equals:

  • sandbox_mode = "workspace-write"
  • approval_policy = "on-request"
  • Network disabled + writes restricted to cwd and TMPDIR

--dangerously-bypass-approvals-and-sandbox (alias --yolo) equals:

  • sandbox_mode = "danger-full-access"
  • approval_policy = "never"

5. Network Proxy / Network Policy

5.1 Built-in Network Proxy

Codex has a built-in HTTP + SOCKS5 proxy intercepting all network requests:

[network]
enabled = true
proxy_url = "http://127.0.0.1:3128"
enable_socks5 = true
socks_url = "http://127.0.0.1:8081"

mode = "full"      # All HTTP methods
mode = "limited"   # Only GET/HEAD/OPTIONS

[network.domains]
"api.openai.com" = "allow"
"*.example.com" = "deny"

[network.unix_sockets]
"/var/run/docker.sock" = "allow"

allow_upstream_proxy = true
dangerously_allow_non_loopback_proxy = false
mitm = false  # MITM decrypt HTTPS (needs custom CA)

5.2 Custom CA Certificate

export CODEX_CA_CERTIFICATE=/path/to/custom-ca.pem
# Or fallback
export SSL_CERT_FILE=/path/to/cert.pem

5.3 Enterprise Proxy Environment

Codex detects standard proxy env vars:

  • HTTP_PROXY / HTTPS_PROXY / ALL_PROXY
  • NO_PROXY
  • allow_upstream_proxy = true to route through upstream proxy

6. config.toml Complete Reference

Config file path: ~/.codex/config.toml

6.1 Core Settings

model = "gpt-5.2-codex"
review_model = "gpt-5.2"
model_provider = "openai"
openai_base_url = "https://api.openai.com/v1"
model_reasoning_effort = "medium"  # low/medium/high
model_context_window = 200000
model_auto_compact_token_limit = 150000
service_tier = "fast"  # fast / flex

sandbox_mode = "workspace-write"
personality = "default"  # default / concise / technical

[shell_environment_policy]
inherit = true
set = { "MY_VAR" = "value" }
exclude = ["SECRET_*"]

notify = ["terminal-notifier", "-title", "Codex", "-message"]

[agents]
max_threads = 6
max_depth = 1

6.2 Config Hierarchy

Configuration merged by priority (later overrides earlier):

  1. Defaults
  2. ~/.codex/config.toml (global)
  3. .codex/config.toml (project-level, searched up directory tree)
  4. Cloud Requirements (remote constraints)
  5. Config Profile (--profile)
  6. CLI -c key=value overrides
  7. CLI flags (--model, --sandbox, --full-auto, etc.)

6.3 Profile System

[profiles.fast]
model = "gpt-4.1-mini"
sandbox_mode = "workspace-write"

[profiles.safe]
model = "gpt-5.2-codex"
sandbox_mode = "read-only"

Usage: codex --profile fast "do something"


7. Operator Reference

7.1 Starting a Codex Session

Method 1: CLI exec (simplest)

codex exec --full-auto -m gpt-5.2-codex "complete task"
# Or fully unrestricted
codex exec --yolo -m gpt-5.2-codex "complete task"

Method 2: MCP Server (multi-agent orchestration)

codex mcp-server  # Communicate via stdio JSON-RPC after start

7.2 Key Environment Variables

Variable Purpose
OPENAI_API_KEY API key
OPENAI_BASE_URL Custom API endpoint
CODEX_HOME Codex home directory (default ~/.codex)
CODEX_SQLITE_HOME SQLite state database path
CODEX_CA_CERTIFICATE Custom CA certificate path
RUST_LOG Debug log level
HTTP_PROXY / HTTPS_PROXY Proxy settings

7.3 Sandbox Troubleshooting

Symptom Cause Fix
Can't mount proc Container lacks proc mount permission Codex auto-fallbacks, or add --no-proc
bwrap not found bubblewrap not in PATH apt install bubblewrap (vendored fallback exists)
Operation not permitted on namespace Kernel blocks unprivileged user namespaces sysctl kernel.unprivileged_userns_clone=1
All sandbox fails in container Docker restrictions --sandbox danger-full-access

7.4 codex exec Common Parameters

codex exec [OPTIONS] [PROMPT]

Key options:
  -m, --model <MODEL>        Specify model
  -p, --profile <PROFILE>    Use config profile
  -s, --sandbox <MODE>       Sandbox mode
  -C, --cd <DIR>             Working directory
  -i, --image <FILE>         Attach image
  -o, --output-last-message <FILE>  Write final response to file
  --full-auto                workspace-write + on-request
  --yolo                     Fully unrestricted
  --json                     JSONL output (for programmatic parsing)
  --ephemeral                Don't persist session files
  --add-dir <DIR>            Additional writable directory
  --skip-git-repo-check      Skip Git repo check
  --oss                      Use local models (Ollama/LMStudio)

7.5 codex review Usage

codex review --uncommitted           # Review uncommitted changes
codex review --base main             # Review changes vs main branch
codex review --commit abc123         # Review single commit
codex exec review --base main        # Review in exec mode

7.6 MCP Server Tool Management

codex mcp add <name> <command> [args...]   # Add MCP server
codex mcp list                              # List configured MCP servers
codex mcp get <name>                        # View single config
codex mcp remove <name>                     # Remove

7.7 Session Recovery

codex resume --last              # Resume most recent session
codex resume <SESSION_ID>        # Resume specific session
codex fork --last "new prompt"   # Fork most recent session with new prompt
codex exec resume --last "continue" # Resume in exec mode

8. Architecture Insights (Value for Command Centers)

  1. MCP Server is the standard orchestration interface: Through codex mcp-server, the hub can treat each Codex instance as an MCP Tool, enabling multi-session orchestration.

  2. Profile system fits multi-role deployment: Different Codex agents can use different Profiles (e.g., fast for simple tasks, safe for critical ops), managed through a single config.toml.

  3. Approvals can be programmatic: In MCP Server mode, approvals go through elicitation/create protocol -- the hub can implement automated approval policies instead of relying on human input.

  4. Sandbox is configurable: On servers with existing container isolation (Docker/K8s), you can safely use danger-full-access to skip Codex's built-in sandbox, avoiding nested sandbox compatibility issues.

  5. Network policy is fine-grained: The built-in proxy's domain allowlist/blocklist ensures agents only access authorized API endpoints.

  6. ThreadId is the session identifier: All MCP interactions are correlated via ThreadId, supporting codex-reply for continued conversation. The hub should maintain ThreadId mappings.