provider-openai-codex
v0.3.1OpenAI Codex (ChatGPT subscription) provider; implements provider::openai-codex::stream and provider::openai-codex::refresh_models behind llm-router, sourcing credentials from the auth-credentials vault.
- macOS: arm64 · x64
- Linux: arm64 · armv7 · x64
- Windows: arm64 · x64 · x86
exact versions are immutable; binary and bundle artifacts are digest-pinned.
readme
open as markdownprovider-openai-codex
OpenAI Codex (ChatGPT subscription) provider worker behind llm-router. It lets the stack generate against a ChatGPT/Codex subscription (billed to the plan, "Sign in with ChatGPT") instead of a pay-per-token API key, by speaking OpenAI's Responses API at the Codex backend.
Implements the provider protocol from tech-specs/2026-06-agentic/llm-router.md:
provider::openai-codex::stream (Responses SSE → AssistantMessageEvent frames
into a router-owned channel) and provider::openai-codex::refresh_models
(fetches and reconciles the authenticated Codex model catalog).
⚠️ Terms-of-service caveat — local/personal dev only. This drives a personal ChatGPT subscription through the undocumented
https://chatgpt.com/backend-api/codexbackend with a Codex-clientoriginatorheader. That is plausibly against OpenAI's terms and may risk account action; the backend is unversioned and can change without notice. Use it for local, personal development. For team/CI/production, use official API-key billing (provider-openai). You assume the risk.
Credentials
This worker is a dumb token consumer — login and refresh live out-of-band:
- Vault (intended authority): the
auth-credentialsworker (auth::get_token), populated by theoauth-openai-codex"Sign in with ChatGPT" flow. A near-expiry token triggers the vault-owned refresh (oauth::openai-codex::refresh); this provider never calls the OAuth endpoints itself. - Local dev fallback: when no vault is running, the worker reads
${CODEX_HOME:-$HOME/.codex}/auth.jsondirectly (read-only — thecodexCLI owns that file's refresh). Requires host access to that path, so it does not apply to sandboxed/microVM-managed workers. On boot the worker also does a one-time, read-only import of that file into the vault when the vault is present but empty (never written back).
API-key credentials are rejected — they belong on provider-openai under
provider id openai.
Behavior
- Registration: self-declares via
router::provider::registerwith backoff, and re-declares on therouter::readytrigger. It advertises dynamic model listing andcredential_env_var: None; identity binds via theregistration_tokenpersisted in iii-state (scopeprovider-openai-codex). - Models: fetches the account-scoped Codex catalog from authenticated
GET /backend-api/codex/models?client_version=…at startup, on explicit refresh, after router readiness, and every three minutes. Picker-visible results become namespaced router ids (codex/). Each successful non-empty response replaces the complete provider slice, adding new models and removing retired ones. Failed or empty refreshes preserve the router's persisted last-known-good slice. Namespacing preventsAmbiguousModelcollisions withprovider-openai. - Request: Responses API —
inputitems,stream: true,store: false, optionaltoolsandreasoning: { effort }. Headers:Authorization: Bearer,chatgpt-account-id, Codex compatibilityversion,openai-beta: responses=experimental,originator: codex_cli_rs. - SSE:
response.output_text.delta→ text,response.reasoning_*→ thinking,response.function_call_arguments.delta→ tool calls,response.completed→ usage + terminal. Unknown event types are ignored (forward-compat). - Liveness / errors:
pingat least every 30s of silence; 401/403 →auth_expired, 429 →rate_limited,context_length_exceeded→context_overflow, 5xx/network →transient, other 4xx →permanent. The router owns retry policy.
Running
Standard worker CLI: --url (engine WebSocket, default ws://127.0.0.1:49134,
or III_WS_URL), --manifest (print the registry manifest and exit), --config
(accepted but ignored — this worker has no file-based config).
cargo run -- --url ws://127.0.0.1:49134Tests
cargo test # unit modules, model-discovery/upstream TCP stubs, schema goldensRegenerate the wire-schema goldens with UPDATE_GOLDENS=1 cargo test.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
not configured: sign in with ChatGPT … |
no vault credential and no readable ~/.codex/auth.json |
run the oauth-openai-codex sign-in, or codex login so ~/.codex/auth.json exists |
| local fallback is used on each request | auth-credentials vault not running |
start the vault for shared/refreshing credentials, or keep relying on the local ~/.codex/auth.json fallback |
requires a ChatGPT OAuth login … API keys belong on provider-openai |
credential is an API key | this provider is OAuth-only; use provider-openai for keys |
missing ChatGPT account id |
token lacks the account claim | sign in again with a ChatGPT account |
backend Unsupported parameter / shape errors |
Codex backend contract drifted | update this worker's request/SSE mapping against the current backend |
| model refresh fails or returns no visible models | auth/network/backend catalog problem | the last known catalog is retained; fix the underlying error and call provider::openai-codex::refresh_models |
| model routes ambiguously | a codex/* id collided with another provider |
keep codex ids namespaced; or pin provider: "openai-codex" |