{"id":50736977,"url":"https://github.com/icoretech/codex-pooler","last_synced_at":"2026-06-13T06:03:22.353Z","repository":{"id":359913539,"uuid":"1247958165","full_name":"icoretech/codex-pooler","owner":"icoretech","description":"🤖 Pool and route Codex accounts behind one Gateway","archived":false,"fork":false,"pushed_at":"2026-06-07T13:18:46.000Z","size":29133,"stargazers_count":4,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T15:10:30.770Z","etag":null,"topics":["api-gateway","beam","codex","codex-cli","elixir","erlang","gateway","hermes-agent","mcp","mcp-server","model-context-protocol","openai","openai-api","openclaw","opencode","phoenix","sse-streaming","websocket"],"latest_commit_sha":null,"homepage":"https://docs.codex-pooler.com","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/icoretech.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"buy_me_a_coffee":"masterkain","github":"masterkain"}},"created_at":"2026-05-24T02:16:31.000Z","updated_at":"2026-06-07T13:18:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/icoretech/codex-pooler","commit_stats":null,"previous_names":["icoretech/codex-pooler"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/icoretech/codex-pooler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-pooler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-pooler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-pooler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-pooler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/icoretech","download_url":"https://codeload.github.com/icoretech/codex-pooler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icoretech%2Fcodex-pooler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34121022,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-gateway","beam","codex","codex-cli","elixir","erlang","gateway","hermes-agent","mcp","mcp-server","model-context-protocol","openai","openai-api","openclaw","opencode","phoenix","sse-streaming","websocket"],"created_at":"2026-06-10T15:00:20.005Z","updated_at":"2026-06-10T15:00:34.199Z","avatar_url":"https://github.com/icoretech.png","language":"Elixir","funding_links":["https://buymeacoffee.com/masterkain","https://github.com/sponsors/masterkain"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eCodex Pooler\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eOne gateway for many Codex accounts.\u003c/strong\u003e\u003cbr\u003e\n  Pool capacity, preserve sessions, route requests, and expose stable API keys\n  for agents and tools.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start-with-docker-compose\"\u003eQuick start\u003c/a\u003e\n  ·\n  \u003ca href=\"#harness-configuration\"\u003eHarness\u003c/a\u003e\n  ·\n  \u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e\n  ·\n  \u003ca href=\"#deployment\"\u003eDeployment\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\".github/assets/codex-pooler-readme-banner.png\" alt=\"Codex Pooler gateway overview\"\u003e\n\u003c/p\u003e\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n      \u003ca href=\".github/assets/screen1.png\"\u003e\n        \u003cimg src=\".github/assets/screen1.png\" alt=\"Codex Pooler upstream account readiness\" width=\"100%\"\u003e\n      \u003c/a\u003e\u003cbr\u003e\n      \u003csub\u003eUpstreams\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n      \u003ca href=\".github/assets/screen2.png\"\u003e\n        \u003cimg src=\".github/assets/screen2.png\" alt=\"Codex Pooler Pool dashboard\" width=\"100%\"\u003e\n      \u003c/a\u003e\u003cbr\u003e\n      \u003csub\u003ePools\u003c/sub\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" valign=\"top\" width=\"33%\"\u003e\n      \u003ca href=\".github/assets/screen3.png\"\u003e\n        \u003cimg src=\".github/assets/screen3.png\" alt=\"Codex Pooler request logs\" width=\"100%\"\u003e\n      \u003c/a\u003e\u003cbr\u003e\n      \u003csub\u003eRequest logs\u003c/sub\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\nCodex Pooler is a self-hosted gateway for sharing Codex account capacity across\nagents, tools, and teams.\n\nInstead of binding each client to one Codex account, you add accounts to Pools\nand issue stable Pool API keys. Clients send familiar Codex backend or\nOpenAI-compatible requests; Codex Pooler selects the right account based on\nmodel support, limits, session continuity, routing policy, and health.\n\nOperators get one place to manage accounts, keys, routing, request accounting,\naudit logs, and health without storing prompts, files, audio, images, bearer\ntokens, or raw Codex secrets. Instance owners keep the global administration\nsurface, while instance admins work only with their assigned Pools.\n\n## Highlights\n\n- **One key for many accounts:** group Codex accounts into Pools and give\n  clients stable Pool API keys instead of binding each tool to one account\n- **Smarter capacity sharing:** route each request to an eligible account with\n  available limits, matching model support, health, session state, and Pool\n  policy\n- **Codex backend compatibility:** point Codex-compatible clients at Codex\n  Pooler and keep responses, compacting, usage, files, audio, images, and\n  backend websocket flows working through pooled accounts\n- **OpenAI-compatible SDK surface:** let `/v1`-only apps and agent tools use\n  multiple Codex subscriptions behind one gateway, with supported requests\n  translated and routed through Codex capacity to help contain API spend\n- **Session-aware websockets:** keep resumable Codex sessions and websocket\n  reconnects attached to the right upstream account without translating backend\n  websocket traffic through an HTTP compatibility layer\n- **Prompt-cache locality:** use a transient `prompt_cache_key` to prefer the\n  same eligible upstream account for repeat stateless requests, improving\n  provider-side cache locality without storing prompts or responses locally\n- **Operator dashboard:** manage Pool-scoped accounts, API keys, invites,\n  usage, request logs, audit logs, MCP access, and the owner-only jobs,\n  operators, and system settings surfaces\n- **Privacy-minded observability:** store request, routing, and audit metadata\n  without storing prompts, file bodies, audio, images, bearer tokens, cookies,\n  raw Codex account tokens, or raw API keys\n- **Configurable without code changes:** tune Pool policy, gateway defaults,\n  diagnostics, model support, limits, and operational settings from the admin UI\n- **Built for self-hosting:** run on Elixir/Erlang's fault-tolerant runtime,\n  start locally with Docker Compose, or deploy the Helm chart with separate web,\n  worker, scheduler, and migration roles for Kubernetes-friendly, multinode\n  growth\n\n## Harness Configuration\n\nKeep Pool API keys in environment variables when the harness supports secret\nexpansion. The `/mcp` endpoint is an optional operator-only add-on for metadata\ninspection; Codex Pooler runtime clients do not need it. If a desktop harness\npersists remote MCP headers in its own private settings, use a dedicated\noperator-scoped MCP token. For a local instance, the URLs are:\n\n```text\nCodex backend base URL:      http://localhost:4000/backend-api/codex\nOpenAI SDK base URL:         http://localhost:4000/v1\nOptional operator MCP URL:   http://localhost:4000/mcp\n```\n\nFor a deployed instance, replace `http://localhost:4000` with your deployed host,\nfor example `https://codex-pooler.example.com`.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/opencode-favicon.png\" alt=\"opencode logo\" width=\"16\" height=\"16\"\u003e OpenCode \u003ccode\u003e~/.config/opencode/opencode.jsonc\u003c/code\u003e\u003c/summary\u003e\n\n![Codex Pooler OpenCode integration](.github/assets/codex-pooler-opencode.png)\n\nOpenCode talks to Codex Pooler through the OpenAI-compatible `/v1` surface. The\nprovider uses the Pool API key, and the optional remote MCP entry uses an\noperator-owned MCP token. MCP is not required for OpenCode to use Codex Pooler;\nit only gives an operator MCP host read-only metadata tools. Its websocket\nsupport is the narrow Responses websocket route at `GET /v1/responses`, not\nOpenAI Realtime SDK compatibility.\n\n```jsonc\n{\n  \"$schema\": \"https://opencode.ai/config.json\",\n  \"provider\": {\n    \"openai\": {\n      \"npm\": \"@ai-sdk/openai\",\n      \"name\": \"Codex Pooler\",\n      \"options\": {\n        \"baseURL\": \"http://localhost:4000/v1\",\n        \"apiKey\": \"{env:CODEX_POOLER_API_KEY}\",\n        \"reasoningEffort\": \"high\",\n        \"reasoningSummary\": \"auto\",\n        \"textVerbosity\": \"medium\",\n        \"include\": [\"reasoning.encrypted_content\"],\n        \"store\": false\n      },\n      \"models\": {\n        \"gpt-5.5\": {\n          \"id\": \"gpt-5.5\",\n          \"name\": \"GPT-5.5\",\n          \"family\": \"gpt\",\n          \"attachment\": true,\n          \"reasoning\": true,\n          \"tool_call\": true,\n          \"temperature\": false,\n          \"modalities\": {\n            \"input\": [\"text\", \"image\"],\n            \"output\": [\"text\"]\n          },\n          \"limit\": {\n            \"context\": 400000,\n            \"input\": 256000,\n            \"output\": 128000\n          }\n        }\n      }\n    }\n  },\n  // Optional operator-only MCP metadata add-on. Omit for normal model/runtime use.\n  \"mcp\": {\n    \"codex_pooler\": {\n      \"type\": \"remote\",\n      \"url\": \"http://localhost:4000/mcp\",\n      \"oauth\": false,\n      \"headers\": {\n        \"Authorization\": \"Bearer {env:CODEX_POOLER_MCP_KEY}\"\n      },\n      \"enabled\": true,\n      \"timeout\": 30000\n    }\n  }\n}\n```\n\nDefine only models that your assigned Pool can serve. For deployed instances,\nchange `baseURL` to `https://codex-pooler.example.com/v1`; if you keep the optional\noperator MCP entry, change its `url` to `https://codex-pooler.example.com/mcp`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/codex-cli-favicon.png\" alt=\"OpenAI logo\" width=\"16\" height=\"16\"\u003e Codex \u003ccode\u003e~/.codex/config.toml\u003c/code\u003e\u003c/summary\u003e\n\n![Codex Pooler Codex CLI integration](.github/assets/codex-pooler-codex.png)\n\nCodex should use the backend compatibility route, not the `/v1` SDK route.\nKeep the provider `name` as `OpenAI`; Codex uses that value for provider-family\nbehavior even when the request is routed through Codex Pooler.\n\n```toml\nmodel = \"gpt-5.5\"\nmodel_provider = \"codex-pooler-ws\"\n\n[model_providers.codex-pooler-ws]\nname = \"OpenAI\"\nbase_url = \"http://localhost:4000/backend-api/codex\"\nenv_key = \"CODEX_POOLER_API_KEY\"\nwire_api = \"responses\"\nsupports_websockets = true\nrequires_openai_auth = true\n\n[model_providers.codex-pooler-http]\nname = \"OpenAI\"\nbase_url = \"http://localhost:4000/backend-api/codex\"\nenv_key = \"CODEX_POOLER_API_KEY\"\nwire_api = \"responses\"\nsupports_websockets = false\nrequires_openai_auth = true\n\n# Optional operator-only MCP metadata add-on. Omit for normal Codex runtime use.\n[mcp_servers.codex_pooler]\nurl = \"http://localhost:4000/mcp\"\nbearer_token_env_var = \"CODEX_POOLER_MCP_KEY\"\n```\n\nUse the websocket provider for normal Codex backend behavior, and keep the HTTP\nprovider when you need to force SSE-only coverage. For deployed instances,\nchange both `base_url` values to `https://codex-pooler.example.com/backend-api/codex`;\nif you keep the optional operator MCP add-on, change its `url` to\n`https://codex-pooler.example.com/mcp`.\n\nCodex filters resumable conversations by `model_provider`. If you already have\nsessions created with the built-in `openai` provider and want them to appear\nunder `codex-pooler-ws`, re-tag both the JSONL transcripts and the newer SQLite\nstate database. Run these with Codex closed; they edit local state in place. The\ntranscript rewrite scans the whole sessions directory and can take a while on\nlarge installs. Set `TO_PROVIDER=codex-pooler-http` if you made the HTTP\nprovider your default.\n\n```bash\nset -eu\n\nFROM_PROVIDER=\"openai\"\nTO_PROVIDER=\"codex-pooler-ws\"\n\nfind ~/.codex/sessions -type f -name '*.jsonl' \\\n  -exec perl -pi -e \\\n    \"s/\\\"model_provider\\\":\\\"${FROM_PROVIDER}\\\"/\\\"model_provider\\\":\\\"${TO_PROVIDER}\\\"/g\" \\\n    {} +\n\nfor db in ~/.codex/state_*.sqlite; do\n  [ -e \"$db\" ] || continue\n  sqlite3 \"$db\" \\\n    \"UPDATE threads SET model_provider = '${TO_PROVIDER}' WHERE model_provider = '${FROM_PROVIDER}';\"\ndone\n```\n\nOn Windows, run the same migration from PowerShell. This expects `sqlite3` to be\navailable on `PATH`.\n\n```powershell\n$ErrorActionPreference = \"Stop\"\n\n$FromProvider = \"openai\"\n$ToProvider = \"codex-pooler-ws\"\n$CodexHome = Join-Path $HOME \".codex\"\n\n$FromJson = '\"model_provider\":\"' + $FromProvider + '\"'\n$ToJson = '\"model_provider\":\"' + $ToProvider + '\"'\n\nGet-ChildItem -Path (Join-Path $CodexHome \"sessions\") -Recurse -Filter \"*.jsonl\" |\n  ForEach-Object {\n    $Path = $_.FullName\n    $TempPath = \"$Path.tmp\"\n    $Reader = [System.IO.StreamReader]::new($Path)\n    $Writer = [System.IO.StreamWriter]::new(\n      $TempPath,\n      $false,\n      [System.Text.UTF8Encoding]::new($false)\n    )\n\n    try {\n      while (($Line = $Reader.ReadLine()) -ne $null) {\n        $Writer.WriteLine($Line.Replace($FromJson, $ToJson))\n      }\n    } finally {\n      $Reader.Dispose()\n      $Writer.Dispose()\n    }\n\n    Move-Item -Force $TempPath $Path\n  }\n\nGet-ChildItem -Path $CodexHome -Filter \"state_*.sqlite\" |\n  ForEach-Object {\n    sqlite3 $_.FullName `\n      \"UPDATE threads SET model_provider = '$ToProvider' WHERE model_provider = '$FromProvider';\"\n  }\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/openclaw-favicon.png\" alt=\"OpenClaw logo\" width=\"16\" height=\"16\"\u003e OpenClaw \u003ccode\u003e~/.openclaw/openclaw.json\u003c/code\u003e\u003c/summary\u003e\n\n![Codex Pooler OpenClaw integration](.github/assets/codex-pooler-openclaw.png)\n\nOpenClaw uses `openai/*` as the canonical OpenAI route. To keep that model name\nwhile sending agent turns to Codex Pooler's OpenAI-compatible `/v1` surface,\npoint the OpenAI provider at Codex Pooler and use the current OpenClaw runtime id.\n\n```json5\n{\n  agents: {\n    defaults: {\n      model: { primary: \"openai/gpt-5.5\" },\n    },\n  },\n  models: {\n    mode: \"merge\",\n    providers: {\n      openai: {\n        baseUrl: \"http://localhost:4000/v1\",\n        apiKey: \"${CODEX_POOLER_API_KEY}\",\n        api: \"openai-responses\",\n        agentRuntime: { id: \"openclaw\" },\n        timeoutSeconds: 300,\n        models: [\n          {\n            id: \"gpt-5.5\",\n            name: \"GPT-5.5 via Codex Pooler\",\n            reasoning: true,\n            input: [\"text\", \"image\"],\n            contextWindow: 400000,\n            contextTokens: 256000,\n            maxTokens: 128000,\n          },\n        ],\n      },\n    },\n  },\n  // Optional operator-only MCP metadata add-on. Omit for normal model/runtime use.\n  mcp: {\n    servers: {\n      codex_pooler: {\n        url: \"http://localhost:4000/mcp\",\n        transport: \"streamable-http\",\n        headers: {\n          Authorization: \"Bearer ${CODEX_POOLER_MCP_KEY}\",\n        },\n      },\n    },\n  },\n}\n```\n\nDefine only models that your assigned Pool can serve. For deployed instances,\nchange `baseUrl` to `https://codex-pooler.example.com/v1`; if you keep the optional\noperator MCP add-on, change its `url` to `https://codex-pooler.example.com/mcp`.\nIf you prefer to keep Codex Pooler separate from OpenClaw's built-in OpenAI\nprovider behavior, use a custom provider id such as `codex-pooler/gpt-5.5`\ninstead. That follows OpenClaw's generic custom-provider shape, but tools that\nlook specifically for `openai/gpt-*` model refs will not see it as canonical\nOpenAI.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/hermes-favicon.png\" alt=\"Hermes Agent logo\" width=\"16\" height=\"16\"\u003e Hermes Agent \u003ccode\u003e~/.hermes/config.yaml\u003c/code\u003e + \u003ccode\u003eauth.json\u003c/code\u003e\u003c/summary\u003e\n\n![Codex Pooler Hermes Agent integration](.github/assets/codex-pooler-hermes.png)\n\nHermes works best through its `openai-api` provider with the Responses transport\nforced explicitly. This is the recommended Codex Pooler setup. Keep the Pool API\nkey in `~/.hermes/.env` and point the provider config at Codex Pooler's `/v1`\nsurface. The `mcp_servers` block is an optional operator-only add-on for\nread-only metadata tools; Codex Pooler works without it.\n\n```bash\nOPENAI_API_KEY=\u003cpool-api-key\u003e\nOPENAI_BASE_URL=http://localhost:4000/v1\n# Optional operator-only MCP metadata add-on:\nCODEX_POOLER_MCP_KEY=\u003coperator-mcp-token\u003e\n```\n\n```yaml\nmodel:\n  default: gpt-5.5\n  provider: openai-api\n  base_url: http://localhost:4000/v1\n  api_mode: codex_responses\n  context_length: 400000\n  supports_vision: true\n\nagent:\n  image_input_mode: native\n\n# Optional operator-only MCP metadata add-on. Omit for model/runtime use.\nmcp_servers:\n  codex_pooler:\n    url: http://localhost:4000/mcp\n    headers:\n      Authorization: \"Bearer ${CODEX_POOLER_MCP_KEY}\"\n    enabled: true\n    timeout: 120\n    connect_timeout: 15\n```\n\nRemote HTTP MCP servers require Hermes' `mcp` extra. If\n`hermes mcp test codex_pooler` reports `mcp.client.streamable_http is not\navailable`, install MCP support into the Hermes environment, following the\n[Hermes MCP Integration docs](https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp),\nand rerun the test.\n\nCheck the one-shot model path:\n\n```bash\nhermes -z 'Reply with exactly: hermes openai api ok' --ignore-rules\n```\n\nHermes can also be made to use its `openai-codex` provider against Codex\nPooler. This is less direct because Hermes treats `openai-codex` as an OAuth\nprovider by default; add a Pool API key credential ahead of any existing\ndevice-code credential and keep the entry's `base_url` on `/v1`. Use this only\nwhen you specifically need Hermes' `openai-codex` credential-pool behavior; the\n`openai-api` configuration above is the preferred setup. This variant stores\nthe key in `auth.json` because Hermes credential pools live there.\n\n```bash\nHERMES_CODEX_BASE_URL=http://localhost:4000/v1\n# Optional operator-only MCP metadata add-on:\nCODEX_POOLER_MCP_KEY=\u003coperator-mcp-token\u003e\n```\n\n```yaml\nmodel:\n  default: gpt-5.5\n  provider: openai-codex\n  base_url: http://localhost:4000/v1\n  context_length: 400000\n  supports_vision: true\n\nagent:\n  image_input_mode: native\n\n# Optional operator-only MCP metadata add-on. Omit for model/runtime use.\nmcp_servers:\n  codex_pooler:\n    url: http://localhost:4000/mcp\n    headers:\n      Authorization: \"Bearer ${CODEX_POOLER_MCP_KEY}\"\n    enabled: true\n    timeout: 120\n    connect_timeout: 15\n```\n\n```json\n{\n  \"active_provider\": \"openai-codex\",\n  \"credential_pool\": {\n    \"openai-codex\": [\n      {\n        \"label\": \"codex-pooler\",\n        \"auth_type\": \"api_key\",\n        \"priority\": -10,\n        \"source\": \"manual\",\n        \"access_token\": \"\u003cpool-api-key\u003e\",\n        \"base_url\": \"http://localhost:4000/v1\"\n      }\n    ]\n  }\n}\n```\n\nFor deployed instances, change the model URLs to\n`https://codex-pooler.example.com/v1`; if you keep the optional operator MCP add-on,\nchange the MCP `url` to `https://codex-pooler.example.com/mcp`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/aider-favicon.png\" alt=\"Aider logo\" width=\"16\" height=\"16\"\u003e Aider \u003ccode\u003e~/.aider.conf.yml\u003c/code\u003e\u003c/summary\u003e\n\nAider uses the OpenAI-compatible route with the `openai/` model prefix. Keep the\nPool API key in the environment and point Aider's OpenAI API base at Codex\nPooler's `/v1` surface.\n\n```yaml\nmodel: openai/gpt-5.5\nopenai-api-base: http://localhost:4000/v1\n```\n\nSmoke-test from a repository:\n\n```bash\nexport OPENAI_API_KEY=\"$CODEX_POOLER_API_KEY\"\naider --model openai/gpt-5.5 --message 'Reply with exactly: aider ok'\n```\n\nFor deployed instances, change `openai-api-base` to\n`https://codex-pooler.example.com/v1`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/continue-favicon.png\" alt=\"Continue logo\" width=\"16\" height=\"16\"\u003e Continue \u003ccode\u003e~/.continue/config.yaml\u003c/code\u003e\u003c/summary\u003e\n\nContinue can use Codex Pooler as an OpenAI-compatible provider by setting\n`provider: openai`, `apiBase` to `/v1`, and the Pool API key as a Continue\nsecret. For `gpt-5*` models, Continue uses the Responses API by default.\n\n```yaml\nname: Codex Pooler\nversion: 1.0.0\nschema: v1\n\nmodels:\n  - name: GPT-5.5 via Codex Pooler\n    provider: openai\n    model: gpt-5.5\n    apiBase: http://localhost:4000/v1\n    apiKey: \"${{ secrets.CODEX_POOLER_API_KEY }}\"\n    roles:\n      - chat\n      - edit\n      - apply\n      - summarize\n    capabilities:\n      - tool_use\n      - image_input\n\n# Optional operator-only MCP metadata add-on. Omit for model/runtime use.\nmcpServers:\n  - name: codex_pooler\n    type: streamable-http\n    url: http://localhost:4000/mcp\n    requestOptions:\n      timeout: 30000\n      headers:\n        Authorization: \"Bearer ${{ secrets.CODEX_POOLER_MCP_KEY }}\"\n```\n\nFor deployed instances, change `apiBase` to `https://codex-pooler.example.com/v1`;\nif you keep the optional operator MCP add-on, change the MCP `url` to\n`https://codex-pooler.example.com/mcp`.\n\nCheck the headless CLI path after saving the config:\n\n```bash\nexport CODEX_POOLER_API_KEY=\u003cpool-api-key\u003e\nnpx -y @continuedev/cli@latest -p \\\n  --config ~/.continue/config.yaml \\\n  --silent \\\n  'Reply with exactly: continue ok'\n```\n\nThe Pool API key authenticates model requests. The MCP token authenticates only\nthe operator metadata endpoint.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/cline-favicon.png\" alt=\"Cline logo\" width=\"16\" height=\"16\"\u003e Cline \u003ccode\u003e~/.cline\u003c/code\u003e + \u003ccode\u003e~/.cline/mcp.json\u003c/code\u003e\u003c/summary\u003e\n\nCline CLI accepts `openai` as shorthand for its OpenAI-compatible provider and\nstores it as `openai-compatible`. Configure it with the Pool API key, the Codex\nPooler `/v1` base URL, and the model id that your assigned Pool can serve.\n\n```bash\ncline auth \\\n  --provider openai \\\n  --apikey \"$CODEX_POOLER_API_KEY\" \\\n  --baseurl http://localhost:4000/v1 \\\n  --modelid gpt-5.5\n```\n\nCheck the headless CLI path after saving auth:\n\n```bash\ncline --provider openai \\\n  --model gpt-5.5 \\\n  --json \\\n  --auto-approve false \\\n  'Reply with exactly: cline ok'\n```\n\nFor optional operator MCP in Cline CLI, add the remote server to\n`~/.cline/mcp.json`. Codex Pooler does not require this for model use. The VS\nCode extension opens its own MCP settings JSON from the Cline MCP Servers panel;\nuse the same `mcpServers` shape there.\n\n```json\n{\n  \"mcpServers\": {\n    \"codex_pooler\": {\n      \"url\": \"http://localhost:4000/mcp\",\n      \"headers\": {\n        \"Authorization\": \"Bearer \u003coperator-mcp-token\u003e\"\n      },\n      \"disabled\": false,\n      \"autoApprove\": []\n    }\n  }\n}\n```\n\nFor deployed instances, change `--baseurl` to `https://codex-pooler.example.com/v1`\nand, if you keep the optional operator MCP add-on, change the MCP `url` to\n`https://codex-pooler.example.com/mcp`.\n\nUse a Pool API key for `/v1` model requests and an operator MCP token for\n`/mcp`. Do not reuse the Pool API key for MCP.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/goose-favicon.png\" alt=\"Goose logo\" width=\"16\" height=\"16\"\u003e Goose \u003ccode\u003e~/.config/goose/config.yaml\u003c/code\u003e\u003c/summary\u003e\n\nConfigure Goose's OpenAI provider for Codex Pooler's OpenAI-compatible\nchat-completions path. Keep the Pool API key in `OPENAI_API_KEY` or Goose's\nsecret storage.\n\n```yaml\nGOOSE_PROVIDER: openai\nGOOSE_MODEL: gpt-5.5\nOPENAI_HOST: http://localhost:4000\nOPENAI_BASE_PATH: v1/chat/completions\n```\n\nCheck the headless CLI path with tool access enabled:\n\n```bash\nexport OPENAI_API_KEY=\"$CODEX_POOLER_API_KEY\"\ngoose run \\\n  --no-session \\\n  --provider openai \\\n  --model gpt-5.5 \\\n  --with-builtin developer \\\n  --text 'Use your developer tool to create goose-ok.txt containing exactly: goose ok. Then reply with exactly: goose ok'\n```\n\nFor optional operator MCP metadata access, add a remote Streamable HTTP\nextension. Codex Pooler model use does not require this. Goose stores remote\nextension headers in its config, so use a dedicated MCP token.\n\n```yaml\n# Optional operator-only MCP metadata add-on. Omit for model/runtime use.\nextensions:\n  codex_pooler:\n    enabled: true\n    type: streamable_http\n    name: codex_pooler\n    uri: http://localhost:4000/mcp\n    headers:\n      Authorization: \"Bearer \u003coperator-mcp-token\u003e\"\n    timeout: 300\n    bundled: null\n    available_tools: []\n```\n\nFor deployed instances, change `OPENAI_HOST` to `https://codex-pooler.example.com`;\nif you keep the optional operator MCP add-on, change the extension `uri` to\n`https://codex-pooler.example.com/mcp`.\n\nUse a Pool API key for OpenAI-compatible model requests and an operator MCP token\nfor `/mcp`. Do not reuse the Pool API key for MCP.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/python-favicon.png\" alt=\"Python logo\" width=\"16\" height=\"16\"\u003e OpenAI Python SDK\u003c/summary\u003e\n\nOpenAI Python SDK clients can use the OpenAI-compatible `/v1` surface by setting\n`base_url` to the Codex Pooler `/v1` URL and using the Pool API key as the API\nkey.\n\n```python\nimport os\n\nfrom openai import OpenAI\n\nclient = OpenAI(\n    api_key=os.environ[\"CODEX_POOLER_API_KEY\"],\n    base_url=\"http://localhost:4000/v1\",\n)\n\nresponse = client.responses.create(\n    model=\"gpt-5.5\",\n    input=\"Write a one-sentence status update.\",\n)\n\nprint(response.output_text)\n```\n\nFor deployed instances, change `base_url` to `https://codex-pooler.example.com/v1`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/nodejs-favicon.png\" alt=\"Node.js logo\" width=\"16\" height=\"16\"\u003e OpenAI Node SDK\u003c/summary\u003e\n\nOpenAI Node SDK clients use the same OpenAI-compatible `/v1` surface. Configure\n`baseURL` with the Codex Pooler `/v1` URL and pass the Pool API key as the API\nkey.\n\n```js\nimport OpenAI from \"openai\";\n\nconst client = new OpenAI({\n  apiKey: process.env.CODEX_POOLER_API_KEY,\n  baseURL: \"http://localhost:4000/v1\",\n});\n\nconst response = await client.responses.create({\n  model: \"gpt-5.5\",\n  input: \"Write a one-sentence status update.\",\n});\n\nconsole.log(response.output_text);\n```\n\nFor deployed instances, change `baseURL` to `https://codex-pooler.example.com/v1`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/vercel-favicon.png\" alt=\"Vercel logo\" width=\"16\" height=\"16\"\u003e Vercel AI SDK\u003c/summary\u003e\n\nVercel AI SDK can point its OpenAI provider at Codex Pooler by creating a custom\nprovider with `createOpenAI`. The provider calls the OpenAI-compatible `/v1`\nsurface with the Pool API key.\n\n```ts\nimport { createOpenAI } from \"@ai-sdk/openai\";\nimport { generateText } from \"ai\";\n\nconst pooler = createOpenAI({\n  apiKey: process.env.CODEX_POOLER_API_KEY,\n  baseURL: \"http://localhost:4000/v1\",\n});\n\nconst { text } = await generateText({\n  model: pooler.responses(\"gpt-5.5\"),\n  prompt: \"Write a one-sentence status update.\",\n});\n\nconsole.log(text);\n```\n\nFor deployed instances, change `baseURL` to `https://codex-pooler.example.com/v1`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cimg src=\".github/assets/claude-code-favicon.png\" alt=\"Claude Code logo\" width=\"16\" height=\"16\"\u003e Claude Code\u003c/summary\u003e\n\n![Claude Code on Codex Pooler](.github/assets/codex-pooler-claude.png)\n\n\u003c/details\u003e\n\n## Quick Start With Docker Compose\n\nThis runs the published release image with a local Postgres database. It is the\nfastest way to try Codex Pooler on a laptop or small server.\n\nPrerequisites:\n\n- Docker with Compose\n- Git, if you are cloning the repository\n- `openssl`\n\nStart Codex Pooler:\n\n```bash\ngit clone https://github.com/icoretech/codex-pooler.git\ncd codex-pooler\n\n# Optional: pin a release tag before generating .env.\n# Omit this for a quick trial that follows the latest tag.\n# export CODEX_POOLER_IMAGE_TAG=\u003crelease-tag\u003e\n\nscripts/self-host/generate-env.sh\ndocker compose pull\ndocker compose up -d\n```\n\nThe first run pulls the app and Postgres images, waits for Postgres health, runs\nthe migration container, then starts the web app.\n\nOpen `http://localhost:4000`. On the first visit, create the owner account at\n`/bootstrap`, then sign in and start with `/admin/pools`.\n\nTo verify the first-run redirect before opening a browser:\n\n```bash\ncurl -sS -D - -o /dev/null http://localhost:4000/ | grep -i '^location: /bootstrap'\ncurl -fsS http://localhost:4000/bootstrap/status\n```\n\nThe status endpoint should return `{\"status\":\"ok\",\"bootstrap\":\"pending\"}` on a\nfresh database.\n\nUseful commands:\n\n```bash\ndocker compose ps\ndocker compose logs -f app\ndocker compose down\n```\n\nUse `http://localhost:4000` for the default Compose stack even if the Phoenix\nstartup banner prints an endpoint URL such as `https://localhost`; the Compose\nport mapping is the local URL to open. The release image includes the OS\ntimezone database used for operator timezone display.\n\nTo remove the local database too:\n\n```bash\ndocker compose down -v\n```\n\n## First Runtime Setup\n\nAfter bootstrap:\n\n1. Create a Pool in `/admin/pools`\n2. Import or connect Codex accounts in `/admin/upstreams`\n3. Create a Pool API key in `/admin/api-keys`\n4. Point Codex or SDK clients at one of the runtime base URLs:\n\nTreat an imported Codex `auth.json` as owned by Codex Pooler after import. Do\nnot keep using the same `auth.json` from another Codex install, machine, or\nautomation unless you accept that provider refresh-token rotation can invalidate\none copy and move the account to `reauth_required`.\n\n```text\nCodex backend base URL: http://localhost:4000/backend-api/codex\nOpenAI SDK base URL:    http://localhost:4000/v1\n```\n\nUse the generated Pool API key as the bearer token. That key represents the\nPool, not a single Codex account, so Codex Pooler can pick the best eligible\naccount for each request. Raw API keys are shown only once when created or\nrotated.\n\n## Operator Roles\n\nThe first bootstrap account is an `instance_owner`. Owners have instance-wide\nadministration access: they create Pools, assign operators to Pools, manage\noperators, inspect global jobs, and change system settings.\n\nAdditional operators can be owners or `instance_admin`s. Instance admins are\nPool-scoped: they can work only with active Pools assigned to them and metadata\nderived from those Pools. If no Pools are assigned, the admin UI shows empty\nPool-scoped states instead of exposing global data. Archiving or deleting a Pool\nremoves future instance-admin visibility for that Pool; historical request and\naudit rows for archived or deleted Pools remain owner-only.\n\n## Runtime Compatibility\n\nCodex Pooler supports two client-facing shapes:\n\n- **Codex backend clients:** `/backend-api/codex/*`, `/backend-api/files`,\n  `/backend-api/transcribe`, usage routes, and backend websocket response\n  streams\n- **OpenAI-style SDK clients:** `/v1/models`, `/v1/responses`,\n  `/v1/chat/completions`, `/v1/files`, `/v1/audio/transcriptions`, selected\n  image endpoints, and narrow Responses websocket compatibility on\n  `GET /v1/responses`\n\nThe `/v1` surface is compatibility, not a second engine. Supported requests are\ntranslated into Codex-compatible calls, then routed through the same Pool rules,\nlimit checks, accounting, and account selection path. `/v1/realtime` and OpenAI\nRealtime SDK websocket or session routes are not supported.\n\nContinuity headers are local routing inputs. Codex Pooler chooses them in this\norder: `x-codex-window-id` \u003e `x-codex-session-id` \u003e `session-id` \u003e\n`x-session-affinity` \u003e `session_id` \u003e `x-codex-conversation-id`. `session-id` and\n`x-session-affinity` are not forwarded upstream. The raw `x-codex-window-id` value is hashed before it becomes a local persisted session key. Local timing regressions showed\n`/v1/responses` HTTP streaming and Responses websocket paths stay inside the\nobserved client budgets with the existing stream timeout settings, so no new\nroute-specific timeout defaults are required.\n\n## Operator MCP Service\n\nCodex Pooler includes an optional metadata-only MCP endpoint at `/mcp` for\ntrusted operators who want an MCP host to inspect Pools, upstream accounts, Pool\nAPI key metadata, operators, invites, request logs, audit logs, and MCP service\nstatus. This operator add-on is not required for Codex Pooler runtime clients.\nThe service is read-only and has no mutation tools. It uses the same owner vs\nassigned-Pool visibility model as the admin UI, but connected MCP hosts can read\nthe metadata visible to that operator, so only connect hosts you trust with that\nview.\n\nMCP access uses operator-owned bearer MCP tokens, not Pool API keys, browser\nsessions, cookies, query tokens, invite tokens, upstream tokens, or custom\nheaders. Operators manage their own MCP account gate and tokens from\n`/admin/settings?tab=account`; the instance-wide service gate is managed from\n`/admin/system`. Both gates must be enabled before a token works. Raw MCP tokens\nare shown only once when created, and per-key usage tracking, counters, last IP,\nand user-agent history are intentionally not stored.\n\nThe `/mcp` route inherits the runtime ingress IP allowlist and trusted-proxy\nsettings. If the allowlist is empty, the firewall is off; if it is configured,\nthe resolved client IP must match before MCP authentication or tool dispatch.\n\n## Configuration\n\n`scripts/self-host/generate-env.sh` writes a local `.env` with generated\nsecrets and local defaults. Keep that file private and don't reuse generated\nvalues between public installs.\n\nEnvironment variables are only for values the release needs before it can read\nthe database:\n\n- `CODEX_POOLER_IMAGE` and `CODEX_POOLER_IMAGE_TAG`, the release image to run\n- `CODEX_POOLER_HTTP_PORT`, the local host port, default `4000`\n- `DATABASE_URL`, the Postgres connection used by the app\n- `SECRET_KEY_BASE`, Phoenix signing and encryption secret\n- `PHX_HOST`, `PORT`, and `PHX_SERVER`, HTTP endpoint boot settings\n- `OBAN_MODE` and `OBAN_JOBS_QUEUE_LIMIT`, release role and queue topology\n- `DNS_CLUSTER_QUERY`, plus release distribution variables when clustering is on\n- `CODEX_POOLER_TOTP_ENCRYPTION_KEY` and `CODEX_POOLER_TOTP_KEY_VERSION`, TOTP\n  encryption root and version\n- `CODEX_POOLER_UPSTREAM_SECRET_KEY` and\n  `CODEX_POOLER_UPSTREAM_SECRET_KEY_VERSION`, upstream secret encryption root\n  and version; the key must be 32 raw bytes or base64-encoded 32 bytes\n\nOperational controls such as file limits, ingress trust, gateway diagnostics,\nroute-class admission, circuit thresholds, metrics auth, operator email, model\nmetadata, upstream timeouts, the OpenAI pricing catalog URL, and SMTP delivery\nlive in DB-managed Instance Settings under `/admin/system`. Live settings apply\nto new runtime work through the settings cache. Cached settings reload after save\nthrough PubSub invalidation; existing leases, in-flight requests, and already-open\nstreams keep the values they started with.\n\nSecret Instance Settings stay write-only in the UI. The metrics bearer token is\nstored only as a keyed HMAC digest, fingerprint, and key version. The SMTP\npassword is stored encrypted with key version metadata and is recovered only for\nmail send or credential-test paths.\n\n## Deployment\n\nDocker Compose is the easiest way to try the software. For Kubernetes, use the\n`icoretech/codex-pooler` Helm chart from the\n[iCoreTech Helm repository](https://github.com/icoretech/helm). The chart\ndeploys the same release image with separate app, worker, scheduler, and\nmigration roles. It expects an explicit immutable image tag for real\ndeployments. Official release images include the OS IANA timezone database used\nfor operator timezone display. Custom runtime images or hosts must provide\nzoneinfo files at `/usr/share/zoneinfo` or set `TZDIR`. The chart defaults the\nweb app to one replica because backend\nwebsocket continuity owns a live upstream websocket in an app pod. Owner-alive\ncross-node forwarding is wired, but scaling web replicas still requires\nclustering, owner-forwarding, and the explicit unsafe topology acknowledgement\nuntil Kubernetes smoke evidence relaxes that guard.\n\nThe Helm migration hook runs database migrations and imports the vendored OpenAI\npricing feed so request-log cost reporting has pricing snapshots after install\nor upgrade. The scheduler also refreshes pricing hourly from the OpenAI pricing\ncatalog URL in Instance Settings, which defaults to\n`https://icoretech.github.io/openai-json-pricing/pricing.json`.\n\n## Local Development\n\nLocal development runs Phoenix on the host and Postgres through the dev compose\nfile:\n\n```bash\nmake dev\n```\n\n`make dev` starts Postgres, prepares the database, imports the vendored OpenAI\npricing feed, and starts the Phoenix server on `http://localhost:4000`. Logs\nare written to `tmp/dev-server.log`.\n\nDevelopment seeds are optional and only run through the explicit seed task. To\ncreate a compact idempotent operator baseline with one owner plus four example\noperators, run:\n\n```bash\nmix dev.seed compact\n```\n\nAll seeded operators use `dev-password-123`.\n\nTo recreate a fuller fake dataset for exercising admin UI states without real\naccounts or real request data, run:\n\n```bash\nmix dev.seed full\n```\n\nThe full seed is idempotent and replaces only deterministic `dev-*` fake rows\nowned by the development seed namespace. It includes active/disabled pools,\nactive/paused/revoked API keys, upstream accounts in active/refresh/reauth/paused\nstates, quota windows, request logs, invites, audit events, and job rows.\n\nCommon checks:\n\n```bash\nmix precommit\nmix quality\ndocker compose -f docker-compose.dev.yml config\ndocker build .\n```\n\nHelm chart validation lives with the published chart in the iCoreTech Helm\nrepository when Kubernetes deployment behavior or values change.\n\n`mix test` and `mix precommit` serialize database-backed test runs with a\nPostgreSQL advisory lock keyed by the configured test database, so concurrent\nlocal runs wait instead of deadlocking the shared sandbox database.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficoretech%2Fcodex-pooler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficoretech%2Fcodex-pooler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficoretech%2Fcodex-pooler/lists"}