Article

OpenAI Codex as a Coding-Agent Platform

Codex is one open-source harness behind many clients, reachable over a JSON-RPC protocol. What that buys you, and where it ties you in.

Codex gets filed under the wrong noun. It is called a coding assistant, a terminal CLI, an OpenAI answer to Claude Code. None of those is wrong, and none of them explains why the repository contains a Rust runtime, a JSON-RPC server, two SDKs, an execution-policy engine, and three per-OS sandbox implementations.

The framing that explains all of it is OpenAI’s own. In the post on building the App Server, the company puts it plainly: “Codex exists across many different surfaces: the web app, the CLI, the IDE extension, and the new Codex macOS app. Under the hood, they’re all powered by the same Codex harness.” On the DevDay stage the same claim arrives as a product promise — “Codex now works everywhere you build. Whether it’s in your IDE, your terminal, GitHub, web, or mobile… it is the same powerful agent under the hood” (Shipping with Codex, 1:10).

So the useful reading is:

Codex is a coding-agent harness with many clients, and the harness is open source, written in Rust, and reachable over a documented protocol.

That is a different kind of thing from an agent framework. It also makes the platform assessable, because most of what matters is in a repository you can read.

TL;DR

The Codex harness is Rust, Apache-2.0, and shared by every surface. Clients talk to it over app-server, a JSON-RPC protocol whose primitives are thread, turn, and item; the harness talks upstream over the Responses API, which is the only wire protocol a model provider can use. Local execution is bounded by an OS-native sandbox plus an approval policy, both configurable and both enforced on spawned commands, not just on the agent’s own file edits.

Build on Codex when you want a coding agent inside a controlled software environment and you are happy to inherit OpenAI’s opinions about how that environment behaves. Reach for a general agent framework when the work is not coding-shaped. And re-read the docs before you ship against them: the harness changes with model releases, and several claims below replace ones that were accurate in earlier write-ups of this same system.

Surfaces Today

Every surface below is the same harness with a different client wrapped around it. That is the whole architectural claim, and it is why the list is long without being incoherent.

SurfaceWhat it isOpen source
Codex CLITerminal client and codex exec for scripts and CIYes
IDE extensionVS Code, Cursor, and Windsurf share one extension; Xcode and JetBrains ship their own Codex integrationsNo
ChatGPT desktop appCodex selected from the ChatGPT dropdown, with plugins and a sidebarNo
Codex CloudTasks in OpenAI-managed containers from a published environment, reviewable on web, mobile, or desktopNo
Code reviewPull-request review; GitHub generally available, GitLab merge requests in previewNo
GitHub Actionopenai/codex-action@v1, which installs the CLI and runs codex exec in a workflowYes
SDKs@openai/codex-sdk for TypeScript, openai-codex for Python 3.10+Yes
App serverThe protocol the first-party clients use, available to yoursYes

Two entries in that table are corrections rather than updates. The standalone Codex macOS app described in OpenAI’s own architecture post is now Codex inside the ChatGPT desktop app, which is also where the Codex plugins live. And the Codex MCP server is gone: the docs now carry a migration page pointing integrators at app-server instead. If you built against either, you are building against a shape that no longer exists.

The open-source column is not editorial judgement. OpenAI publishes it as a table: CLI, SDKs, app-server, skills, plugins, and the universal cloud environment are open; the IDE extension and Codex cloud are not.

The Harness

The harness is the part worth studying, because it is the part you cannot replace and the part you can read. Dominik Kundel’s walkthrough is the clearest public account of its shape, and it opens with both facts: “the Codex harness… is actually open source… the harness is written in Rust” (How Codex Works, 1:02). The repository’s LICENSE file settles the licence as Apache-2.0, worth checking yourself because the talk corrects itself on stage about which one it is. Michael Bolin, who leads the open-source side, frames the job as letting “the agent shine… making sure that it gets used in training so that things are in distribution” (Turing Post, 1:01).

Kundel also draws the two boundaries that make the architecture legible: between a UI and the harness sits the app server, and between the harness and inference sits the Responses API (2:05). Everything else is implementation detail inside one of those three zones.

How a Codex client reaches a model CLI, IDE, desktop, cloud, and SDK clients connect to one app server. The app server speaks JSON-RPC with thread, turn, and item primitives. The harness behind it has context, tools, sandbox, and approvals. MCP hangs off the tools layer. The harness reaches the model only through the Responses API. CLIENTS PROTOCOL HARNESS INFERENCE CLI IDE Desktop Cloud SDK App server Context Tools Sandbox Approvals MCP Responses API Model JSON-RPC thread · turn · item
Clients talk to one app server. The harness sits behind it. MCP hangs off tools. The only path to a model is the Responses API.

Context construction

The context builder optimises for three things Kundel names in order: size, flexibility, and cacheability (4:12). Cacheability is the one that shapes the rest — if the stable prefix of the prompt keeps changing, every turn pays full price, so the harness is conservative about what it lets move.

That is also the reason tools are loaded late rather than up front. Deferred tools, exposed through a tool-search mechanism, mean a tool definition enters context when it becomes relevant instead of at startup. This is no longer a setting to weigh: the configuration reference now lists tool_search = false and tool_search_always_defer_mcp_tools = false among retired switches that are rejected outright. Progressive tool discovery is the harness’s position, not an option. Skills get a similar budget treatment: Kundel says the skills list is capped “at 2% of your context… window” (6:15), which is the same argument Designing Tools for Agents makes about tool inventories, enforced by a number.

Tools

Edits go through a dedicated apply_patch tool the models were trained on; everything else goes through the shell tool, which runs PowerShell on Windows, and all filesystem access passes through the sandbox layer (10:21). Third-party capability arrives over MCP, which the harness treats as a first-class client of the same tool router. If you are writing those servers, MCP in Practice covers the protocol side; the thing to know here is that Codex’s MCP support carries its own feature flags, including mcp_2026_07_28, so a server built for the current revision needs the matching flag on the harness side.

Sandboxing, per OS

The sandbox is the strongest claim Codex makes, and it is specific rather than rhetorical. The docs state the scope first: the sandbox “applies to spawned commands, not just to built-in file operations”, so git, package managers, and test runners inherit the same boundary. Network access is off by default and writes are limited to the active workspace.

Enforcement is platform-native:

Bolin walks that same per-OS split at 3:04. A sandbox is not only a safety control. It is what makes autonomous command execution a normal case rather than an exception.

Approvals

Approvals are the second layer, and this is where the 2025-era descriptions have gone stale. Current sandbox modes are read-only, workspace-write, and danger-full-access. Current approval policies are on-request, never, and a granular policy that keeps some prompt categories interactive while auto-rejecting others — sandbox approvals, execpolicy-rule prompts, MCP elicitations, request_permissions, and skill-script approvals. The untrusted policy that older guides and configs reference is retired — the docs warn it “can prevent either client from starting” and publish a migration path to a project-level trust_level instead. There is also a newer approvals_reviewer setting whose values are user or auto_review, the second routing eligible prompts under on-request or granular to a reviewer agent rather than to you.

The low-friction local preset is --sandbox workspace-write --ask-for-approval on-request; full access means pairing danger-full-access with never, which is the configuration to treat as a deliberate decision rather than a convenience. Codex also elicits approval for MCP and app tool calls that advertise side effects, and destructive annotations always require approval. That mapping of consequence to checkpoint is the subject of Human-in-the-Loop Control Design, and the boundary it enforces is the one Structured Outputs, Guardrails, and Execution Boundaries argues has to live outside the model.

The App Server Protocol

App-server is the part to look at if you want to embed Codex rather than use it. OpenAI describes it as “a client-friendly, bidirectional JSON-RPC API” and “a long-lived process that hosts the Codex core threads”, built out of four components: the stdio reader, the Codex message processor, the thread manager, and core threads. The JetBrains and Xcode integrations are what drove it.

Three primitives carry everything:

Transport is stdio by default, carrying newline-delimited JSON. A Unix socket and a WebSocket listener also exist, and the docs are blunt that “the app-server command and WebSocket transport are experimental and aren’t supported for production workloads”. Two details will bite you in the first hour: the "jsonrpc": "2.0" header is omitted on the wire, and a connection must send initialize and then an initialized notification before anything else, or every request returns Not initialized.

Here is a complete client. It spawns the server, completes the handshake, opens a thread, runs one turn, and prints the item stream. It also answers server-initiated approval requests — under the default on-request policy a real turn will sit forever if you ignore them — and kills the child process if the server returns an error.

// codex-client.mjs — a minimal app-server client.
// Run with: node codex-client.mjs "Summarise this repo, then stop."
import { spawn } from "node:child_process";
import { createInterface } from "node:readline";

const prompt = process.argv[2] ?? "List the files in this directory, then stop.";

// Pinned: the protocol below was verified against this version.
const codex = spawn("npx", ["--yes", "@openai/codex@0.162.1", "app-server"], {
  stdio: ["pipe", "pipe", "inherit"]
});

let nextId = 0;
const request = (method, params) => {
  const id = nextId++;
  // No "jsonrpc" field — app-server omits the JSON-RPC header on the wire.
  codex.stdin.write(`${JSON.stringify({ method, id, params })}\n`);
  return id;
};
const notify = (method, params = {}) => codex.stdin.write(`${JSON.stringify({ method, params })}\n`);
const reply = (id, result) => codex.stdin.write(`${JSON.stringify({ id, result })}\n`);

const initializeId = request("initialize", {
  clientInfo: { name: "aeorg_demo", title: "Agent Engineering Demo", version: "0.1.0" }
});
let threadStartId;
let turnStartId;

for await (const line of createInterface({ input: codex.stdout })) {
  const message = JSON.parse(line);

  if (message.error) {
    // -32001 is backpressure, not a bug: retry with an exponential delay plus jitter.
    console.error(`error ${message.error.code}: ${message.error.message}`);
    codex.kill();
    break;
  }

  // Server-initiated request: method + id. (Our replies have id but no method.)
  if (typeof message.method === "string" && message.id !== undefined) {
    const result = message.method.startsWith("mcpServer/")
      ? { action: "decline", content: null }
      : { decision: "decline" };
    reply(message.id, result);
    console.log(`declined ${message.method}`);
    continue;
  }

  if (message.id === initializeId) {
    notify("initialized");
    threadStartId = request("thread/start", {});
    continue;
  }

  if (message.id === threadStartId) {
    // Pass model, cwd, approvalPolicy, or sandboxPolicy here to override the defaults.
    turnStartId = request("turn/start", {
      threadId: message.result.thread.id,
      input: [{ type: "text", text: prompt }]
    });
    continue;
  }

  if (message.id === turnStartId) continue;

  // Everything else is a notification: the turn streaming its items.
  switch (message.method) {
    case "item/started":
    case "item/completed": {
      const item = message.params.item;
      const detail =
        item.type === "agentMessage" ? item.text
        : item.type === "commandExecution" ? item.command
        : item.type === "fileChange" ? item.changes.map((c) => `${c.kind} ${c.path}`).join(", ")
        : item.type === "mcpToolCall" ? `${item.server}/${item.tool}`
        : "";
      console.log(`${message.method === "item/completed" ? "done " : "start"} ${item.type}: ${detail}`);
      break;
    }
    case "turn/completed":
      console.log(`turn ${message.params.turn.status}`);
      codex.stdin.end();
      break;
  }
}

Four things in there are worth copying into your own client. Pin the version, because the protocol surface moves with it — and when you need exact types, codex app-server generate-ts --out ./schemas emits a schema for the version you actually ran. Answer item/commandExecution/requestApproval and item/fileChange/requestApproval (and the other server-initiated requests); a missing reply is a hang, not a skip. Treat JSON-RPC error -32001, “Server overloaded; retry later”, as backpressure to retry with jitter rather than a failure to surface, and kill the child if you are going to stop. And treat item/completed as the source of truth; deltas are for rendering, and the docs warn that a final plan item may not exactly equal its concatenated deltas.

If you only want to script Codex rather than build a client around it, the SDKs do this for you — the Python package drives exactly this protocol against a local app-server.

When to Build on Codex, and When Not To

The decision is narrower than “which agent platform”. It is about whether your problem is coding-shaped.

Build on Codex when the work is software work in a repository, you want OS-enforced execution limits without writing them, and you want one runtime behind several entry points — a terminal, an editor, CI, and your own product reaching the same threads through app-server or the SDKs. The thread model is the part you cannot easily rebuild: resumable sessions, forkable history, steerable in-flight turns, and an item stream you can render.

Reach for a framework instead when the agent is not editing code. Codex gives you no graph, no router, no supervisor, no multi-agent composition primitives; those belong to the systems covered in AI Agent Frameworks. Trying to express a business workflow as a sequence of coding turns is the wrong shape in both directions.

The honest cost is coupling. The Responses API is the only wire protocol a provider can speak to the harness — the configuration reference says responses “is the only supported value” for model_providers.<id>.wire_api — and the client surfaces, identity, and admin paths are OpenAI’s. The open-source core limits that risk without removing it: you can read the harness, run it against a local model, and build your own client, but you are still inside someone else’s execution model.

The other cost is one every coding-agent platform shares. Ryan Lopopolo’s account of running a codebase where the constraint was “to not write any of the code myself” (Extreme Harness Engineering, 3:41) includes harness quirks that differ between model generations. The 5.2-era harness had no background shells; adding them in 5.3 made the model less patient and less willing to block (5:43). Behaviour you depend on can be behaviour that moves. The organisational version of that lesson is in What Stripe’s Minions Reveal About Production Coding Agents: the hard parts of putting coding agents to work are environment, verification, and review, not the agent.

Which is also the most useful thing about Codex’s AGENTS.md story. In the DevDay demo, the file is used to tell Codex about a verification script so the agent can check its own UI work by screenshot (11:05). The platform gives you execution; you still have to supply the definition of done.

FAQ

Is Codex open source?

Partly, and OpenAI publishes exactly which parts. The CLI, the SDKs, app-server, skills, plugins, and the universal cloud environment are open in openai/codex and sibling repositories, under Apache-2.0. The IDE extension and Codex cloud are not. The open-source page is the authoritative list, and it is worth checking rather than assuming, because “the harness is open source” gets repeated as if it covered every surface.

Which parts run locally?

The harness runs on your machine for the CLI, the IDE extension, and the desktop app: context construction, tool calls, sandboxing, and approvals are local, and model inference is remote over the Responses API. Codex Cloud is the inverse — tasks execute in OpenAI-managed containers from a published environment, with the results reviewed from web, mobile, or desktop. The SDKs and app-server drive a local agent, which is why the Python package ships with a pinned Codex CLI runtime dependency.

How does sandboxing work on each OS?

macOS uses Seatbelt and needs no setup. Linux and WSL2 use bubblewrap for filesystem and namespace isolation plus seccomp for syscall filtering, with Landlock retained as a legacy path; you install bubblewrap yourself, and WSL1 is unsupported. Windows uses its own native sandbox in PowerShell, or the Linux path under WSL2. In every case the boundary applies to spawned commands, so test runners and package managers inherit it, and the default is no network access with writes confined to the workspace.

Can I use non-OpenAI models?

Yes, within one constraint: the provider has to speak the Responses API. openai, ollama, and lmstudio are reserved built-in provider IDs, and you can define others under model_providers.<id> with a base_url, auth, and headers — but wire_api accepts only responses. So a local or third-party model that exposes a Responses-compatible endpoint plugs into the same harness, sandbox, and approvals, and one that only speaks Chat Completions does not. Enterprise gateway paths, including Bedrock through LiteLLM, exist for the same reason.

Codex versus Claude Code?

They occupy the same category — a coding-agent harness rather than an agent framework — so the comparison that actually decides anything is about inspectability and embedding rather than which answers better on your repo. With Codex you can read the harness, run it against a Responses-compatible local model, and drive it over a documented JSON-RPC protocol with versioned schema generation. If none of those matter to your team, the choice comes down to day-to-day fit, and you should run both against real tasks and your own error analysis rather than trusting either vendor’s framing.

Further watching

Each of these was transcript-checked for this article; timestamps link to the moment the claim appears.

Primary Sources

Codex product documentation moved from developers.openai.com/codex to learn.chatgpt.com/docs during 2026; the old paths redirect, and every page has a Markdown twin at /docs/<slug>.md. Note the naming trap: /docs/security is now Codex Security, the application-security scanning product, while sandboxing and approvals live on the pages below.

Last verified: Oct 2026, against @openai/codex 0.162.1 and the app-server protocol reference.