Skip to content

Codex with ChatGPT

A loopback bridge that hands planning and review to the ChatGPT web app while Codex keeps the keyboard: ChatGPT reads the workspace through nine read-only MCP tools, Codex types sub-kilobyte state messages into the ChatGPT tab, and no file body, diff or log is ever pasted into the chat.

Screenshot of Codex with ChatGPT
Editor screenshot, 30 Sep 2026Codex with ChatGPT ↗

What it is

A bridge that splits one coding session between two models. The ChatGPT web app plans and reviews; Codex keeps the shell, the tests and the commits. What makes the split work is that the two halves never share a channel: Codex types tiny [C2C] control messages into the ChatGPT tab through its own in-app browser, and ChatGPT pulls the code it needs over an OAuth-protected read-only MCP connection to a loopback bridge running on the developer machine. Nine tools exist and all of them read — workspace_info, list_directory, read_file, search_workspace, git_status, git_diff, test_status, execution_summary, execution_output — so the repository is never uploaded, and no prompt injection can reach a write tool, because there is no write tool. The public half is a Cloudflare tunnel; the only secret that ever reaches a browser is a one-time pairing code. A Codex Skill, not a GUI, is the interface: the user asks for it in one sentence and the agent installs, pairs and runs the loop itself.

Who built itThe repository answers its own issue tracker through an AI agent that signs every reply "[Agent · codex-with-chatgpt]", records the report, and tells the reporter that the author — at school, the agent says — will see it within about 48 hours. Commits split across two identities: 20 as Xiaoduo from xiaoduo_@xiaoduodeMacBook-Air.local and 19 under the XiaoDuoYa account, 39 of the 51 in total, against ten contributors of whom three landed more than one commit. Between the first commit and the last, sixteen days, the repository reached 6,943 stars and 642 forks.

How it is put together

The parts · 6

One Express bridge per workspace, bound to 127.0.0.1, holding an OAuth 2.1 authorization server, a pairing-code manager, a tunnel manager, a read-only MCP server, and a record of each iteration. The ChatGPT side never runs anything: it holds a connector and a Project, and the only way it touches the repository is the nine read tools. The Codex side owns everything that writes — shell, tests, git — and drives the ChatGPT tab through its own in-app browser with sub-kilobyte control messages, which is why the largest hand-written file in the repository is the CLI and the second largest is the Skill document telling an agent how to use it. The workspace is the security boundary: one bridge serves one workspace, every token is bound to it, and a token from another workspace gets 403. Durable state — pairing, sessions, tunnels, preferences — lives in the OS application-state directory and never in the project, which holds at most an optional .c2c.json and a .c2cignore.

src/cli/
index.ts at 48,389 bytes is the largest hand-written file in the repository — the c2c command surface: setup, doctor, pair, unpair, status, logs, session, prefs, record, sandbox-allow, update-check, workspace and tunnel, with --json on everything the Skill parses.
skill/SKILL.md
42,600 bytes, 634 lines, and described in the README as the real UX layer: golden rules, the in-app-browser discipline, first-time setup with an automatic and a guided manual path, the coding loop, and a recovery map from symptom to command.
src/mcp/ and src/bridge/
server.ts (16,351 bytes) registers the nine read-only tools on a stateless Streamable HTTP transport, one fresh server per request. The bridge is server.ts (8,529) plus runtime.ts (3,644): loopback-only listener, port fallback, runtime state and a local admin API.
src/auth/ and src/pairing/
An OAuth 2.1 authorization server — oauth.ts 14,340, store.ts 8,505, middleware.ts 2,271, the pairing page in html.ts 695 — with PKCE S256, dynamic client registration, rotating refresh tokens and revocation, next to manager.ts (4,965) for the one-time codes.
src/workspace/, src/execution/ and src/session/
Reading and containment: manager.ts 12,784, git.ts 10,319, search.ts 6,307, ignore.ts 2,416. Then the review loop’s records — output.ts 3,493, sanitize.ts 2,359, records.ts 2,090 — and session/state.ts (9,152), which holds the conversation mode and the checkpoint.
src/tunnel/, docs/ and tests/
Eight tunnel files and 31 KB behind a TunnelProvider interface, from Cloudflare Quick to a workspace-configured named tunnel. Four documents and 23 KB under docs/; eighteen test files and 107 KB under tests/, the largest the MCP integration test at 16,289 bytes.

Choices, and what they beat

  • Keep Codex as the executor instead of writing a harness over implementing the coding loop inside the bridge

    docs/architecture.md makes it the first principle and says why: "ChatGPT thinks. Codex works. The bridge never re-implements a coding harness." Everything the bridge does is therefore transport, authentication and reading.

  • A read-only MCP server with no write plane at all over write or shell tools guarded by an approval step

    docs/security.md states the consequence rather than the intention: "Write files, delete files, run shell commands, commit, install packages — these tools do not exist on the server, so no prompt injection, scope bug, or UI confusion can enable them." The README adds that knowing the public URL grants nothing, because every request still needs a token.

  • Two channels that never carry each other’s traffic over pasting diffs, logs and file bodies into the chat

    docs/protocol.md: "Never mix the two: control messages carry state, never content", with control messages kept under 1 KB of key-value headers and sections. The data plane is pull-only, so ChatGPT fetches exactly the lines it needs and the repository is never uploaded.

  • A local checkpoint rather than a new protocol state over a resume state, or replaying the previous conversation from logs

    The protocol says "There is no STATE: RESUME", and a restart is handled by reading the checkpoint and, if the chat is gone, sending HANDOFF built from the checkpoint "never from logs". docs/security.md records the boundary as a rule: no new protocol state, no log paste, no re-pairing.

  • A Cloudflare Quick Tunnel by default, a named hostname on request over requiring the user to own a domain before anything works

    docs/architecture.md: the default URL changes per start, so c2c doctor restarts it and tells the Skill to delete and recreate that workspace’s connector, while a named hostname is a once-per-workspace choice with cloudflared tunnel login as the only extra user step — and if named provisioning fails, the tunnel falls back to Quick.

Read fromdocs/architecture.md、docs/protocol.md、docs/security.md、skill/SKILL.md、README.md、src/session/state.ts、src/pairing/manager.ts、src/auth/store.ts、src/config/ui-prefs.ts、src/tunnel/cloudflared.ts, read against the complete 70-file tree with its sizes.

Build log

6 stages
  1. 01

    Sixteen days, four releases, and an agent answering the issues

    The first commit is Initial V1: C2C Bridge with read-only MCP, OAuth+pairing, tunnel, CLI and Skill on 2026-08-28 and the last is Accept leftover -w on machine-wide c2c commands. on 2026-09-13: 51 commits, 35 in August and 16 in September, and four releases inside the first thirteen days — v0.1.0 — First Public Release on 2026-08-30, v0.1.1 — Setup Choice, Checkpoints & Sanitized Output the next day, v0.1.2 — Structured MCP, Safer Tunnels & Windows Polish on 2026-09-04 and v0.1.3 — Connector Recovery, Hidden git_status & HTTP/2 on 2026-09-11. The motive is stated in one line: paid ChatGPT web quota sits idle while the coding agent burns scarce API tokens on planning and review, so the thinking moves to the subscription and the execution stays on the machine, with no API key and no reverse proxy. Around those sixteen days sit 6,943 stars, 642 forks and 34 open issues. The repository also runs itself: issue after issue comes back with a recorded reply signed [Agent · codex-with-chatgpt], saying the report has been relayed and the author, who is at school, is expected to see it within about 48 hours. The report reads the thirty newest issues and pull requests in full, and they are a support queue for a product whose hardest part turned out to be authentication.

  2. 02

    Two models, two channels, and a rule against mixing them

    The division of labour is written down as a protocol. A control message starts with [C2C], carries key-value headers and sections, and must stay under 1 KB: INIT from Codex asking for a plan, PLAN from ChatGPT with rationale, actions, files likely involved, tests and success criteria, EXECUTED from Codex with iteration metadata, then REVIEW, DONE or BLOCKED. docs/protocol.md states the boundary twice — "Never mix the two: control messages carry state, never content", and "No diffs, no logs, no file bodies" — and the Skill’s first golden rule repeats it for the agent: never paste file contents, diffs or logs into ChatGPT, because ChatGPT reads them through MCP. That is the data plane: nine read-only tools over a stateless Streamable HTTP transport, and the review step is built on them. After EXECUTED, ChatGPT inspects the real git diff and the recorded test status itself, because the boot prompt says "Do not assume an implementation succeeded just because Codex says so". The architecture document explains why the bridge stays this small: "The bridge never re-implements a coding harness."

  3. 03

    How a session survives: a checkpoint, a handoff and a Project

    Continuity is deliberately local. c2c session keeps one JSON file per workspace in the OS application-state directory, holding protocolState, waitingFor, the original goal, completed subtasks, known issues and the next expected step, each field capped — 500, 800, 800 and 400 characters. The protocol has no resume state and says so: "There is no STATE: RESUME." If Codex restarts mid-task it reads the checkpoint; if the chat itself is gone it sends HANDOFF, a brief built from that checkpoint "never from logs", and the new conversation re-reads the code through MCP. Conversations come in two shapes — one long-lived chat per workspace, or one ChatGPT Project per workspace with each new chat starting inside it, where durable identity lives in the project instructions and only the connector name is written down, never an address that will change. The trust order is written out as well: current code from the connector, then the handoff, then the instructions, then project memory, with stale memory losing. The Skill carries the same discipline into the browser: a reply is awaited with one cheap DOM check every 20 to 30 seconds rather than a five-minute waitFor, and the commit that introduced the rule is titled Poll ChatGPT replies in short DOM checks so a 5-minute browser wait is not treated as failure.

  4. 04

    Where the quota thinking shows up

    The project exists because of a quota asymmetry, so quota is visible in the details rather than in a settings page. The loop stops at maxIterations, default 12, set in an optional .c2c.json; control messages are capped under 1 KB; the Skill runs c2c update-check and c2c sandbox-allow at the start of every workflow, both cached, both silent unless something changed. The pairing code lives five minutes, allows five attempts, is limited to ten tries per IP per minute and is destroyed on use; OAuth access tokens last an hour, refresh tokens thirty days and rotate on every use, and scopes are separated into workspace.read, workspace.search, git.read, execution.read and offline_access. The public address is a Cloudflare Quick Tunnel that gets 45 seconds to become ready, and a filtered network gets one documented escape hatch, C2C_TUNNEL_PROTOCOL=http2, rather than a proxy. The sharpest evidence that this is about allowances sits in the issue tracker instead: the review bot on pull request 453 announces "You have reached your Codex usage limits for code reviews", and an outside contributor’s pull request 441 proposes the missing feature — c2c quota and c2c route, reading the signed-in account’s remaining allowance and moving planning to ChatGPT below a 20 per cent threshold while Codex keeps execution. That one is still open.

  5. 05

    What the author changed, and the tool that made the commits

    The commit history is also a record of which agent wrote it. Of the 51 commits, 36 carry at least one co-author trailer and those 36 hold 46 trailer lines; 28 of them name Cursor as cursoragent@cursor.com, including the very first commit. The rest are people — moonjun and pi on three each, AtlaxTech on two, and a tail of ten one-offs that includes hbhuyt, credited after the -w fix. The change list reads as a list of things that broke for real users: Whitelist the C2C state directory in the Codex sandbox on macOS and Windows so new chats stop needing elevation; Keep the ChatGPT in-app tab visible and reused so first-time setup stops stalling; Isolate one ChatGPT connector per workspace so two projects can stay connected at once; Stop opening ChatGPT or sending C2C until doctor reports a healthy local bridge; Unify git_diff sensitive-file rules with MCP reads so secrets cannot leak through renames; Offer an optional Cloudflare named hostname so ChatGPT connectors survive restarts; Remember ChatGPT developer mode and let users pick auto or guided setup; and three in a row on 2026-09-02 that stop the bridge, quick-tunnel and named-tunnel consoles from flashing up on Windows. Setup asks the user once to choose between automatic configuration, which may fail twice before falling back, and a guided manual path the prompt says takes about three minutes. Updates are self-service too: a daily check, then git pull --ff-only, corepack pnpm install && corepack pnpm build, reinstall the Skill, restart the bridge, and carry on with whatever task triggered it.

  6. 06

    What the community reported, and the bug that was a flag

    The reports cluster around the parts that are not code. On macOS, pairing succeeds and then stops: the ChatGPT Desktop loopback callback never completes, the persisted token count stays at zero, and a second click on Connect returns "This authorization request has expired" because the first click already consumed the pending request (457). Elsewhere a connector sits in an indefinite authorization spinner (450), every MCP call returns an internal error while the local checks are green (449), a healthy bridge still reports fetch failed on a Windows proxy route that can reach cloudflared but not both chatgpt.com and Cloudflare’s verification host (459), and a fresh *.trycloudflare.com hostname fails to resolve inside the 45-second window (437). Re-authorising every day is the longest thread, and the answer is the one in the code: a quick tunnel dies with the terminal, and surviving a restart means owning a domain (447). In VS Code there is no in-app browser, so the golden rule that forbids a third-party browser blocks setup until the user insists (456). The best of them starts as a misdiagnosis: a user on Clash reports that 「更新 Codex with ChatGPT」fails, with a model’s write-up blaming Node and the sandbox, and the real cause is update-check rejecting the -w flag the Skill had been told to pass everywhere. The author rewrote the Skill to say which commands take -w and which must not, made the machine-wide commands accept and ignore it, gave the reporter a co-author line and thanked him in the thread.

Adjacent records

All records →