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.

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 · 6One 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.tsat 48,389 bytes is the largest hand-written file in the repository — thec2ccommand surface:setup,doctor,pair,unpair,status,logs,session,prefs,record,sandbox-allow,update-check,workspaceandtunnel, with--jsonon 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 isserver.ts(8,529) plusruntime.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.ts14,340,store.ts8,505,middleware.ts2,271, the pairing page inhtml.ts695 — with PKCE S256, dynamic client registration, rotating refresh tokens and revocation, next tomanager.ts(4,965) for the one-time codes. - src/workspace/, src/execution/ and src/session/
- Reading and containment:
manager.ts12,784,git.ts10,319,search.ts6,307,ignore.ts2,416. Then the review loop’s records —output.ts3,493,sanitize.ts2,359,records.ts2,090 — andsession/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
TunnelProviderinterface, from Cloudflare Quick to a workspace-configured named tunnel. Four documents and 23 KB underdocs/; eighteen test files and 107 KB undertests/, 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.mdmakes 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.mdstates 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, sendingHANDOFFbuilt from the checkpoint "never from logs".docs/security.mdrecords 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, soc2c doctorrestarts it and tells the Skill to delete and recreate that workspace’s connector, while a named hostname is a once-per-workspace choice withcloudflared tunnel loginas 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- 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 Skillon 2026-08-28 and the last isAccept 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 Releaseon 2026-08-30,v0.1.1 — Setup Choice, Checkpoints & Sanitized Outputthe next day,v0.1.2 — Structured MCP, Safer Tunnels & Windows Polishon 2026-09-04 andv0.1.3 — Connector Recovery, Hidden git_status & HTTP/2on 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. - 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:INITfrom Codex asking for a plan,PLANfrom ChatGPT with rationale, actions, files likely involved, tests and success criteria,EXECUTEDfrom Codex with iteration metadata, thenREVIEW,DONEorBLOCKED.docs/protocol.mdstates 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. AfterEXECUTED, ChatGPT inspects the realgit diffand 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." - 03
How a session survives: a checkpoint, a handoff and a Project
Continuity is deliberately local.
c2c sessionkeeps one JSON file per workspace in the OS application-state directory, holdingprotocolState,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 noSTATE: RESUME." If Codex restarts mid-task it reads the checkpoint; if the chat itself is gone it sendsHANDOFF, 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-minutewaitFor, and the commit that introduced the rule is titledPoll ChatGPT replies in short DOM checks so a 5-minute browser wait is not treated as failure. - 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 runsc2c update-checkandc2c sandbox-allowat 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 intoworkspace.read,workspace.search,git.read,execution.readandoffline_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 quotaandc2c 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. - 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-wfix. 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, thengit pull --ff-only,corepack pnpm install && corepack pnpm build, reinstall the Skill, restart the bridge, and carry on with whatever task triggered it. - 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 failedon a Windows proxy route that can reachcloudflaredbut not bothchatgpt.comand Cloudflare’s verification host (459), and a fresh*.trycloudflare.comhostname 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 isupdate-checkrejecting the-wflag the Skill had been told to pass everywhere. The author rewrote the Skill to say which commands take-wand 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 →No. 070
OpenChatCut
A local-first video editor whose editing surface is a conversation: the built-in agent and external Codex or Claude Code sessions call the same editing tools the interface itself uses, so every change lands on a real multi-track timeline as a clip, transition, caption, effect or audio item that can still be dragged, undone and exported. Projects and media stay on the machine, and preview and final render both come out of Remotion.
No. 081
pgbot
A single static Go binary that connects to PostgreSQL read-only, reads the server’s own statistics views and prints a graded, findings-first health report — and, because every run saves a local baseline, tells you what changed since last time; the same deterministic findings are served to AI agents over MCP, and the optional AI layer may only explain them.
No. 077
Lody
A workspace where a team shares the coding agents it already runs: connect a machine, bring Claude Code, Codex, Kimi or any other agent that speaks the protocol, and dispatch work from desktop, phone, web or terminal while sessions delegate to each other and the code stays on the machine its owner connected.