Codex Proxy

Your Local Codex Coding Assistant Gateway

Expose Codex Desktop's capabilities as standard OpenAI / Anthropic / Gemini APIs, seamlessly connecting any AI client.

Node.js TypeScript Hono Docker Desktop License

Quick StartFeaturesModelsClient SetupConfigurationAcknowledgements

简体中文 | English


X Issues Donate
--- > **Disclaimer**: This project is independently developed and maintained by a single person — built to scratch my own itch. I have my own account pipeline and am not short on tokens; this project exists because I needed it, not to freeload off anyone. > > I open-source and maintain this voluntarily. Features get added when I need them; bugs get fixed as soon as I find them. But I am under no obligation to serve any individual user's demands. > > Think the code is garbage? Don't use it. Think you can do better? Open a PR and join as a contributor. The issue tracker is for bug reports and suggestions — not feature demands, update nagging, or unsolicited code reviews. --- **Codex Proxy** is a lightweight local gateway that translates the [Codex Desktop](https://openai.com/codex) Responses API into multiple standard protocol endpoints — OpenAI `/v1/chat/completions`, Anthropic `/v1/messages`, Gemini, Codex `/v1/responses` passthrough, and an optional Ollama-compatible `/api/chat` bridge. Use Codex coding models directly in Cursor, Claude Code, Continue, or any compatible client. Just a ChatGPT account (or a third-party API key provider) and this proxy — your own personal AI coding assistant gateway, running locally. ## 🚀 Quick Start ### Desktop App (Easiest) Download the installer from [GitHub Releases](https://github.com/icebear0828/codex-proxy/releases): | Platform | Installer | |----------|-----------| | Windows | `Codex Proxy Setup x.x.x.exe` | | macOS | `Codex Proxy-x.x.x.dmg` | | Linux | `Codex Proxy-x.x.x.AppImage` | Open the app, log in with your ChatGPT account. Dashboard at `http://localhost:8080`. ### Docker ```bash mkdir codex-proxy && cd codex-proxy curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/docker-compose.yml curl -O https://raw.githubusercontent.com/icebear0828/codex-proxy/master/.env.example cp .env.example .env docker compose up -d # Open http://localhost:8080 to log in ``` > Data persists in `data/`. Cross-container access: use host LAN IP (e.g. `192.168.x.x:8080`), not `localhost`. Uncomment Watchtower in `docker-compose.yml` for auto-updates. To enable the Ollama-compatible bridge in Docker, see [Ollama Bridge configuration](#ollama-bridge-configuration). ### From Source ```bash git clone https://github.com/icebear0828/codex-proxy.git cd codex-proxy npm install # Backend dependencies cd web && npm install && cd .. # Frontend dependencies npm run dev # Dev mode (hot reload) # Or: npm run build && npm start # Production mode ``` > **Requires Rust toolchain** (for TLS native addon): > ```bash > curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh > cd native && npm install && npm run build && cd .. > ``` > Docker / desktop app ship pre-built addons — no manual compilation needed. ### Verify After logging in, open the dashboard at `http://localhost:8080` and find your API Key in the **API Configuration** section: ```bash # Replace your-api-key with the key shown in the dashboard curl http://localhost:8080/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}' ``` If you see streaming AI text, the setup is working. If you get 401, double-check the API Key. ## 🌟 Features ### 1. 🔌 Full Protocol Compatibility - Compatible with `/v1/chat/completions` (OpenAI), `/v1/messages` (Anthropic), Gemini, and `/v1/responses` (Codex passthrough) - Optional built-in Ollama-compatible bridge, defaulting to `http://127.0.0.1:11434` - SSE streaming, works with all OpenAI / Anthropic SDKs and clients - Automatic bidirectional translation between all protocols and Codex Responses API - **Structured Outputs** — `response_format` (`json_object` / `json_schema`) and Gemini `responseMimeType` - **Function Calling** — native `function_call` / `tool_calls` across all protocols - **Third-party API keys** — supports OpenAI / Anthropic / Gemini / OpenRouter / custom OpenAI-compatible providers, routed by model. ### 2. 🔐 Account Management & Smart Rotation - **OAuth PKCE login** — one-click browser auth - **Multi-account rotation** — `least_used`, `round_robin`, and `sticky` strategies - **Plan Routing** — accounts on different plans (free/plus/team/business) auto-route to their supported models - **Auto token refresh** — JWT renewed before expiry with exponential backoff - **Passive quota collection** — updates account quota from upstream response headers and WebSocket rate-limit events; `quota.refresh_interval_minutes` only controls local usage snapshots, and `0` disables that timer. - **Ban detection** — upstream 403 auto-marks banned; 401 token invalidation auto-expires and switches account - **API key provider pool** — manage third-party API keys, model lists, import/export, and enable/disable state from the dashboard. - **Web dashboard** — account management, usage stats, batch operations; dashboard login gate for remote access ### 3. 🌐 Proxy Pool - **Per-account proxy routing** — different upstream proxies per account - **Four assignment modes** — Global Default / Direct / Auto / Specific proxy - **Health checks** — scheduled + manual, reports exit IP and latency - **Auto-mark unreachable** — unreachable proxies excluded from rotation ### 4. 🛡️ Anti-Detection & Protocol Impersonation - **Rust Native TLS** — built-in reqwest + rustls native addon, TLS fingerprint matches real Codex Desktop exactly (pinned dependency versions) - **Desktop header replication** — `originator`, `User-Agent`, `x-openai-internal-codex-residency`, `x-codex-turn-state`, `x-client-request-id` headers sent per real client behavior - **Cookie persistence** — automatic Cloudflare cookie capture and replay - **Fingerprint auto-update** — polls Codex Desktop update feed, auto-syncs `app_version` and `build_number` ## 🏗️ Architecture ``` Codex Proxy ┌──────────────────────────────────────────────────────────┐ │ │ │ Client (Cursor / Claude Code / Continue / SDK / ...) │ │ │ │ │ POST /v1/chat/completions (OpenAI) │ │ POST /v1/messages (Anthropic) │ │ POST /v1/responses (Codex passthrough) │ │ POST /gemini/* (Gemini) │ │ │ │ │ ▼ │ │ ┌──────────┐ ┌───────────────┐ ┌──────────────┐ │ │ │ Routes │──▶│ Translation │──▶│ Proxy │ │ │ │ (Hono) │ │ Multi→Codex │ │ Native TLS │ │ │ └──────────┘ └───────────────┘ └──────┬───────┘ │ │ ▲ │ │ │ │ ┌───────────────┐ │ │ │ └──────────│ Translation │◀─────────┘ │ │ │ Codex→Multi │ SSE stream │ │ └───────────────┘ │ │ │ │ ┌──────────┐ ┌───────────────┐ ┌──────────────────┐ │ │ │ Auth │ │ Fingerprint │ │ Model Store │ │ │ │OAuth/API │ │ Rust (rustls) │ │ Static + Dynamic │ │ │ │ API Keys │ │ Headers/UA │ │ Plan Routing │ │ │ └──────────┘ └───────────────┘ └──────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ Rust Native Addon (napi-rs) reqwest 0.12.28 + rustls 0.23.36 (TLS fingerprint = real Codex Desktop) │ ┌──────┴──────┐ ▼ ▼ chatgpt.com 3rd-party providers /backend-api/codex (3rd-party API) ``` ## 📦 Available Models | Model ID | Reasoning | Current context | Max context | Max output | Output | Description | |----------|-----------|-----------------|-------------|------------|--------|-------------| | `gpt-5.5` | low / medium / high / xhigh | 272,000 | 272,000 | 128,000 | text | Frontier model for complex coding, research, and real-world work | | `gpt-5.4` | low / medium / high / xhigh | 272,000 | 1,000,000 | 128,000 | text | Strong model for everyday coding (default) | | `gpt-5.4-mini` | low / medium / high / xhigh | 400,000 | — | 128,000 | text | GPT-5.4 lightweight model | | `gpt-5.3-codex` | low / medium / high / xhigh | 400,000 | — | 128,000 | text | GPT-5.3 coding-optimized model | | `gpt-5.2` | low / medium / high / xhigh | 400,000 | — | 128,000 | text | Professional work & long-running agents | | `gpt-5-codex` | low / medium / high | 400,000 | — | 128,000 | text | GPT-5 coding-optimized model | | `gpt-5-codex-mini` | medium / high | — | — | — | text | Lightweight Codex / CLI coding model | | `gpt-oss-120b` | low / medium / high | 131,072 | — | — | text | Open-source 120B model | | `gpt-oss-20b` | low / medium / high | 131,072 | — | — | text | Open-source 20B model | | `gpt-image-2` | — | — | — | — | image | Image-generation tool backend, invoked via `image_generation` | > **Suffixes**: Append `-fast` to any chat model for Fast mode, `-high`/`-low` for reasoning effort. E.g. `gpt-5.4-fast`, `gpt-5.4-high-fast`. The image model (`gpt-image-2`) does not take suffixes. > > **Plan Routing**: Accounts on different plans auto-route to the models returned for that account by the Codex backend. Do not treat old Plus-only notes as fixed model access rules. Models are dynamically fetched and auto-synced; if a model appears in the Dashboard or `/v1/models/catalog`, it can be used as the request `model`. > > **Dashboard model picker ≠ config file**: Changing the model in the Dashboard only affects the UI display and API examples — it does **not** modify `model.default` in `config/default.yaml` or `data/local.yaml`. The actual model used is determined by the `model` field in each client request (Cursor, Claude Code, etc.). The `model.default` config is only a fallback when the client omits the model field. > > **Max token note**: the table follows the current `config/models.yaml` and Codex runtime `/v1/models/catalog` metadata. `—` means the current catalog does not return that field, not that the model is unavailable. Runtime data fetched from the Codex backend overrides static values and preserves `contextWindow`, `maxContextWindow`, `maxOutputTokens`, and `truncationPolicyLimit`. Request fields such as `context_window`, `max_context_window`, `truncation_policy`, and `max_output_tokens` are not usable switches; forwarding them to the native Codex API returns `400 Unsupported parameter`. ### 🖼️ Image Generation Image generation rides on `/v1/responses` via the built-in `image_generation` tool; the backend is always `gpt-image-2`. **Prerequisite**: a **ChatGPT Plus or higher** account (free accounts have the tool silently stripped by upstream, and the model falls back to replying with an SVG snippet). ```bash curl -N http://localhost:8080/v1/responses \ -H "Authorization: Bearer $PROXY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-5.5", "stream": true, "input": [{"role":"user","content":"Draw a red circle on white background."}], "tools": [{"type":"image_generation","size":"3840x2160"}] }' ``` Tunable fields: `size` (1024×1024 / 1024×1536 / 1536×1024 / 2048×2048 / 2048×3072 / 3072×2048 / 3840×2160 (4K UHD) / `auto`; longest edge ≤ 3840 px, pixel budget ≈ 8 MP), `output_format` (`png` / `jpeg` / `webp`), `output_compression` (jpeg / webp only), `background` (`auto` / `opaque`), `moderation` (`auto` / `low`), `partial_images` (0–3). Upstream forces `model = gpt-image-2` and rejects `n`, `input_image`, `mask`, `input_fidelity`, `style`, `response_format`. See [API.md](./API.md#image_generation-tool) for the full matrix. In the stream, the `image_generation_call` item's `result` field is a base64-encoded image; `revised_prompt` contains the final prompt used by the model. **Edit mode** (with a reference image): include `{"type":"input_image","image_url":"data:image/png;base64,..."}` in the user message `content` array. > The `/v1/chat/completions` compatibility path accepts the `image_generation` tool so OpenAI clients do not fail schema validation, but image payloads are only exposed reliably through `/v1/responses` as `image_generation_call.result`. Use `/v1/responses` when you need the image bytes. ## 🔗 Client Setup > Get your API Key from the dashboard (`http://localhost:8080`). Use a concrete model ID (default `gpt-5.4`) or any [model ID](#-available-models) as the model name. ### Claude Code (CLI) ```bash export ANTHROPIC_BASE_URL=http://localhost:8080 export ANTHROPIC_API_KEY=your-api-key # Switch model: export ANTHROPIC_MODEL=gpt-5.4 / gpt-5.4-fast / gpt-5.4-mini ... claude ``` > Copy env vars from the **Anthropic SDK Setup** card in the dashboard (includes Opus / Sonnet / Haiku tier model config). > > Recommended models: Opus → `gpt-5.5`, Sonnet → `gpt-5.4`, Haiku → `gpt-5.3-codex`. ### Codex CLI `~/.codex/config.toml`: ```toml [model_providers.proxy_codex] name = "Codex Proxy" base_url = "http://localhost:8080/v1" wire_api = "responses" # Inline the API Key (recommended for local single-user setups) [model_providers.proxy_codex.http_headers] Authorization = "Bearer your-api-key" [profiles.default] model = "gpt-5.4" model_provider = "proxy_codex" ``` > 💡 To keep the key out of the config file (shared machine / open-source repo), drop the `http_headers` block and use `env_key = "PROXY_API_KEY"` instead, then `export PROXY_API_KEY=your-api-key && codex`. ### Claude Desktop 1. **Enable Developer Mode**: Click menu **Help** → **Troubleshooting** → **Enable Developer Mode**. 2. **Configure Third-Party Inference**: Click the new **Developer** menu → **Configure Third-Party Inference...**. 3. **Fill in details**: - **Endpoint**: `http://127.0.0.1:8080` - **API Key**: your-api-key - **Model**: `claude-opus-4-7` / `claude-sonnet-4-6` / `claude-haiku-4-5` > Alternatively, edit the config file (usually a JSON file in `%APPDATA%\Claude-3p\configLibrary\` on Windows, or `~/Library/Application Support/Claude-3p/configLibrary/` on Mac), adding the following fields: > ```json > { > "disableDeploymentModeChooser": true, > "inferenceProvider": "gateway", > "inferenceGatewayBaseUrl": "http://127.0.0.1:8080", > "inferenceGatewayApiKey": "your-api-key", > "inferenceGatewayAuthScheme": "bearer", > "inferenceModels": [ > "claude-opus-4-7", > "claude-sonnet-4-6", > "claude-haiku-4-5" > ] > } > ``` Built-in Claude-shaped model names map to Codex models. Put custom mappings in `data/local.yaml`; do not edit `config/models.yaml`: ```yaml model: aliases: claude-opus-4-7: gpt-5.5 claude-sonnet-4-6: gpt-5.4 claude-haiku-4-5: gpt-5.3-codex my-openai: openai:gpt-4o my-deepseek: deepseek-chat ``` The left side is the model name used by the client; the right side is the real upstream model. The target can be a Codex model ID, a provider-prefixed model such as `openai:gpt-4o` / `anthropic:claude-sonnet-4-5` / `gemini:gemini-2.5-pro`, or a model already bound to a custom provider via `model_routing` such as `deepseek-chat`. Aliases appear in `/v1/models`; direct provider requests rewrite the outgoing `model` to the mapped target. > 💡 **Troubleshooting (Windows)**: If Claude Desktop shows `ERR_CONNECTION_REFUSED` when using `127.0.0.1` (and `must use https` when using `localhost`), it means Node.js is only binding to IPv6 by default. Go to the Codex Proxy dashboard settings, change **Host** to `127.0.0.1`, or add `server: { host: "127.0.0.1" }` to `data/local.yaml` and restart the proxy. > > 💡 **LAN Usage Tip**: Claude Desktop strictly validates the endpoint and **only allows** `https://` or exactly `http://127.0.0.1`. If your proxy is on another machine in the LAN (e.g. `192.168.x.x`), you cannot use it directly via HTTP. Workarounds: > 1. **SSH Tunnel (Easiest)**: Run `ssh -L 8080:127.0.0.1:8080 user@192.168.x.x` on your client machine, then use `http://127.0.0.1:8080` in Claude. > 2. **Reverse Proxy**: Setup Caddy or Nginx with a valid HTTPS certificate for your LAN IP. ### Codex Desktop (Official App) The official client shares configuration with the CLI. Restart the app after editing. `~/.codex/config.toml`: ```toml [model_providers.proxy_codex] name = "Codex Proxy" base_url = "http://localhost:8080/v1" wire_api = "responses" [model_providers.proxy_codex.http_headers] Authorization = "Bearer your-api-key" [profiles.default] model = "gpt-5.4" model_provider = "proxy_codex" ``` > 💡 **Why not `env_key`?** macOS/Windows GUI apps do not inherit env vars from your shell rc files — `export PROXY_API_KEY=...` in your terminal is invisible to the GUI process and Codex Desktop will fail with `Missing environment variable`. Inlining `Authorization` via `http_headers` avoids `launchctl setenv` / LaunchAgent gymnastics. Switch back to `env_key = "PROXY_API_KEY"` only when you need the key out of the config file. > > ⚠️ When logged in via "ChatGPT account", existing sessions might bypass this config and hit the official upstream directly. New sessions started after `[model_providers.proxy_codex]` is wired up + `profiles.default.model_provider = "proxy_codex"` will route through the proxy. ### Claude for VSCode / JetBrains Open Claude extension settings → **API Configuration**: - **API Provider**: Anthropic - **Base URL**: `http://localhost:8080` - **API Key**: your API key ### Cursor 1. Settings → Models → OpenAI API 2. **Base URL**: `http://localhost:8080/v1` 3. **API Key**: your API key 4. Add model `gpt-5.4` ### Windsurf 1. Settings → AI Provider → **OpenAI Compatible** 2. **API Base URL**: `http://localhost:8080/v1` 3. **API Key**: your API key 4. **Model**: `gpt-5.4` ### Cline (VSCode Extension) 1. Cline sidebar → gear icon 2. **API Provider**: OpenAI Compatible 3. **Base URL**: `http://localhost:8080/v1` 4. **API Key**: your API key 5. **Model ID**: `gpt-5.4` ### Continue (VSCode Extension) `~/.continue/config.json`: ```json { "models": [{ "title": "Codex", "provider": "openai", "model": "gpt-5.4", "apiBase": "http://localhost:8080/v1", "apiKey": "your-api-key" }] } ``` ### aider ```bash aider --openai-api-base http://localhost:8080/v1 \ --openai-api-key your-api-key \ --model openai/gpt-5.4 ``` ### Cherry Studio 1. Settings → Model Services → Add 2. **Type**: OpenAI 3. **API URL**: `http://localhost:8080/v1` 4. **API Key**: your API key 5. Add model `gpt-5.4` ### Ollama-Compatible Clients Enable it in Dashboard → Settings → **Ollama Bridge**, then use the default Ollama base URL: | Setting | Value | |---------|-------| | Base URL | `http://localhost:11434` | | API Key | Not required; the bridge uses the Codex Proxy key internally | | Model | `gpt-5.4` (or any model ID) | ```bash curl http://localhost:11434/api/tags curl http://localhost:11434/api/chat \ -H "Content-Type: application/json" \ -d '{"model":"gpt-5.4","messages":[{"role":"user","content":"Hello!"}],"stream":true}' ``` > The Ollama API has no authentication. The bridge listens on `127.0.0.1` by default; do not expose it to the public internet or untrusted LANs. ### Any OpenAI-Compatible Client | Setting | Value | |---------|-------| | Base URL | `http://localhost:8080/v1` | | API Key | from dashboard | | Model | `gpt-5.4` (or any model ID) |
SDK examples (Python / Node.js) **Python** ```python from openai import OpenAI client = OpenAI(base_url="http://localhost:8080/v1", api_key="your-api-key") for chunk in client.chat.completions.create( model="gpt-5.4", messages=[{"role": "user", "content": "Hello!"}], stream=True ): print(chunk.choices[0].delta.content or "", end="") ``` **Node.js** ```typescript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "http://localhost:8080/v1", apiKey: "your-api-key" }); const stream = await client.chat.completions.create({ model: "gpt-5.4", messages: [{ role: "user", content: "Hello!" }], stream: true, }); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content || ""); } ```
## ⚙️ Configuration All configuration in `config/default.yaml`: ### CORS Allowed Hosts Configure allowed CORS origins via the `CORS_ALLOWED_HOSTS` environment variable, which maps to the `server.cors` field in the config file. Separate multiple hosts with commas: ```bash export CORS_ALLOWED_HOSTS="example.com,another-domain.com" ``` Or in `data/local.yaml`: ```yaml server: cors: - "https://example.com" - "https://another-domain.com" ``` | Section | Key Settings | Description | |---------|-------------|-------------| | `server` | `host`, `port`, `proxy_api_key` | Listen address and API key | | `api` | `base_url`, `timeout_seconds` | Upstream API URL and timeout | | `client` | `app_version`, `build_number`, `chromium_version` | Codex Desktop version to impersonate | | `model` | `default`, `default_reasoning_effort`, `default_service_tier`, `aliases`, `custom_models`, `inject_desktop_context` | Default model, reasoning config, aliases, and custom catalog entries | | `auth` | `rotation_strategy`, `rate_limit_backoff_seconds` | Rotation strategy and rate limit backoff | | `tls` | `proxy_url`, `force_http11` | TLS proxy and HTTP version | | `quota` | `refresh_interval_minutes`, `warning_thresholds`, `skip_exhausted` | Usage snapshots, threshold config, exhausted-account skipping | | `session` | `ttl_minutes`, `cleanup_interval_minutes` | Dashboard session management | | `ollama` | `enabled`, `host`, `port`, `version`, `disable_vision` | Ollama-compatible bridge | ### Model Aliases `model.aliases` maps client-facing model names to the real upstream model. This is useful when Claude Desktop / Cursor / Continue only lets you pick certain model IDs, or when you want shorter local names. You can also manage aliases in Dashboard → Settings → **Model Aliases**. Saving writes to `data/local.yaml` and hot-reloads the backend, so you do not need to edit `config/default.yaml`. ```yaml model: aliases: claude-opus-4-7: gpt-5.5 sonnet-local: gpt-5.4 openai-fast: openai:gpt-4o deepseek-local: deepseek-chat providers: custom: deepseek: api_key: "sk-..." base_url: "https://api.deepseek.com/v1" models: ["deepseek-chat"] model_routing: deepseek-chat: deepseek ``` Alias resolution runs before `model_routing` and built-in Claude/Gemini auto-routing. Aliases targeting Codex models still work with Codex suffixes such as `-fast` / `-high`; aliases targeting third-party providers rewrite the outgoing direct request `model` field to the mapped target. If you need to add fully custom Codex-compatible model IDs to the catalog, configure `model.custom_models` in `data/local.yaml`. A string entry uses default text/medium metadata; an object entry can define display name, reasoning efforts, context, and output limits: ```yaml model: custom_models: - local-simple - id: local-rich display_name: Local Rich description: Local rich model supported_reasoning_efforts: [low, high] default_reasoning_effort: high input_modalities: [text, image] output_modalities: [text] context_window: 12345 max_context_window: 23456 max_output_tokens: 3456 ``` ### Quota Rotation When `quota.skip_exhausted: true`, the account pool skips accounts whose cached quota is already exhausted before session affinity / `preferredEntryId` is applied. A long conversation therefore cannot force routing back to a cached-exhausted account. The skip condition is currently `rate_limit.limit_reached === true`, `secondary_rate_limit.limit_reached === true`, or `code_review_rate_limit.limit_reached === true` in cached quota. If `used_percent` is merely near 100, for example 99%, but upstream has not set `limit_reached`, the proxy may still use that account. Once upstream returns 429, the account is marked `rate_limited`, enters backoff, and the request is retried with another available account. Secondary and code-review windows are removed from cache after their own `reset_at` passes, so an account is not skipped forever on stale quota data. ### Ollama Bridge Configuration ```yaml ollama: enabled: false # true = start the built-in Ollama-compatible listener host: 127.0.0.1 # localhost-only by default port: 11434 # Ollama default port version: "0.18.3" # value returned by /api/version disable_vision: false # true = do not advertise vision in /api/show ``` Supported Ollama endpoints: | Endpoint | Method | Description | |----------|--------|-------------| | `http://localhost:11434/api/version` | GET | Ollama version probe | | `http://localhost:11434/api/tags` | GET | Model list | | `http://localhost:11434/api/show` | POST | Model metadata | | `http://localhost:11434/api/chat` | POST | Chat completions with streaming NDJSON | | `http://localhost:11434/v1/*` | Any | OpenAI `/v1` passthrough | For Docker deployments that need host access to `11434`: 1. Set `ollama.enabled: true` and `ollama.host: 0.0.0.0` in the Dashboard or `data/local.yaml`. 2. Uncomment the `127.0.0.1:${OLLAMA_BRIDGE_PORT:-11434}:11434` port mapping in `docker-compose.yml`. 3. Keep the host binding on `127.0.0.1` unless you intentionally want to expose an unauthenticated Ollama API. Browser CORS access is limited to loopback origins such as `localhost`, `127.x.x.x`, and `::1`; non-local web origins are not allowed to read bridge responses. The bridge injects the configured Codex Proxy API key for `/v1/*` passthrough requests, so exposing it beyond localhost effectively grants unauthenticated access to the main proxy API. ### Listen Address The source/Docker default config listens on `::` (IPv6 unspecified, usually still reachable from localhost). Electron passes `127.0.0.1` at startup unless `data/local.yaml` explicitly overrides `server.host`. To force localhost-only binding: ```yaml server: host: "127.0.0.1" ``` To allow LAN access, set `server.host: "0.0.0.0"` in `data/local.yaml` and use a strong proxy API key. ### API Key ```yaml server: proxy_api_key: "pwd" # clients use Authorization: Bearer pwd # proxy_api_key: null # no global key; logged-in accounts still have account-level codex-proxy-xxxx keys ``` On first startup, if `data/local.yaml` is missing, Codex Proxy creates it with `server.proxy_api_key: pwd`. The active key is shown in the dashboard API Configuration section. ### Environment Variable Overrides | Variable | Overrides | |----------|-----------| | `PORT` | `server.port` | | `CODEX_PLATFORM` | `client.platform` | | `CODEX_ARCH` | `client.arch` | | `HTTPS_PROXY` | `tls.proxy_url` | | `OLLAMA_BRIDGE_ENABLED` | `ollama.enabled` | | `OLLAMA_BRIDGE_HOST` | `ollama.host` | | `OLLAMA_BRIDGE_PORT` | `ollama.port` | | `OLLAMA_BRIDGE_VERSION` | `ollama.version` | | `OLLAMA_BRIDGE_DISABLE_VISION` | `ollama.disable_vision` | ## 📡 API Endpoints
Click to expand main endpoint list **Protocol Endpoints** | Endpoint | Method | Description | |----------|--------|-------------| | `/v1/chat/completions` | POST | OpenAI format chat completions | | `/v1/responses` | POST | Codex Responses API passthrough | | `/v1/responses/compact` | POST | Codex compact response proxy | | `/v1/messages` | POST | Anthropic format chat completions | | `/v1/models` | GET | List available models | | `/v1/models/catalog` | GET | Full model catalog for the dashboard | | `/v1/models/:modelId/info` | GET | Reasoning and metadata for one model | | `/v1beta/models` | GET | Gemini-format model list | | `/v1beta/models/:modelAction` | POST | Gemini `generateContent` / `streamGenerateContent` | | `:11434/api/chat` | POST | Ollama-compatible chat completions (requires Ollama Bridge) | **Auth & Accounts** | Endpoint | Method | Description | |----------|--------|-------------| | `/auth/login` | GET | OAuth login entry | | `/auth/accounts` | GET | Account list (`?quota=true` / `?quota=fresh`) | | `/auth/accounts` | POST | Add single account (token or refreshToken) | | `/auth/accounts/import` | POST | Bulk import accounts | | `/auth/accounts/export` | GET | Export accounts (`?format=minimal` for compact) | | `/auth/accounts/batch-delete` | POST | Batch delete accounts | | `/auth/accounts/batch-status` | POST | Batch update account status | | `/auth/accounts/health-check` | POST | Batch account health check | | `/auth/accounts/:id/refresh` | POST | Refresh and probe one account | | `/auth/accounts/:id/quota` | GET | Actively query one account quota | | `/auth/accounts/:id/cookies` | GET/POST/DELETE | Manage account Cloudflare cookies | | `/auth/quota/warnings` | GET | Current quota warning state | **Third-Party API Keys** | Endpoint | Method | Description | |----------|--------|-------------| | `/auth/api-keys/catalog` | GET | Built-in providers and suggested model catalog | | `/auth/api-keys` | GET/POST | List / add API keys | | `/auth/api-keys/models` | POST | Fetch models from a custom OpenAI-compatible provider | | `/auth/api-keys/export` | GET | Export API key config | | `/auth/api-keys/import` | POST | Import API key config | | `/auth/api-keys/batch-delete` | POST | Batch delete API keys | | `/auth/api-keys/:id` | DELETE | Delete one API key | | `/auth/api-keys/:id/label` | PATCH | Update API key label | | `/auth/api-keys/:id/status` | PATCH | Enable or disable an API key | **Account Import/Export Examples** ```bash # Export all accounts (full format with tokens) curl -s http://localhost:8080/auth/accounts/export \ -H "Authorization: Bearer your-api-key" > backup.json # Export minimal format (refreshToken + label only, safe to share) curl -s "http://localhost:8080/auth/accounts/export?format=minimal" \ -H "Authorization: Bearer your-api-key" > backup-minimal.json # Bulk import (token, refreshToken, or both) curl -X POST http://localhost:8080/auth/accounts/import \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d '{ "accounts": [ { "token": "eyJhbGciOi..." }, { "refreshToken": "v1.abc..." }, { "refreshToken": "v1.def...", "label": "Backup" } ] }' # Returns: { "added": 2, "updated": 1, "failed": 0, "errors": [] } # One-step backup restore (export file → import to another instance) curl -X POST http://localhost:8080/auth/accounts/import \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key" \ -d @backup.json ``` **Admin** | Endpoint | Method | Description | |----------|--------|-------------| | `/admin/rotation-settings` | GET/POST | Rotation strategy config | | `/admin/quota-settings` | GET/POST | Quota refresh & warning config | | `/admin/ollama-settings` | GET/POST | Ollama Bridge config | | `/admin/ollama-status` | GET | Ollama Bridge runtime status | | `/admin/refresh-models` | POST | Trigger manual model list refresh | | `/admin/usage-stats/summary` | GET | Usage stats summary | | `/admin/usage-stats/history` | GET | Usage time series | | `/admin/logs` | GET | Request log list | | `/admin/logs/state` | GET/POST | Log capture settings | | `/admin/update-status` | GET | Self-update status | | `/admin/check-update` | POST | Check for updates | | `/admin/apply-update` | POST | Apply self-update | | `/health` | GET | Health check | **Proxy Pool** | Endpoint | Method | Description | |----------|--------|-------------| | `/api/proxies` | GET/POST | List / add proxies | | `/api/proxies/:id` | PUT/DELETE | Update / remove proxy | | `/api/proxies/:id/check` | POST | Health check single proxy | | `/api/proxies/check-all` | POST | Health check all proxies | | `/api/proxies/assign` | POST | Assign proxy to account | | `/api/proxies/assignments` | GET | View account proxy assignments | | `/api/proxies/assign-bulk` | POST | Bulk assign proxies | | `/api/proxies/assign-rule` | POST | Rule-based proxy assignment | | `/api/proxies/export` | GET | Export proxy pool YAML | | `/api/proxies/import` | POST | Import proxy pool YAML |
## 📋 Requirements - **Node.js** 18+ (20+ recommended) - **Rust** — required for source builds (compiles TLS native addon); Docker / desktop app ship pre-built - **ChatGPT account** — free account is sufficient - **Docker** (optional) ## ⚠️ Notes - Codex API is **stream-only**. `stream: false` causes the proxy to stream internally and return assembled JSON. - This project relies on Codex Desktop's public API. Upstream updates are auto-detected and fingerprints auto-synced. - Windows source builds need Rust toolchain for the TLS native addon. Docker deployment has it pre-built. ## ☕ Donate & Community
WeChat Donate
☕ Donate
WeChat Group
💬 WeChat
Telegram Group
💬 Telegram
## 🙏 Acknowledgements Codex Proxy is primarily maintained by one person, but it has been improved by a lot of community help. Special thanks to these contributors who submitted code, documentation, fixes, or PRs: [@SsuJojo](https://github.com/SsuJojo) · [@TutuchanXD](https://github.com/TutuchanXD) · [@kanweiwei](https://github.com/kanweiwei) · [@et2010](https://github.com/et2010) · [@d-demand-priv](https://github.com/d-demand-priv) · [@hangox](https://github.com/hangox) · [@jarvisluk](https://github.com/jarvisluk) · [@jeasonstudio](https://github.com/jeasonstudio) · [@JPClaw12](https://github.com/JPClaw12) · [@lezi-fun](https://github.com/lezi-fun) · [@lookvincent](https://github.com/lookvincent) · [@pocper1](https://github.com/pocper1) · [@woai66](https://github.com/woai66) · [@xsShuang](https://github.com/xsShuang) · [@yuwei5380](https://github.com/yuwei5380) · [@aeltorio](https://github.com/aeltorio) · [@williamjameshandley](https://github.com/williamjameshandley) · [@FlavienKlr](https://github.com/FlavienKlr) Thanks as well to everyone who opened [Issues](https://github.com/icebear0828/codex-proxy/issues) with bug reproductions, logs, compatibility reports, and feature suggestions. Those reports directly shaped account rotation, proxy compatibility, the Dashboard, Ollama Bridge, model compatibility, and error observability. ## 📄 License **Non-Commercial** license: - **Allowed**: Personal learning, research, self-hosted deployment - **Prohibited**: Any commercial use including selling, reselling, paid proxy services, or commercial product integration Not affiliated with OpenAI. Users assume all risks and must comply with OpenAI's Terms of Service. ---
Built with Hono + TypeScript | Powered by Codex Desktop API