A braided cable running from a laptop into a small unbranded aluminium pass-through box on a desk, with a second cable leaving the far side.

Using any LLM with Codex CLI: what the proxy sends


You can run Codex CLI on any LLM you like — Claude, Gemini, or a model on your own GPU. Every guide on the first page of Google shows you the config to make that happen. None of them shows you what actually leaves your machine once you do.

So we captured it. We put a recording server where the provider should be, pointed opencodex 2.43.0 at it, and ran a single five-word prompt through Codex CLI 0.153.4 on macOS 26.6.2.

Key takeaways

  • A five-word prompt through the proxy produced a 51,731-byte request to the provider. The user’s actual prompt was 113 bytes of it — 0.22%.
  • Codex no longer speaks Chat Completions at all. In the openai/codex source the WireApi enum has one variant, Responses, and wire_api = "chat" now returns a hard error. Guides older than that change send you into a config Codex refuses to start with.
  • Prompt caching survives the translation. We expected it to break and it didn’t: we counted four cache_control breakpoints on the wire, in the order the adapter’s own comment promises.
  • In OAuth mode the proxy sends vendor-specific first-party headers: a Claude Code fingerprint, and an Antigravity user agent the source documents as decompiled from Google’s language server binary, down to the function offset. No vendor has ruled on that publicly. What the project’s README does warn about is separate and broader — routing through a third-party proxy at all may get an account “suspend[ed] or restrict[ed]”, and it names Anthropic.
  • Starting the proxy rewrites your Codex config, 24 bytes to 398 in our test. It then sits in the path of every request. Including the ones going to OpenAI.

How you point Codex CLI at another model

You point it, and then you discover that pointing was never the hard part. ~/.codex/config.toml takes a custom provider block and a selector, and this is first-party and documented:

# ~/.codex/config.toml
model = "claude-sonnet-4-5"
model_provider = "myproxy"

[model_providers.myproxy]
name = "My proxy"
base_url = "http://127.0.0.1:10199/v1"
env_key = "MYPROXY_API_KEY"
wire_api = "responses"

# then: export MYPROXY_API_KEY=whatever-your-proxy-expects

That aims Codex anywhere you like. What it can’t do is make the far end intelligible, because Codex speaks the Responses API and nothing else. So whatever answers at base_url isn’t optional plumbing. It’s a translator, and everything interesting happens inside it.

That’s the whole category: LiteLLM, Bifrost, opencodex, and the local-model setups built on Ollama or llama.cpp all work this way. They differ in what they translate to and how much they add on the way through. If you’re new to the tool itself, our guides on AI coding tools cover the ground before this one.

We picked opencodex because it’s the one making the broadest claim — its README says it “translates Codex’s Responses API into whatever your provider speaks — streaming, tool calls, reasoning tokens, images, in both directions” — across 40-plus providers. Broad claims are the ones worth measuring.

Half the guides you’ll find are now broken

Codex dropped Chat Completions support. That single change invalidates a lot of what’s published.

We checked it in the source rather than taking a docs page’s word for it. In codex-rs/model-provider-info/src/lib.rs, the enum is:

pub enum WireApi {
    /// The Responses API exposed by OpenAI at `/v1/responses`.
    #[default]
    Responses,
}

One variant. Deserializing the string "chat" doesn’t fall back. It returns an error carrying this message:

`wire_api = "chat"` is no longer supported.
How to fix: set `wire_api = "responses"` in your provider config.
More info: https://github.com/openai/codex/discussions/7782

The ollama-chat provider id got the same treatment, pointing at the same place. Discussion 7782 is titled “Deprecating chat/completions support in Codex” and was opened in December 2025.

OpenAI gave the timeline in that discussion: “Full removal is slated for early February 2026,” and “In February 2026, this will transition to a hard error as support is fully removed.” A commenter on February 5, 2026 confirmed it had landed, pointing at PR #10157.

What we couldn’t pin down is the exact CLI release tag that shipped it. A git log -S against a shallow clone returned a commit whose message had nothing to do with the change — a clone-boundary artifact, not an answer. So: February 2026, PR #10157, release tag unconfirmed.

The practical consequence: if a tutorial hands you a wire_api = "chat" block, it predates this and Codex won’t start. Check the date on anything you follow.

What 51,731 bytes is made of

Here’s the part nobody publishes. Our sink recorded a single POST /v1/messages for the prompt Say the single word: ok:

Component Bytes Share
System prompt 23,953 46.3%
Tool definitions 15,460 29.9%
Messages 12,144 23.5%
Total 51,731 100%

Inside that message block, a <skills_instructions> preamble is 11,218 bytes, the environment context is 809, and the thing we typed is 113. Five tools are declared; exec alone accounts for 10,898 bytes of schema.

A small blank sticky note lying beside a tall stack of paper on a desk, illustrating the size gap between a short prompt and the request it travels in

Two caveats before anyone quotes that number. Most of this is Codex’s own payload, not the proxy’s. We measured the far side, so we can’t split “what Codex sends” from “what the proxy adds” without a second capture on the inbound port, and we didn’t run one. And byte counts aren’t tokens. But the shape holds. On a short prompt, the prompt is a rounding error. We hit the same pattern when we measured Claude Code’s startup token overhead, from a completely different direction.

The caching hypothesis we got wrong

We went in expecting the proxy to destroy prompt caching. A translation layer that re-serializes every request is the classic way to turn cache hits into full-price misses, and nothing we could find addressed it. The README mentions cache token counts in the dashboard’s request log, but never says whether the translation preserves the breakpoints that produce them, and no page ranking for this query raises the question either. That silence looked like a finding.

It wasn’t. The Anthropic adapter carries a documented strategy — four breakpoints placed in order of stability, most stable first, so Anthropic’s cumulative prefix cuts land where they help:

  1. tools, last block
  2. system, last block
  3. the penultimate user message
  4. the last user message

We confirmed all four on the wire, exactly there, each {"type":"ephemeral"}. The adapter also bails out entirely if the client already set its own cache_control, enforces Anthropic’s four-breakpoint ceiling, and demotes a one-hour TTL that appears after a five-minute one — that ordering rule is easy to get wrong and they didn’t.

Default retention is "short", the five-minute tier. If you want the one-hour cache you set cacheRetention: "long" yourself.

That’s competent work. The interesting question about these proxies turned out to be somewhere else.

What headers does the proxy send in OAuth mode?

Configure a provider with authMode: "oauth" — the mode you’d use to put your Claude subscription behind the proxy — and opencodex attaches a set of headers that mimic Anthropic’s official client. A full X-Stainless-* block pinned to “Claude Code 2.1.63 / @anthropic-ai/sdk 0.74.0”, plus an X-Claude-Code-Session-Id shaped like a UUIDv4 but derived from a SHA-256 of your OAuth token.

The file’s own header comment says why:

Routed OAuth providers reject — or quietly flag — requests whose header signature doesn’t match the real first-party client that minted the token. […] These constants mirror the headers the real Claude Code CLI and Antigravity CLI send, so the proxy’s request fingerprint matches the credential.

The Antigravity user agent is documented as “decompiled from 2.5.5 Go LS (setHeaders @ 0x1018fbe00)”, with a note that Google’s backend answers 404 to CLI-shaped user agents and only the IDE string unlocks newer models.

A blank unbranded access badge on a lanyard resting beside a closed laptop, illustrating a request that carries someone else's client credentials

A second file goes further. Codex hardcodes “You are Codex, a coding agent based on GPT-5.” into every system prompt, which is wrong when the model is Claude, so the proxy rewrites it. An earlier version of that rewrite mentioned the proxy by name, and the comment explaining why they removed it reads:

…which leaked our proxy identity into the upstream payload — a signature no first-party client (Claude Code, Gemini CLI, Kiro) ever sends, and a likely ToS trigger.

We verified the result on the wire. “You are Codex” is gone, replaced by “You are a coding agent powered by the claude-sonnet-4-5. If asked which model you are, identify as claude-sonnet-4-5…” The strings opencodex and proxy appear nowhere in the 51,731 bytes.

Two limits on that. It only fires in OAuth mode: with a plain API key the adapter sends x-api-key and none of it. The ordinary, uncontroversial case. And we never pointed this at a real subscription, so we have not seen any vendor react. Whether accounts actually get flagged is exactly the question a code read can’t answer, and we’re not going to guess at it.

The project doesn’t hide the underlying risk either, though its README frames it more broadly than the code does. The source comments worry about the proxy being identifiable. The README worries about the proxy existing at all: “Some providers — notably Anthropic (Claude) — may suspend or restrict accounts that route API traffic through third-party proxies. Use at your own risk (UAYOR). Before connecting a provider, review its Terms of Service to confirm that proxy-based access is permitted.”

That names the vendor and uses the word suspend. It’s the project’s own warning, not our inference — and note where it attaches: to the routing, not to the headers. Which is the part worth sitting with. Three things are in play, and they aren’t the same thing: the README warns that proxying at all can cost you an account, the comments worry about the proxy being spotted, and the code sends first-party headers so it isn’t. We found no statement from any vendor about the headers specifically.

What breaks when you set this up?

The install is not the ten minutes the tutorials promise, and the failures are worth knowing about.

Pointing opencodex’s own upstream provider at 127.0.0.1 was rejected outright (the Codex-side base_url in the block above is supposed to be loopback; this is the other end): baseUrl points to a loopback address; set allowPrivateNetwork:true only for intentionally local/self-hosted providers. That’s a sensible SSRF guard and we’d rather it were there, but it isn’t in any guide we read.

A fake OAuth token got a 401 OAuth authentication failed from the proxy itself, before anything reached our recorder. Score one for the proxy: it validates credentials instead of blindly forwarding them. It also cost us a rerun.

And on every launch Codex tried a WebSocket transport first, got 426 Upgrade Required from the proxy, and printed five Reconnecting... lines before falling back to HTTP and working fine. Noisy. Harmless as far as we could tell, though we didn’t chase it.

Worth knowing before you install: the package is 112 MB and bundles its own Bun runtime.

It takes over your Codex config

ocx start doesn’t just run a server. In our isolated test home, config.toml went from 24 bytes to 398, gaining a model catalog path and two injected lines:

# Auto-injected by opencodex
openai_base_url = "http://127.0.0.1:10199/v1"
# Auto-injected by opencodex
experimental_realtime_ws_base_url = "http://127.0.0.1:10199/v1"

Note which key it writes. Rather than adding a scoped [model_providers.opencodex] block of the kind shown earlier, it overrides openai_base_url — the global setting for Codex’s built-in OpenAI provider. That’s the mechanism behind its own startup log, which says “All models now route through opencodex proxy” and “OpenAI models (gpt-5.5, etc.) are passed through to OpenAI.” Once installed it sits in the path of everything Codex sends, including the traffic that was going to OpenAI in the first place. ocx stop reverses it, and there’s an ocx uninstall that removes the shim.

On what it keeps: we found no third-party telemetry. Greps for PostHog, Sentry, Mixpanel, Amplitude and Segment across src/ come back empty, and the “routing analytics” is a local management endpoint. There’s a spill store that writes response state to ~/.opencodex/responses-state-spill/ with a 256 MB per-payload cap, but our run never created the directory. On the same evidence standard we used when we read OpenCode’s source for its default telemetry, that’s a clean result for a single short session — not a claim about long ones.

So should you use one

If you’re running local models, you probably don’t need a separate proxy process at all. Codex ships an --oss mode with oss_provider set to LM Studio or Ollama, and OpenAI’s own deprecation notice points at LM Studio in particular. llama.cpp exposes a Responses endpoint too — though its README is candid that the endpoint “works by converting Responses request into Chat Completions request,” so the translation still happens. It just happens inside llama.cpp instead of in a box you installed.

If you want Claude or Gemini inside Codex with your own API keys, this class of tool works, and opencodex handles the parts that are easy to get wrong: caching survives, the identity line gets fixed, and the SSRF guard is on before you ask for it.

If your plan is to put a subscription behind it, that’s a different decision. The project warns on its own front page that Anthropic may suspend accounts for routing through a proxy at all — before you even get to the headers. Nobody has ruled on those. The mimicry is the part we’d weigh hardest, not because we’ve seen anyone banned but because it was built deliberately. That’s an informed risk, not a hidden one. Take it knowingly or use an API key.

The number we’d keep in mind either way is 113 bytes out of 51,731. Whatever you route it to, most of what you’re paying for was never your prompt. That’s the same arithmetic behind where your token spend actually goes.


Tested September 6, 2026 on macOS 26.6.2 with Node v26.3.0, Codex CLI 0.153.4 and opencodex 2.43.0. Wire captures were taken against a local recording server, not a paid provider; no real subscription credentials were used. Source claims are pinned to openai/codex at commit ac192cd. The photographs on this page are illustrations, not screenshots of the run — every measurement is in the tables and code blocks.

  • codex-cli
  • openai
  • anthropic
  • proxy
  • tokens
  • privacy

Frequently asked questions

Can Codex CLI use Claude or Gemini instead of GPT?

Yes. Codex's config takes a [model_providers.<id>] block with its own base_url, so aiming it elsewhere is native and documented. What isn't native is the protocol: Codex speaks only the Responses API, so any provider that doesn't needs a translating proxy on the other end. In our September 2026 run with opencodex 2.43.0, a Codex CLI 0.153.4 request came out the far side as a valid Anthropic POST /v1/messages.

Why does my Codex config say wire_api = "chat" is no longer supported?

Because Codex dropped Chat Completions. In the openai/codex source, the WireApi enum has exactly one variant, Responses, and deserializing "chat" returns an error pointing at discussion 7782, "Deprecating chat/completions support in Codex". Any guide that tells you to set wire_api = "chat" predates the removal.

Does a proxy break Anthropic prompt caching?

It does not have to, and opencodex does not. We expected broken caching and were wrong: the adapter places four cache_control breakpoints ordered by stability — tools, system, penultimate user message, last user message — and we confirmed all four on the wire. It also respects breakpoints the client already set and demotes a 1h TTL that follows a 5m one.

Is it against the terms of service to route a subscription through a proxy?

We cannot give you a legal answer, and we did not test a real subscription. What we can report is what the project says itself: its README warns that some providers, "notably Anthropic (Claude)", may suspend or restrict accounts that route API traffic through third-party proxies, and tells you to check the provider terms first. Its source comments separately describe revealing the proxy to the upstream as "a likely ToS trigger". That is the maintainers characterizing the risk, not us.