Codex Integration
opencodex makes Codex route through the proxy by editing two things Codex reads: its config
($CODEX_HOME/config.toml, default ~/.codex/config.toml) and its model catalog. Every edit is
idempotent and reversible.
The Integrations overview has a Codex switch for this native integration. Its switch shows the desired state from OpenCodex’s configuration, while the badge reports whether Codex is currently observed using the proxy; during cleanup those can briefly differ while the badge continues to report the observed state. Disabling names the effective Codex config file, removes OpenCodex’s generated routing artifacts, and leaves the proxy running for other clients. Re-enabling rebuilds the catalog from the models available at that time, so it does not restore the Codex files byte for byte.
The proxy exposes one bare openai Codex-login route with Pool(default) and Direct account modes,
plus openai-apikey/<model> for the configured API key. Pool includes main plus added accounts;
Direct uses only the caller/main bearer. The routes do not fall back to one another. Shipped v1
configs migrate to marker 2 and preserve config.json.pre-openai-tiers-v2.bak for manual restore.
Config injection
Section titled “Config injection”ocx init, ocx start, and ocx sync call the injector. On the default loopback bind, it keeps
Codex’s built-in openai provider id and points that provider at opencodex:
# root keys, before the first tablemodel_catalog_json = "/absolute/path/to/opencodex-catalog.json"# Auto-injected by opencodexopenai_base_url = "http://127.0.0.1:10100/v1"# Auto-injected by opencodexexperimental_realtime_ws_base_url = "http://127.0.0.1:10100/v1"
# only when fastMode is set; unset adds no [features] table[features]fast_mode = trueThe second key is the voice sideband override. Codex creates a WebRTC voice call through
openai_base_url, but since codex 0.146 (openai/codex#35830) it joins that call’s sideband
WebSocket at api.openai.com directly unless experimental_realtime_ws_base_url redirects it. In
Pool mode the call is created under the account opencodex selects, so a direct join under the app’s
own login fails with realtime websocket handshake failed (404). The injected key sends the join
back through opencodex (GET /v1/live/{callId}), where the Pool reuses the account it bound to that
session/thread pair (a process-local binding). In Direct mode both legs already use the caller’s
current bearer, so the key only keeps the join on the proxy path. It is written only on the loopback
openai_base_url form, is removed together with it, and a user-owned
experimental_realtime_ws_base_url is never overwritten.
The injected fast_mode follows the tri-state fastMode setting: true writes fast_mode = true,
false writes fast_mode = false, and unset leaves an existing fast_mode untouched without
adding a [features] table.
The proxy listens on port 10100 by default and serves POST /v1/responses,
POST /v1/responses/compact, POST /v1/images/generations, POST /v1/images/edits,
GET /v1/models, GET /healthz, and the /api/* management surface.
Built-in image generation (image_gen)
Section titled “Built-in image generation (image_gen)”Codex’s built-in image_gen tool does not go through /v1/responses — the codex-rs extension
POSTs {base_url}/images/generations (or /images/edits when reference images are attached)
directly, with the same ChatGPT bearer auth it uses for chat. Because the injected base_url
points at opencodex, the proxy relays those calls to the OpenAI upstream.
This is separate from the Image Bridge, which only activates when a
Responses turn lists the hosted image_generation tool while a non-OpenAI model is selected.
Standalone /images/generations calls never enter that bridge.
- One mode-aware forward candidate: Pool selects an eligible main/added account; Direct uses the caller OAuth bearer. The configured mode applies consistently to the image request.
- OpenAI API-key provider: it is used only when no forward candidate owns an authentication failure. A broken/expired Pool credential is never hidden behind separately billed API usage.
- Explicit custom provider: set
images.providerto the id of a custom API-keyopenai-responsesprovider whose endpoint implements the OpenAI Images API. Explicit selection fails closed and never falls back to a different paid upstream. Registry-managed provider ids are not accepted here; omitimages.providerto use the built-in OpenAI tiers. - xAI Imagine (Grok OAuth) relay: when
images.bridgeEnabledistrue,images.provideris omitted, and anxaiprovider is configured,/v1/images/generationsand/v1/images/editsare sent tohttps://api.x.ai/v1. The credential depends on the provider’sauthMode: with"oauth"the relay reuses the Grok CLI grant fromocx login xai; with any other mode it uses the provider’s API key. An OAuth login does not arm a keyed provider, and vice versa. ChatGPT credentials are not forwarded. If the credential is missing, the proxy returns 400 instead of billing ChatGPT. Settingimages.providerexplicitly hands/v1/imagesto that provider; its own validation errors are returned as-is and the xAI relay is never tried. The relay maps Codexsize/aspect_ratioonto xAI’s Imagine body and returns the same{created, data:[{b64_json}]}shape. Combined decoded bytes and base64-encoded output across the batch (inlineb64_jsonand downloaded URLs) stay under 100 MiB; a batch that would exceed that cap returns 502. When xAI returns an image URL instead of inline bytes, the proxy fetches it itself with no credential: the URL must be public HTTPS (no redirects, nofile:, no loopback or private addresses), each download is capped at 50 MiB, and the result is materialized as a local artifact that is served only through the authenticated management endpoint. This is independent of the Responses Image Bridge loop (which remains API-key-only). - Google Antigravity (CCA) fallback: when neither an OpenAI forward candidate nor a keyed
provider is configured,
/v1/images/generations(not/images/edits) falls back to the Antigravity Cloud Code Assist endpoint using thegemini-3.1-flash-imagemodel. The fallback also fires after OpenAI auth resolution fails (e.g. an expired or missing ChatGPT credential), not only when no OpenAI candidate is configured. This requiresocx login google-antigravity; the OAuth token is sent only to the pinned CCA registry host, never to a config-levelbaseUrloverride. The response is returned in the same{created, data:[{b64_json}]}shape Codex expects. - Neither: the proxy returns a clear error instead of a generic 404. Routed providers
(Cursor, Gemini, Kiro, …) cannot serve the
image_generationtool relay; if you don’t want the tool offered at all, disable it in Codex withcodex features disable image_generation([features] image_generation = falseinconfig.toml).
The tool declaration still travels with the model’s Responses request. For API-key Responses
providers, opencodex lowers Codex’s private image_gen namespace to an upstream-safe
image_gen__<inner-name> alias (for example image_gen__imagegen). When that usable alias replaces
the client declaration, opencodex removes a duplicate hosted image_generation declaration. It maps
the function call to the explicit image_gen namespace before Codex sees it, and encodes the native
call again when later history is replayed upstream. This keeps client-side image generation callable
on public-compatible upstreams that reserve the namespace or reject dotted function names. ChatGPT
forward mode remains untouched and keeps its native Responses Lite shape.
For an OpenAI-compatible custom gateway, configure a dedicated provider and select it only for standalone Images requests:
{ "providers": { "custom-images": { "adapter": "openai-responses", "baseUrl": "https://gateway.example.com/v1", "authMode": "key", "apiKey": "${IMAGE_GATEWAY_API_KEY}" } }, "images": { "provider": "custom-images", "timeoutMs": 300000 }}The custom endpoint must accept POST /v1/images/generations and /v1/images/edits and return the
OpenAI Images response shape expected by Codex. The provider’s configured key replaces any caller
bearer before the upstream request.
Note: This refers only to the Codex
image_generationtool (/images/generationsrelay). Gemini models that are image-capable produce inline images natively through theresponseModalities: ["TEXT", "IMAGE"]), independent of this relay — see Adapters.
For a non-loopback hostname, Codex must send the generated API auth header. The injector therefore
uses a dedicated provider instead:
# root keysmodel_provider = "opencodex"model_catalog_json = "/absolute/path/to/opencodex-catalog.json"
# appended at the end of the file# Auto-injected by opencodex[model_providers.opencodex]name = "OpenCodex Proxy"base_url = "http://your-host:10100/v1"wire_api = "responses"requires_openai_auth = trueenv_key = "OPENCODEX_API_AUTH_TOKEN"# supports_websockets = true # only when config.websockets is trueWhen OpenCodex owns routing, both modes write $CODEX_HOME/opencodex.config.toml as a
reference/fallback config. On loopback it contains the root keys you can merge manually if automatic
injection was removed; on non-loopback it contains the dedicated provider form. External-provider
mode leaves this profile untouched.
Shared model catalog
Section titled “Shared model catalog”Codex CLI, TUI, App, and SDK all read the same Codex home. opencodex resolves that directory from
CODEX_HOME, falling back to ~/.codex, and manages:
$CODEX_HOME/config.toml$CODEX_HOME/opencodex.config.toml$CODEX_HOME/opencodex-catalog.json$CODEX_HOME/models_cache.jsonOn WSL, if CODEX_HOME is unset and the Linux ~/.codex/config.toml is absent, opencodex also
checks for a single Windows Codex Desktop home at /mnt/c/Users/*/.codex/config.toml. When exactly
one candidate exists, it uses that directory so WSL app-server mode and Windows Codex Desktop share
the same config and auth files. Set CODEX_HOME explicitly to override this detection.
Codex can keep SQLite-backed thread state in a separate directory. OpenCodex history operations use
the same precedence as Codex: root sqlite_home in config.toml, then CODEX_SQLITE_HOME, then the
effective CODEX_HOME. Relative SQLite homes resolve from the current working directory. When an
explicit CODEX_SQLITE_HOME is present during service installation or repair, the durable launcher
stores its install-time absolute path so the background proxy continues to address the same database.
If config.toml or its root sqlite_home key is absent, OpenCodex continues to the
environment/home fallback. If the file cannot be read or parsed, or the key is present but blank or
not a string, SQLite-home resolution stops instead of risking history operations against a different
database.
On Windows, an Orca shell can set both CODEX_HOME and ORCA_CODEX_HOME to Orca’s bundled runtime
home while the ChatGPT/Codex app still reads %USERPROFILE%\\.codex. ocx status and ocx doctor
warn about this exact mismatch and print redacted target paths. If a background service was installed
from that Orca shell, uninstall it from the original shell first, then set CODEX_HOME to the app
home, unset ORCA_CODEX_HOME, rerun sync/restore, and install the service again.
In dedicated-provider mode, requires_openai_auth = true keeps Codex App/TUI account-gated surfaces
aligned with native Codex. opencodex also serves /v1/responses over WebSocket. The dedicated
provider advertises supports_websockets = true only when "websockets": true; on loopback Codex’s
built-in provider may try WebSocket first, and a disabled proxy returns 426 so Codex falls back to
HTTP/SSE.
Authless Codex Desktop (opt-in)
Section titled “Authless Codex Desktop (opt-in)”In Dashboard → Overview, Open Codex without signing in controls this existing
opt-in preference. The switch defaults to off when the setting is absent or false;
an existing explicit codexDesktopAuthless: true stays enabled. The dashboard saves
the preference and runs a full sync. Restart Codex Desktop after changing it.
If synchronization fails, the saved preference remains and the dashboard shows the error;
retry Sync before restarting. Account-gated Desktop features may be unavailable
when enabled. Upstream credentials, local eligibility, remote admission authentication
and user-owned gateway settings retain their existing requirements.
Codex Desktop shows its ChatGPT login screen whenever the active provider requires OpenAI auth. If
your OpenCodex setup never uses ChatGPT credentials (routed providers only, or a blocked
chatgpt.com), you can opt out of that gate:
ocx system settings --desktop-authless on # or "codexDesktopAuthless": true in config.jsonocx sync # rewrites ~/.codex/config.toml; restart DesktopWith the switch on, a loopback bind injects the dedicated provider form instead of the root
openai_base_url override:
model_provider = "opencodex"
[model_providers.opencodex]name = "OpenCodex Proxy"base_url = "http://127.0.0.1:10100/v1"wire_api = "responses"requires_openai_auth = falseDesktop then starts without a login and routes every turn through the proxy. The setting survives
ocx start, restart, ocx sync, and ocx ensure; turning it off (--desktop-authless off) makes the
next sync restore the default loopback form, and ocx restore strips it like any other injected
routing. What to expect while it is on:
- ChatGPT-gated Desktop chrome (account, usage, Fast mode) stays dark: Codex derives those surfaces from the provider’s auth requirement.
- New threads are tagged with the
opencodexprovider, as on a non-loopback bind, and history is handled the same way. - Desktop releases that filter the model picker against a native-only allowlist may show an empty
or
Custompicker in this mode as well; requests still use the configured model. Setmodel = "<provider>/<id>"inconfig.tomlas described in Desktop remote servers.
This only changes the Desktop login gate. Non-loopback binds keep requires_openai_auth = true and
the env_key admission credential regardless of the switch; it never exposes an OpenCodex listener
without authentication.
Thread identity and history
Section titled “Thread identity and history”The default loopback form keeps new threads tagged with Codex’s native openai provider, so normal
resume history needs no remapping. Sync and restore apply only a matching backup manifest and
restore each thread’s exact original provider, source, and event marker. A bare opencodex row with
no manifest is left unchanged; use ocx recover-history --legacy-openai --yes only when you explicitly
intend to force that legacy relabel. The command is intentionally broad: it rewrites every thread
with a user message currently tagged opencodex to openai, normalizes exec to cli, and sets
the event marker—including legitimate dedicated-provider history. Back up the state and use it only
when that full scope is intended. Non-loopback dedicated-provider mode still mirrors history
under the opencodex provider while active and restores the backed-up metadata on exit. Set
syncResumeHistory: false to leave history untouched.
Model catalog sync
Section titled “Model catalog sync”Codex shows models from an on-disk catalog ($CODEX_HOME/opencodex-catalog.json by default). On
start and on ocx sync, opencodex:
- Backs up the pristine catalog once to
~/.opencodex/catalog-backup.json(so featuring is reversible). - Fetches eligible providers’ live model catalogs (cached ~5 min; falls back to the last good
list, then configured
models[]). Forward auth has no model endpoint, and Cursor uses itsGetUsableModelsRPC rather than/models. - Merges routed models in as namespaced entries (
provider/model), cloned from a native Codex catalog template so Codex’s strict parser accepts them. - Filters
config.disabledModelsand each provider’s non-emptyselectedModelsallowlist. - Re-ranks so featured models sort first (see below), then writes the merged catalog back.
Routed catalog entries also get their GPT-5 identity rewritten to the real upstream model name.
Reasoning controls come from provider/model metadata across Codex’s low | medium | high | xhigh | max | ultra ladder; unsupported values are mapped or clamped before the upstream request.
Coordinator diagnosis and recovery
Section titled “Coordinator diagnosis and recovery”Native config/history writes use a per-user SQLite coordinator keyed by the canonical CODEX_HOME.
If a process terminates in SQLite’s initial creation window, a zero-byte coordinator can remain even
though it contains no authoritative transition row. ocx doctor reports the exact coordinator path
and distinguishes zero-byte, unversioned, rowless, valid, unsafe, and unreadable states without
creating SQLite sidecars. Automatic sync tolerates only an identity-stable zero-byte file that has
settled for at least one second and whose immutable SQLite snapshot has version zero with no tables;
a newly created zero-byte file remains on the locked coordinator path.
For a state that doctor proves is a zero-byte creation remnant, stop the OpenCodex proxy/service and run:
ocx doctor --recover-zero-byte-coordinator --yesocx syncRecovery moves the still-identical zero-byte file to a same-directory .zero-byte-backup-* path;
it does not delete the evidence or adopt legacy routed state. It refuses a running proxy, lock
contention, symlinks/reparse points, foreign ownership, changed files, every non-empty database,
and any coordinator that already has an authoritative row. Desktop renderer filtering is a
separate layer: a correct catalog and coordinator do not by themselves bypass the Codex App model
allowlist.
Routed local tools
Section titled “Routed local tools”Non-native routed catalog rows use tool_mode: "code_mode_only". This lets Codex expose its official
exec entrypoint and nested MCP tools, including Browser and Computer Use, while opencodex routes
only the model’s ordinary function call. Tool execution, permissions, and confirmations remain
local to Codex; opencodex does not implement a second browser or desktop-control executor.
For key-auth Responses providers that do not accept Codex’s exec custom-tool grammar, opencodex
encodes that declaration and its history as an upstream function tool, then restores the streamed
function-call lifecycle to custom_tool_call before Codex sees it. Native OpenAI forward routing
and the supported apply_patch custom tool stay unchanged.
If a routed model sends a complete patch as the entire code-mode exec input, opencodex
converts it to the nested tools.apply_patch call before the tool-completion events reach
Codex. Native custom calls and converted function calls use the same completion rule;
patch previews are held while their executable form is unresolved. JavaScript that merely
contains patch text and unrelated native custom payloads stay unchanged.
Routed code-mode turns are also told the host’s rules for the nested helpers before the first
call: tools.apply_patch takes one string that opens and closes with the bare patch marker lines,
the isolate has no import, and long-running commands are polled through write_stdin. When a
code-mode exec result on the native routed Responses, Kiro, or Cursor path still carries one of the host’s
failure messages, opencodex appends a one-line hint naming the rule. This change does not rewrite
the model’s code or its patch text.
Ordinary routed Responses function calls also use the original declared parameter schema at
completion: integral floats in integer fields and integral numbers in string-only fields are
normalized, while fractions and numeric unions stay unchanged. An explicitly empty completed
argument string becomes {}. Final events and locally stored continuation history agree.
Unambiguous dotted namespace spellings are restored to the declared namespace and tool name.
The selected provider must support function/tool calling. A text-only provider without tool-call
support cannot use exec, Browser, or Computer Use. Native OpenAI rows keep their upstream tool
mode unchanged.
After ocx sync changes this metadata, restart Codex App and open a fresh task. Existing app-server
processes and tasks may retain the catalog and tool plan they loaded at startup.
Custom model display names
Section titled “Custom model display names”A custom model can carry a human-readable display name that overrides the label Codex shows in
its model picker, without changing anything about how the model is routed. The display name maps to
the catalog entry’s display_name field only — the routing slug (<provider>/<model>), alias
collision order, provider, and native OpenAI marketing names are all left untouched.
Add a display name from the CLI (the proxy syncs the catalog right away when live):
ocx models add deepseek deepseek-v4 --display-name "DeepSeek V4" --context-window 128000Remote Codex clients can fetch the same generated catalog with an ordinary data-plane key
— the same credential they already use for /v1/responses, not an admin token:
dest="${CODEX_HOME:-$HOME/.codex}/opencodex-catalog.json"tmp="$(mktemp "${dest}.XXXXXX")"curl -fsS -H "x-opencodex-api-key: $OPENCODEX_API_AUTH_TOKEN" \ "https://proxy.example.com/v1/catalog" > "$tmp" \ && mv "$tmp" "$dest"ocx sync-cacheThe response is the raw opencodex-catalog.json document (no provider credentials). When
available, the x-opencodex-codex-version header reports the Codex runtime version on the
server so clients can spot version skew.
GET /v1/catalog exists so that reading a list of models does not cost an admin token. It is
read-only (GET and HEAD), accepts x-opencodex-api-key, a bearer token, or
x-api-key, and serves exactly the same bytes as the management route. Responses carry a
strong ETag — pass it back as If-None-Match to re-validate and get a 304 instead of the
full document — and Cache-Control: private, no-cache, since the body sits behind a
credential.
A data-plane key admitted here gains nothing on the management plane: /api/catalog and
every other /api/* route still require the admin token or a dashboard session. The older
/api/catalog route keeps working unchanged for the dashboard and for scripts that already
hold an admin token.
You can also set or edit it through the management API (POST /api/custom-models,
PUT /api/custom-models/<id> with a displayName string) and the web dashboard. A / is rejected
because it would collide with the routed-slug separator.
The display name is display-only and stable across regeneration. Every ocx sync and catalog
refresh re-derives routed entries from config.json (including customModels), so the configured
name is reapplied instead of drifting back to the routed slug. A managed service restart also attempts
this sync shortly after the proxy binds. If that best-effort boot sync fails, for example during an
offline login, the previously persisted catalog is retained and the next successful ocx sync
reapplies the configured name. Genuine upstream native names (e.g. gpt-5.6-sol →
“GPT-5.6-Sol”) come from the pinned upstream snapshot and are never overridden by a custom display
name.
External provider managers
Section titled “External provider managers”If config.toml already selects a provider other than openai or opencodex, OpenCodex leaves the
file unchanged and skips profile writes, catalog/cache refresh, and both immediate and background
Codex history metadata restoration. Tools that manage a custom provider often tag existing sessions with that
provider id; replacing the active id can make those intact sessions disappear from Codex’s history
view. The same protection applies to an external provider selected by a legacy root profile.
Keep one tool as the owner of Codex provider configuration. To use OpenCodex behind an existing
provider manager, point that provider at http://127.0.0.1:10100/v1 with Responses passthrough
(wire_api = "responses" in Codex TOML), not Chat Completions translation. When proxy API auth is
enabled, also pass x-opencodex-api-key from OPENCODEX_API_AUTH_TOKEN, matching the non-loopback
provider form above. To let OpenCodex inject routing directly, first switch Codex back to its
built-in openai provider and remove any user-owned root openai_base_url, then rerun ocx start.
Explicit tool_search troubleshooting
Section titled “Explicit tool_search troubleshooting”Routed local tooling has two distinct discovery paths. In normal routed code mode, Codex can expose
deferred MCP/app tools through the official exec tool’s tools global and ALL_TOOLS; that path
does not require the model to see or call tool_search.
Separately, tool_search is a client-executed Codex discovery surface. It is not an OpenCodex
feature flag, and an upstream tool_choice: "auto" value does not create or enable it. OpenCodex can
relay the explicit surface only when Codex already included a declaration like this in the incoming
Responses request:
{ "tools": [ { "type": "tool_search", "description": "Load deferred tools" } ]}For routed chat/local models, OpenCodex exposes that declaration as a normal function named
tool_search. If the model calls it, OpenCodex converts the call back to a Responses
tool_search_call; Codex executes the search and supplies the resulting tool definitions in a
later tool_search_output. Definitions loaded that way are then available on the next model turn.
Check the failure boundary before changing provider settings:
- No
type: "tool_search"in the incoming request: the active Codex client/session did not advertise the explicittool_searchsurface. OpenCodex cannot invent that declaration. This does not mean normal code-mode tools are unavailable: check whether the routed model can useexecand discover the needed nested tool throughtools/ALL_TOOLSfirst. - The incoming declaration exists, but no
tool_searchfunction reaches the routed request: capture only the redacted tool-type/name list and open an OpenCodex bug. Never attach the bearer, account id, conversation input, full headers, or complete request body. - The routed request contains
tool_search, but the local model never calls it: the relay is working. Use a model/template with reliable function calling and instructions that explicitly tell it to search for a deferred tool it needs. LM Studio’stool_choice: "auto"permits tool use; it does not force the model to call this function. - A call is emitted repeatedly or loaded tools never become usable: capture the redacted
tool_search_call/tool_search_outputitem types and call ids. OpenCodex preserves both in history so the model should see the completed search instead of issuing it forever.
See The parser and bridge for the explicit wire mapping.
There is no provider-level setting that can add a missing tool_search declaration; ordinary
code-mode discovery remains a separate path.
Catalog troubleshooting
Section titled “Catalog troubleshooting”If a model is missing from Codex, or the catalog order/visibility looks wrong, check in order:
selectedModelson the provider — a non-empty allowlist exposes only those ids to Codex; empty or omitted exposes all discovered models. An id not in the allowlist never reaches the catalog.disabledModels(top level) — hides models from both the catalog and/v1/models, and flips bare native GPT slugs tovisibility: "hide".liveModels: false— WithliveModels: false, an empty or omittedmodelslist seeds the configureddefaultModelfirst, followed byretainModels; duplicate ids are removed while preserving first occurrence. A nonempty explicitmodelslist instead seedsmodelsfollowed byretainModels, without implicitly adding a differentdefaultModel. That default can still be listed explicitly inmodelsorretainModels. If none of these fields supplies an id, the static seed is empty. This is seed order, not a promise of final picker order.selectedModels,disabledModelsand provider-disabled policy still apply.authMode: "forward"keeps its separate branch and does not use this routed static seed. These rules do not change live-discovery failure fallback.- Cursor
GetUsableModels— the Cursor adapter discovers models through its protobufGetUsableModelsRPC, not/models, so a Cursor-side change can alter which ids are visible independently of other providers. - Cache and
ocx sync— live catalogs are cached for about five minutes (modelCacheTtlMs, default300000). Runocx syncto force a fresh fetch and rewrite the catalog immediately. - Running Codex
app-server— rewriting the on-disk catalog is not enough while a long-lived Codexapp-server(Desktop / CLI background host) keeps the previous list in memory.ocx syncandocx sync-cachewarn when those processes are detected. Restart them withocx sync --restart-codex(or stop the matchingapp-serverprocesses yourself), then let Codex recreate them so the new list appears.
Proxy connection errors
Section titled “Proxy connection errors”If Codex retries and then fails with an error like
stream disconnected before completion: error sending request for url (http://127.0.0.1:10100/v1/responses)
— or Claude Code reports a similar connection failure — the opencodex proxy is not
running: nothing is listening on the configured port, so the client renders that raw
connection error itself. Restart the proxy:
ocx start # foregroundocx service install # persistent: auto-starts on login and respawns on crashocx status shows whether the proxy is running and prints the same restart hint when
it is not; ocx doctor reports restart safety (service/shim coverage).
Routed models during Codex reserve mode
Section titled “Routed models during Codex reserve mode”When the ChatGPT 5-hour quota is exhausted, Codex may offer a reserve fallback model
(gpt-reserve / Luna Reserve). While that state is active, the Codex model picker can make
every other entry unselectable — including opencodex routed models, even though those
run on independent providers and credentials and consume none of the exhausted quota.
This is a Codex client behavior and the proxy cannot change it. The reserve state
arrives from the ChatGPT backend on the client’s own authenticated connection, not through
the proxy. The desktop app polls backend-api/wham/usage and treats reserve as active when
the response carries rate_limit_upsell.banner_type = "luna_reserve", the primary
rate_limit.allowed is false, and additional_rate_limits[] contains an entry with
limit_name = "gpt-reserve" that is still allowed. While that holds, the app forces the
conversation’s model setting to gpt-reserve and rewrites any other pick back to it — the
picker is collapsed to the reserve entry by the client, and a model = value in
config.toml is overridden the same way. None of this consults the model catalog, so no
representation on our side participates in the decision. opencodex has no reserve concept to
adjust, and the alternative — misreporting your own quota back to your own client — would be
a worse bug than the one it papered over.
Workaround: the models themselves stay fully usable; only the Codex app’s model selection is gated. Reach them from a client that does not consult the ChatGPT usage snapshot:
- Claude Code through the proxy (
ocx claude). - Any HTTP client against the local
/v1endpoint. - The dashboard’s own request paths.
Normal picker behavior returns when the 5-hour window resets.
The subagent picker
Section titled “The subagent picker”Catalog sync makes the selected sub-agent models available to Codex; see Codex App model picker for picker ordering and Sub-agent Surface for v1/base/v2 delegation and fallback behavior.
Codex account warmup
Section titled “Codex account warmup”When a ChatGPT account is added to the Codex account pool, opencodex verifies it before persistence
with a small streaming request to the Codex Responses backend. The request uses a real Responses
item array (input: [{ type: "message", ... }]), waits for response.completed, and defaults to
gpt-5.4-mini. If that model returns HTTP 400, it retries with gpt-5.5; structured upstream error
details are surfaced without exposing raw response bodies. Background revalidation is separate and
off by default; it runs only when Token Guardian is enabled, the chatgpt refresh policy is
proactive, and tokenGuardian.codexWarmupEnabled is true.
Restoring native Codex
Section titled “Restoring native Codex”opencodex never traps you. ocx stop is the single command that fully reverts to native Codex — it
stops the proxy, stops the background service if one is installed, and strips every injected line and
routed catalog entry so plain codex works exactly as if opencodex was never there:
ocx stop # stop the proxy + service, restore native Codexocx restore # restore without stopping (alias: ocx eject)ocx restore back # point plain Codex at the running proxy againWhen opencodex runs as a managed background service, it sets
OCX_SERVICE=1 so a service-driven restart does not thrash the Codex config — only an explicit
ocx stop / ocx service stop restores native Codex.
Sub-agent fallback and V2 compatibility
Section titled “Sub-agent fallback and V2 compatibility”In Subagents → Delegation settings, edit the ordered fallback chain and its availability polling interval (5000–600000 ms), then save it separately from the featured roster. A configured target that is no longer advertised remains in the chain until you remove it. The roster and fallback chain are separate settings; this editor does not make the roster replace the fallback policy.
When a routed preferred model may receive V2 work from a native ChatGPT parent, the panel explains the upstream encrypted-task limitation. Readable tasks from routed parents are unaffected. The guidance uses /api/v2 mode and native V1 pin state; the current API does not expose recovery activation or request-specific eligibility, so the panel reports those as unknown. V1/plaintext-compatible delegation remains an alternative. Experimental V2 recovery, where eligible and explicitly enabled, adds quota usage, latency, backend dependence and possible fidelity loss; it does not repair the upstream protocol. See sub-agent surfaces and the upstream limitation.

