The full featured self-hosted Codex gateway, for teams, agents and you. Works with:
English · 简体中文
Website · Docs · Quick start · Harness · Configuration · Deployment · X · Reddit
Upstreams |
Pools |
Request logs |
Codex Pooler is a self-hosted gateway for running Codex-compatible agents, tools, and automation through stable Pool API keys. It works with one upstream Codex account for credential isolation, client normalization, metadata-only operations, and saved reset visibility; add more accounts when you want shared capacity and routing across eligible accounts.
Clients send familiar Codex backend or OpenAI-compatible requests; Codex Pooler selects an eligible account based on model support, quota evidence, limits, session continuity, routing policy, and health. The Pool key stays stable while upstream assignments, lifecycle state, reset policy, and capacity change behind it.
Operators get one place to manage Pools, accounts, API keys, saved resets, routing, request accounting, audit logs, and health without storing prompts, files, audio, images, bearer tokens, or raw Codex secrets. Instance owners keep the global administration surface, while instance admins work only with their assigned Pools.
- 🧩 Use the tools you already know: connect Codex, OpenCode and other supported coding agents, plus apps built with OpenAI-compatible SDKs
- 🔑 One key for your apps: connect tools with a Pool API key that stays the same when you add or replace Codex accounts, without sharing account credentials
- ⚡ High cache reuse across protocols: over 95% cached input observed on HTTP/SSE and WebSockets, backed by cache-aware routing and connection reuse
- 🎯 Automatic account selection: send requests to accounts that can serve the chosen model, with available quota and account health taken into account
- 📏 Control how much each key can use: set request limits and daily or weekly AI usage allowances
- 🚀 Give agents more room to work: let them use several tools at once in Full mode, with Lite compatibility when needed
- 🖼️ Images and voice, too: generate and edit images or transcribe audio through supported apps, using the same Pool API key
- 🛡️ Keep conversation content private: track usage and troubleshoot requests without saving prompts, replies, uploaded files, images or audio
- 🔁 Keep conversations together: keep supported sessions linked to the right account when a client reconnects
- 🔭 Let users track their own usage: enable a personal Observatory dashboard for each key, showing activity, response times and estimated costs
- 🏦 Put saved resets to use: see available reset credits and use them to restore account quota, manually or automatically when enabled
- 🖥️ Manage everything in one place: add accounts, manage keys and invites, check usage and change settings from a browser
- 👥 Organize teams and projects: group accounts into Pools with their own access rules and model choices
- 🤝 Connect accounts by invitation: let account owners join a Pool through a guided browser flow, without sending you credential files
- 🚨 Know when attention is needed: receive alerts about low capacity, account problems and reset events in the dashboard, by email or through webhooks
- 🔎 Spot model downgrades: see when the provider reports a different model from the one sent, or changes the model name during a response
- 🧷 Fill in missing continuity: derive stable session identities from cache keys or conversation IDs when a harness does not send them directly
- 🧱 Choose who can connect: optionally allow requests only from approved networks
- 🐳 Run it on your own infrastructure: start with Docker Compose or deploy on Kubernetes as your needs grow
Start with a running Codex Pooler instance, a Pool API key, and an installed
client. The examples include gpt-6-luna, gpt-6.1-sol and gpt-6-astra, with
Sol selected by default. Keep the models available to your Pool.
Replace <pool-api-key> with your key and run the command for your terminal
before starting the client.
macOS / Linux / Windows WSL (bash or zsh)
export CODEX_POOLER_API_KEY="<pool-api-key>"Windows PowerShell
$env:CODEX_POOLER_API_KEY = "<pool-api-key>"These commands set the key for the current terminal. For desktop apps, follow the linked guide to save the key for the app.
The paths below are defaults. On macOS/Linux, ~ is your home folder.
On Windows, paste paths starting with %USERPROFILE%, %APPDATA% or
%LOCALAPPDATA% into File Explorer's address bar. If you installed a client
inside WSL, use its Linux paths and commands inside WSL. Custom configuration
folders or profiles take precedence over these defaults.
For a local instance:
| Client | Base URL |
|---|---|
| Codex CLI / Desktop | http://localhost:4000/backend-api/codex |
| Other harnesses and SDKs | http://localhost:4000/v1 |
For a deployed instance, replace http://localhost:4000 with your instance's
host, such as https://codex-pooler.example.com. Merge snippets into existing
configurations. The examples use the large 828,400-token context for GPT-6.
Codex CLI and Desktop read the available context size automatically from your Pool.
Each entry covers the basic connection. Its full setup & extras link covers installation, advanced options and troubleshooting. Operator MCP is optional and uses a separate token; see Operator MCP Service.
Codex CLI and Codex Desktop config.toml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.codex/config.toml |
| Windows | %USERPROFILE%\.codex\config.toml |
Open config.toml at the path for your system and add the following. If you
set CODEX_HOME, use the file in that folder instead. If the file already has
a [features] section, add the setting to that section.
model = "gpt-6.1-sol"
model_provider = "codex-pooler-ws"
[model_providers.codex-pooler-ws]
name = "OpenAI"
base_url = "http://localhost:4000/backend-api/codex"
model_catalog_url = "http://localhost:4000/backend-api/codex/models"
env_key = "CODEX_POOLER_API_KEY"
wire_api = "responses"
supports_websockets = true
requires_openai_auth = true
[features]
api_key_model_discovery = trueRestart Codex and choose a model available to your Pool. If you use Codex Desktop, follow the full guide to make your API key available to the app.
Full setup & extras — desktop setup, account settings and existing conversations.
OpenCode opencode.jsonc
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.config/opencode/opencode.jsonc |
| Windows | %USERPROFILE%\.config\opencode\opencode.jsonc |
Open opencode.jsonc at the path for your system and add the configuration
for your OpenCode version below.
OpenCode v2
OpenCode v2 full setup & extras — installation and advanced options.
OpenCode v1
{
"$schema": "https://opencode.ai/config.json",
"model": "openai/gpt-6.1-sol",
"small_model": "openai/gpt-6-luna",
"provider": {
"openai": {
"npm": "@ai-sdk/openai",
"name": "Codex Pooler",
"options": {
"baseURL": "http://localhost:4000/v1",
"apiKey": "{env:CODEX_POOLER_API_KEY}"
},
"models": {
"gpt-6-luna": {
"id": "gpt-6-luna",
"name": "GPT-6 Luna",
"family": "gpt",
"attachment": true,
"reasoning": true,
"tool_call": true,
"temperature": false,
"options": {
"reasoningEffort": "high",
"reasoningSummary": "auto",
"include": ["reasoning.encrypted_content"]
},
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
},
"gpt-6.1-sol": {
"id": "gpt-6.1-sol",
"name": "GPT-6.1 Sol",
"family": "gpt",
"attachment": true,
"reasoning": true,
"tool_call": true,
"temperature": false,
"options": {
"reasoningEffort": "high",
"reasoningSummary": "auto",
"include": ["reasoning.encrypted_content"]
},
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
},
"gpt-6-astra": {
"id": "gpt-6-astra",
"name": "GPT-6 Astra",
"family": "gpt",
"attachment": true,
"reasoning": true,
"tool_call": true,
"temperature": false,
"options": {
"reasoningEffort": "high",
"reasoningSummary": "auto",
"include": ["reasoning.encrypted_content"]
},
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
}
}
}
}
}OpenCode v1 full setup & extras — installation and OMO setup.
OpenClaw openclaw.json
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.openclaw/openclaw.json |
| Windows | %USERPROFILE%\.openclaw\openclaw.json |
Open openclaw.json at the path for your system and add this configuration:
{
agents: {
defaults: {
model: {
primary: "openai/gpt-6.1-sol",
list: [{ id: "background", model: "openai/gpt-6-luna" }],
},
compaction: { reserveTokens: 128000 },
},
},
models: {
mode: "merge",
providers: {
openai: {
baseUrl: "http://localhost:4000/v1",
apiKey: "${CODEX_POOLER_API_KEY}",
api: "openai-responses",
agentRuntime: { id: "openclaw" },
timeoutSeconds: 300,
models: [
{
id: "gpt-6-luna",
name: "GPT-6 Luna via Codex Pooler",
reasoning: true,
input: ["text", "image"],
contextWindow: 828400,
contextTokens: 828400,
maxTokens: 128000,
},
{
id: "gpt-6.1-sol",
name: "GPT-6.1 Sol via Codex Pooler",
reasoning: true,
input: ["text", "image"],
contextWindow: 828400,
contextTokens: 828400,
maxTokens: 128000,
},
{
id: "gpt-6-astra",
name: "GPT-6 Astra via Codex Pooler",
reasoning: true,
input: ["text", "image"],
contextWindow: 828400,
contextTokens: 828400,
maxTokens: 128000,
},
],
},
},
},
}Restart OpenClaw and start a new conversation.
Full setup & extras — background tasks, more models and advanced options.
Hermes Agent config.yaml
| System | Folder for .env and config.yaml |
|---|---|
| macOS / Linux | ~/.hermes/ |
| Windows | %LOCALAPPDATA%\hermes\ |
Open .env in the folder for your system and add your Pool API key and
Codex Pooler address. If you set HERMES_HOME, use that folder instead:
OPENAI_API_KEY=<pool-api-key>
OPENAI_BASE_URL=http://localhost:4000/v1
STT_OPENAI_BASE_URL=http://localhost:4000/v1Add this to config.yaml in the same folder, then restart Hermes:
model:
default: gpt-6.1-sol
provider: openai-api
base_url: http://localhost:4000/v1
api_mode: codex_responses
context_length: 828400
supports_vision: true
agent:
image_input_mode: native
api_max_retries: 2
auto_recovery_cycles: 1
image_gen:
provider: openai
model: gpt-image-2.5-flare-medium
stt:
enabled: true
provider: openai
openai:
model: gpt-4o-transcribe
compression:
threshold: 0.95
auxiliary:
compression:
timeout: 900This setup includes image generation and voice-to-text. To switch the chat
model, set model.default to a model available to your Pool.
Keep all three addresses pointed at your Codex Pooler instance. Your Pool must
also offer the image and transcription models to use those features.
Full setup & extras — images, speech-to-text, priority processing and troubleshooting.
Pi models.json
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.pi/agent/models.json |
| Windows | %USERPROFILE%\.pi\agent\models.json |
Open models.json at the path for your system and add this configuration:
{
"providers": {
"codex-pooler": {
"name": "Codex Pooler",
"baseUrl": "http://localhost:4000/v1",
"api": "openai-responses",
"apiKey": "$CODEX_POOLER_API_KEY",
"authHeader": true,
"models": [
{
"id": "gpt-6-luna",
"name": "GPT-6 Luna via Codex Pooler",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 828400,
"maxTokens": 128000,
"thinkingLevelMap": { "xhigh": "xhigh" }
},
{
"id": "gpt-6.1-sol",
"name": "GPT-6.1 Sol via Codex Pooler",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 828400,
"maxTokens": 128000,
"thinkingLevelMap": { "xhigh": "xhigh" }
},
{
"id": "gpt-6-astra",
"name": "GPT-6 Astra via Codex Pooler",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 828400,
"maxTokens": 128000,
"thinkingLevelMap": { "xhigh": "xhigh" }
}
]
}
}
}Add these defaults to settings.json in the same folder:
{
"defaultProvider": "codex-pooler",
"defaultModel": "gpt-6.1-sol",
"enabledModels": [
"codex-pooler/gpt-6-luna",
"codex-pooler/gpt-6.1-sol",
"codex-pooler/gpt-6-astra"
],
"compaction": { "reserveTokens": 128000 }
}Then start Pi:
piFull setup & extras — installation, default models and extra options.
OMP models.yml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.omp/agent/models.yml |
| Windows | %USERPROFILE%\.omp\agent\models.yml |
Open models.yml at the path for your system and add this configuration:
providers:
codex-pooler:
baseUrl: http://localhost:4000/v1
api: openai-responses
apiKey: CODEX_POOLER_API_KEY
authHeader: true
remoteCompaction:
enabled: true
api: openai-codex-responses
endpoint: http://localhost:4000/backend-api/codex/responses/compact
v2StreamingEnabled: true
v2Endpoint: http://localhost:4000/backend-api/codex/responses
models:
- id: gpt-6-luna
name: GPT-6 Luna via Codex Pooler
reasoning: true
input: [text, image]
compat:
streamIdleTimeoutMs: 300000
contextWindow: 828400
maxTokens: 128000
- id: gpt-6.1-sol
name: GPT-6.1 Sol via Codex Pooler
reasoning: true
input: [text, image]
compat:
streamIdleTimeoutMs: 300000
contextWindow: 828400
maxTokens: 128000
- id: gpt-6-astra
name: GPT-6 Astra via Codex Pooler
reasoning: true
input: [text, image]
compat:
streamIdleTimeoutMs: 300000
contextWindow: 828400
maxTokens: 128000Add these defaults to config.yml in the same folder:
startup:
setupWizard: false
enabledModels:
- codex-pooler/gpt-6-luna
- codex-pooler/gpt-6.1-sol
- codex-pooler/gpt-6-astra
modelProviderOrder:
- codex-pooler
modelRoles:
default: codex-pooler/gpt-6.1-sol:high
smol: codex-pooler/gpt-6-luna:low
tiny: codex-pooler/gpt-6-luna:minimal
slow: codex-pooler/gpt-6-astra:xhigh
plan: codex-pooler/gpt-6-astra:xhigh
task: codex-pooler/gpt-6.1-sol:high
vision: codex-pooler/gpt-6.1-sol:high
advisor: codex-pooler/gpt-6.1-sol:medium
commit: codex-pooler/gpt-6-luna:minimal
designer: codex-pooler/gpt-6-astra:high
compaction:
enabled: true
thresholdPercent: 80
reserveTokens: 128000
remoteStreamingV2Enabled: true
midTurnEnabled: true
handoffSaveToDisk: true
methodOrder: [remote, soft]The 80% trigger leaves room for compaction instructions and recent tool results. Keep reserveTokens aligned with the output budget. For an existing session already near its limit, see the compaction recovery notes.
Then start OMP:
ompFull setup & extras — installation, model choices and long conversations.
Cursor Settings → Models → API Keys
In Settings → Models → API Keys, enable OpenAI API Key and
Override OpenAI Base URL. Enter your Pool API key and a public HTTPS URL
such as https://codex-pooler.example.com/v1, then select a model available
to your Pool.
Cursor BYOK requires Pro or higher. Requests pass through Cursor's servers, so localhost and private LAN URLs do not work. Use an explicit model instead of Auto mode.
Full setup & extras — prerequisites, model selection and connection checks.
Kilo Code kilo.jsonc
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.config/kilo/kilo.jsonc |
| Windows | %USERPROFILE%\.config\kilo\kilo.jsonc |
Open kilo.jsonc at the path for your system and add this configuration:
{
"$schema": "https://app.kilo.ai/config.json",
"model": "codex-pooler/gpt-6.1-sol",
"enabled_providers": ["codex-pooler"],
"provider": {
"codex-pooler": {
"options": {
"apiKey": "{env:CODEX_POOLER_API_KEY}",
"baseURL": "http://localhost:4000/v1"
},
"models": {
"gpt-6-luna": {
"name": "GPT-6 Luna via Codex Pooler",
"tool_call": true,
"reasoning": true,
"temperature": false,
"attachment": true,
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
},
"gpt-6.1-sol": {
"name": "GPT-6.1 Sol via Codex Pooler",
"tool_call": true,
"reasoning": true,
"temperature": false,
"attachment": true,
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
},
"gpt-6-astra": {
"name": "GPT-6 Astra via Codex Pooler",
"tool_call": true,
"reasoning": true,
"temperature": false,
"attachment": true,
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"limit": { "context": 828400, "input": 828400, "output": 64000 }
}
}
}
}
}Restart Kilo and select the Codex Pooler model.
Full setup & extras — installation, model choices and extra options.
Trae Settings -> Models
Sign in to Trae, open Settings → Models, and add a custom model:
| Field | Value |
|---|---|
| API format | OpenAI Chat Completions |
| Custom Request URL | http://localhost:4000/v1 |
| Full URL | Off |
| Model ID | gpt-6.1-sol |
| API key | Your Pool API key |
| Model Series | Default |
Repeat this setup with another Model ID to add more models available to your Pool.
Do not add a trailing slash to the URL. Save the model, turn Auto Mode off in the agent model picker, and select it under Custom Models.
Full setup & extras — Trae CN, extra settings and connection checks.
Aider .aider.conf.yml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.aider.conf.yml |
| Windows | %USERPROFILE%\.aider.conf.yml |
Open .aider.conf.yml at the path for your system and add these settings:
model: openai/gpt-6.1-sol
openai-api-base: http://localhost:4000/v1Keep the Pool API key in the environment, then start Aider from your repository:
macOS / Linux / WSL
export OPENAI_API_KEY="$CODEX_POOLER_API_KEY"
aiderWindows PowerShell
$env:OPENAI_API_KEY = $env:CODEX_POOLER_API_KEY
aiderIf Aider does not recognize the model, follow the additional setup in the full guide.
To switch models, set model to a model available to your Pool, keeping the
openai/ prefix.
Full setup & extras — additional model setup and editing files.
Continue config.yaml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.continue/config.yaml |
| Windows | %USERPROFILE%\.continue\config.yaml |
Save your Pool API key in Continue as CODEX_POOLER_API_KEY using the
secret setup instructions.
Then open config.yaml at the path for your system and add this configuration:
name: Codex Pooler
version: 1.0.0
schema: v1
models:
- name: GPT-6 Luna via Codex Pooler
provider: openai
model: gpt-6-luna
apiBase: http://localhost:4000/v1
apiKey: "${{ secrets.CODEX_POOLER_API_KEY }}"
contextLength: 828400
defaultCompletionOptions:
maxTokens: 128000
roles: [chat, edit, apply, summarize]
capabilities: [tool_use, image_input]
- name: GPT-6.1 Sol via Codex Pooler
provider: openai
model: gpt-6.1-sol
apiBase: http://localhost:4000/v1
apiKey: "${{ secrets.CODEX_POOLER_API_KEY }}"
contextLength: 828400
defaultCompletionOptions:
maxTokens: 128000
roles: [chat, edit, apply, summarize]
capabilities: [tool_use, image_input]
- name: GPT-6 Astra via Codex Pooler
provider: openai
model: gpt-6-astra
apiBase: http://localhost:4000/v1
apiKey: "${{ secrets.CODEX_POOLER_API_KEY }}"
contextLength: 828400
defaultCompletionOptions:
maxTokens: 128000
roles: [chat, edit, apply, summarize]
capabilities: [tool_use, image_input]Select this configuration and the Codex Pooler model in Continue.
Full setup & extras — saving your API key, extra settings and CLI usage.
Cline
For Cline CLI, save the connection settings with:
macOS / Linux / WSL
cline auth \
--provider openai \
--apikey "$CODEX_POOLER_API_KEY" \
--baseurl http://localhost:4000/v1 \
--modelid gpt-6.1-solWindows PowerShell
cline auth --provider openai --apikey "$env:CODEX_POOLER_API_KEY" --baseurl http://localhost:4000/v1 --modelid gpt-6.1-solStart Cline and use the saved model. In the IDE extension, choose OpenAI Compatible and enter the same address, API key and model.
Set --modelid to a model available to your Pool.
Full setup & extras — IDE setup, extra settings and connection checks.
Goose config.yaml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.config/goose/config.yaml |
| Windows | %APPDATA%\Block\goose\config\config.yaml |
Open config.yaml at the path for your system and add this configuration:
GOOSE_PROVIDER: openai
GOOSE_MODEL: gpt-6.1-sol
OPENAI_HOST: http://localhost:4000
OPENAI_BASE_PATH: v1/chat/completions
GOOSE_CONTEXT_LIMIT: 828400
GOOSE_MAX_TOKENS: 128000Run this in your terminal before starting Goose:
macOS / Linux / WSL
export OPENAI_API_KEY="$CODEX_POOLER_API_KEY"Windows PowerShell
$env:OPENAI_API_KEY = $env:CODEX_POOLER_API_KEYTo switch models, set GOOSE_MODEL to a model available to your Pool.
Full setup & extras — tools, extra settings and Windows setup.
DeepSeek Harness (dsh) cordis.patch.yml
| System | Configuration file |
|---|---|
| macOS / Linux | ~/.dsh/profiles/headless/cordis.patch.yml |
| Windows | %USERPROFILE%\.dsh\profiles\headless\cordis.patch.yml |
Run dsh --profile headless --dump-default-config once to create the
configuration, then open cordis.patch.yml at the path for your system and
add the following. If you set DSH_HOME, use its profiles/headless folder:
- id: llm-pi-ai
config:
providers:
codex-pooler:
apiKeyEnv: CODEX_POOLER_API_KEY
api: openai-responses
compat:
supportsStrictMode: true
baseURL: http://localhost:4000/v1
models:
- id: gpt-6-luna
contextWindow: 828400
- id: gpt-6.1-sol
contextWindow: 828400
- id: gpt-6-astra
contextWindow: 828400
- id: agent-default-model
config:
provider: codex-pooler
model: gpt-6.1-solKeep any existing settings in these entries when adding the configuration.
Start DeepSeek Harness with dsh --profile headless.
Full setup & extras — installation, tools and extra settings.
Windmill AI customai workspace provider
Store a dedicated Pool API key as a Windmill secret variable, then create a
customai resource that references it:
description: Codex Pooler API credentials for Windmill AI
value:
api_key: '$var:u/<owner>/codex_pooler'
base_url: http://localhost:4000/v1
headers: {}
resource_type: customaiIn workspace AI settings, use the resource you just created and add all three models. The matching configuration is:
providers:
customai:
resource_path: u/<owner>/codex_pooler
models:
- gpt-6-luna
- gpt-6.1-sol
- gpt-6-astra
default_model:
provider: customai
model: gpt-6.1-sol
metadata_model:
provider: customai
model: gpt-6-lunaUse a URL reachable from the Windmill server; private addresses require
ALLOW_PRIVATE_AI_BASE_URLS=true on that server.
Full setup & extras — resource creation, workspace configuration and supported features.
OpenHands
In OpenHands Agent Canvas, select the native OpenHands agent. Under Settings → LLM → Add LLM Profile → Advanced, set:
- Custom Model:
openai/gpt-6-luna(or another exact model ID served by your Pool) - Base URL:
https://codex-pooler.example.com/v1, reachable from the Canvas backend - API Key: your Pool API key
Link this LLM profile under Settings → Agent and use Full serving mode for the verified tool workflow. For a local Pooler with Canvas on Docker Desktop, use http://host.docker.internal:4000/v1.
Full setup & extras — Docker setup, screenshots and model profiles.
OpenAI Python SDK
With OpenAI Python SDK installed and CODEX_POOLER_API_KEY set, point the
client at Codex Pooler's /v1 endpoint:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["CODEX_POOLER_API_KEY"],
base_url="http://localhost:4000/v1",
)
response = client.responses.create(
model="gpt-6.1-sol",
input="Write a one-sentence status update.",
)
print(response.output_text)Use a model available to your Pool.
Full setup & extras — streaming, tools, media and API compatibility.
OpenAI Node SDK
With OpenAI Node SDK installed and CODEX_POOLER_API_KEY set, point the
client at Codex Pooler's /v1 endpoint:
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.CODEX_POOLER_API_KEY,
baseURL: "http://localhost:4000/v1",
});
const response = await client.responses.create({
model: "gpt-6.1-sol",
input: "Write a one-sentence status update.",
});
console.log(response.output_text);Use a model available to your Pool.
Full setup & extras — streaming, tools, media and API compatibility.
Vercel AI SDK
With Vercel AI SDK installed and CODEX_POOLER_API_KEY set, point the
client at Codex Pooler's /v1 endpoint:
import { createOpenAI } from "@ai-sdk/openai";
import { generateText } from "ai";
const pooler = createOpenAI({
apiKey: process.env.CODEX_POOLER_API_KEY,
baseURL: "http://localhost:4000/v1",
});
const { text } = await generateText({
model: pooler.responses("gpt-6.1-sol"),
prompt: "Write a one-sentence status update.",
});
console.log(text);Use a model available to your Pool.
Full setup & extras — streaming, tools, media and API compatibility.
This runs the published release image with a local Postgres database. It is the
fastest way to try Codex Pooler on a laptop or small server.
For normal use, run a versioned, tagged stable release from GitHub Releases. The latest image tag follows the most recently published release, but a version tag keeps the installation reproducible; run from source only in Local Development.
Prerequisites:
- Docker with Compose
- Git, if you are cloning the repository
openssl
Start Codex Pooler:
git clone https://github.com/icoretech/codex-pooler.git
cd codex-pooler
# Run the latest tagged stable release. Find its version at
# https://github.com/icoretech/codex-pooler/releases, then substitute it here.
export CODEX_POOLER_IMAGE_TAG=<release-tag>
scripts/self-host/generate-env.sh
docker compose pull
docker compose up -dThe first run pulls the app and Postgres images, waits for Postgres health, runs the migration container, then starts the web app.
Open http://localhost:4000. On the first visit, create the owner account at
/bootstrap, then sign in and start with /admin/pools.
To verify the first-run redirect before opening a browser:
curl -sS -D - -o /dev/null http://localhost:4000/ | grep -i '^location: /bootstrap'
curl -fsS http://localhost:4000/bootstrap/statusThe status endpoint should return {"status":"ok","bootstrap":"pending"} on a
fresh database.
Useful commands:
docker compose ps
docker compose logs -f app
docker compose downTo upgrade an existing Compose install, set CODEX_POOLER_IMAGE_TAG in .env
to the target tagged stable release, then run:
docker compose pull
docker compose up -dThe Compose stack has a one-shot migrate service. It waits for Postgres, runs
release migrations, imports the bundled pricing snapshot, and exits before the
web app starts. Normal app boot does not migrate the database by itself. If a
failed migration needs to be rerun after fixing configuration or database
access, run:
docker compose up -d db
docker compose run --rm migrate
docker compose up -d appUse http://localhost:4000 for the default Compose stack even if the Phoenix
startup banner prints an endpoint URL such as https://localhost; the Compose
port mapping is the local URL to open. The release image includes the OS
timezone database used for operator timezone display.
To remove the local database too:
docker compose down -vAfter bootstrap:
- Create a Pool in
/admin/pools - Link, import, or invite one or more Codex accounts in
/admin/upstreams - Create a Pool API key in
/admin/api-keys - Point Codex or SDK clients at one of the runtime base URLs:
One upstream account is enough for a working setup. Additional upstreams expand the same Pool into shared capacity without changing client credentials.
Prefer OAuth in /admin/upstreams for new operator-managed upstream
accounts when browser authorization is practical. The admin dialog links the
account, stores resulting credential material through encrypted upstream secret
storage, and stays metadata-only after completion. Use Import only when an
existing Codex auth.json is the right source of credentials.
Treat an imported Codex auth.json as owned by Codex Pooler after import. Do
not keep using the same auth.json from another Codex install, machine, or
automation unless you accept that provider refresh-token rotation can invalidate
one copy and move the account to reauth_required.
Hosted invite onboarding and the OAuth device-code fallback use OpenAI's Codex
device-code authorization. This setup is only needed for hosted invites and the
OAuth device-code fallback; browser OAuth linking does not depend on it. For a
personal ChatGPT account, open chatgpt.com, go to Settings > Security, and enable
Enable device code authorization for Codex. For workspace-managed accounts,
ask a workspace admin to enable device-code login for Codex in the workspace
permissions. OpenAI's Codex authentication docs
describe device-code login. The invite or fallback flow can fail at the OpenAI
approval step when device-code authorization is off.
Codex backend base URL: http://localhost:4000/backend-api/codex
OpenAI SDK base URL: http://localhost:4000/v1
Use the generated Pool API key as the bearer token. That key represents the Pool, not a single Codex account, so Codex Pooler can pick the best eligible account for each request. Raw API keys are shown only once when created or rotated.
The first bootstrap account is an instance_owner. Owners have instance-wide
administration access: they create Pools, assign operators to Pools, manage
operators, inspect global jobs, and change system settings.
Additional operators can be owners or instance_admins. Instance admins are
Pool-scoped: they can work only with active Pools assigned to them and metadata
derived from those Pools. If no Pools are assigned, the admin UI shows empty
Pool-scoped states instead of exposing global data. Archiving or deleting a Pool
removes future instance-admin visibility for that Pool; historical request and
audit rows for archived or deleted Pools remain owner-only.
Use the client guides when wiring a specific tool. At a glance, clients pick one of two public shapes:
- Codex backend clients use
/backend-api/codexfor Codex-native behavior such as sessions, compacting, files, audio, images, and backend websockets. - OpenAI-compatible clients use
/v1for supported SDK-style Responses, chat, files, audio, image, and model-list calls.
Both paths authenticate with Pool API keys and route through the same Pool policy, account health, model support, quota evidence, session continuity, and metadata-only accounting. Codex Pooler is intentionally not a wildcard OpenAI proxy; unsupported API areas fail predictably. For exact route details, use the Runtime Routes reference and the OpenAI-compatible client guide.
Codex Pooler includes an optional metadata-only MCP endpoint at /mcp for
trusted operators who want an MCP host to inspect Pools, upstream accounts, Pool
API key metadata, operators, invites, request logs, audit logs, and MCP service
status. This operator add-on is not required for Codex Pooler runtime clients.
The service is read-only and has no mutation tools. It uses the same owner vs
assigned-Pool visibility model as the admin UI, but connected MCP hosts can read
the metadata visible to that operator, so only connect hosts you trust with that
view.
MCP access uses operator-owned bearer MCP tokens, not Pool API keys, browser
sessions, cookies, query tokens, invite tokens, upstream tokens, or custom
headers. Operators manage their own MCP account gate and tokens from
/admin/settings?tab=account; the instance-wide service gate is managed from
/admin/system. Both gates must be enabled before a token works. Raw MCP tokens
are shown only once when created, and per-key usage tracking, counters, last IP,
and user-agent history are intentionally not stored.
The /mcp route inherits the runtime ingress IP allowlist and trusted-proxy
settings. If the allowlist is empty, the firewall is off; if it is configured,
the resolved client IP must match before MCP authentication or tool dispatch.
scripts/self-host/generate-env.sh writes a local .env with generated
secrets and local defaults. Keep that file private and don't reuse generated
values between public installs.
Environment variables are only for values the release needs before it can read the database:
CODEX_POOLER_IMAGEandCODEX_POOLER_IMAGE_TAG, the release image to runCODEX_POOLER_HTTP_PORT, the local host port, default4000DATABASE_URL, the Postgres connection used by the appSECRET_KEY_BASE, Phoenix signing and encryption secretPHX_HOST,PORT, andPHX_SERVER, HTTP endpoint boot settingsOBAN_MODEandOBAN_JOBS_QUEUE_LIMIT, release role and queue topologyDNS_CLUSTER_QUERY, plus release distribution variables when clustering is onCODEX_POOLER_TOTP_ENCRYPTION_KEYandCODEX_POOLER_TOTP_KEY_VERSION, TOTP encryption root and versionCODEX_POOLER_UPSTREAM_SECRET_KEYandCODEX_POOLER_UPSTREAM_SECRET_KEY_VERSION, upstream secret encryption root and version; the key must be 32 raw bytes or base64-encoded 32 bytes
Operational controls such as file limits, ingress trust, gateway diagnostics,
route-class admission, circuit thresholds, metrics auth, operator email, model
metadata, upstream timeouts, the OpenAI pricing catalog URL, and SMTP delivery
live in DB-managed Instance Settings under /admin/system. Live settings apply
to new runtime work through the settings cache. Cached settings reload after save
through PubSub invalidation; existing leases, in-flight requests, and open streams
keep the values they started with. The exception is an already-open Responses
websocket: after a locally applied runtime-firewall settings snapshot, it
re-evaluates the client IP captured during its handshake.
Secret Instance Settings stay write-only in the UI. The metrics bearer token is stored only as a keyed HMAC digest, fingerprint, and key version. The SMTP password is stored encrypted with key version metadata and is recovered only for mail send or credential-test paths.
Choose the deployment path that matches how you want to operate Codex Pooler:
| Path | Use it for | Start here |
|---|---|---|
| Docker Compose | A quick self-hosted install on a laptop, lab server, or small single node | Docker Compose deployment guide |
| Kubernetes | Production installs, managed ingress, external Postgres, metrics, and separate runtime roles | Helm deployment guide |
The Kubernetes path uses the
icoretech/codex-pooler chart
from the iCoreTech Helm repository. The chart runs one release image as separate
web, worker, scheduler, and migration roles. For a real install, pin the chart
--version; the chart defaults image.tag to the matching appVersion.
👉 codex-action runs OpenAI Codex CLI non-interactively in GitHub Actions workflows
👉 codex-docker provides a multi-arch OpenAI Codex CLI Docker image built from official upstream releases
Local development runs Phoenix on the host and Postgres through the dev compose file:
make devmake dev starts Postgres, prepares the database, imports the vendored OpenAI
pricing feed, and starts the Phoenix server on http://localhost:4000. Logs
are written to the local development server log.
Development seeds are optional and only run through the explicit seed task. To create a compact idempotent operator baseline with one owner plus four example operators, run:
mix dev.seed compactAll seeded operators use dev-password-123.
To recreate a fuller fake dataset for exercising admin UI states without real accounts or real request data, run:
mix dev.seed fullThe full seed is idempotent and replaces only deterministic dev-* fake rows
owned by the development seed namespace. It includes active/disabled pools,
active/paused/revoked API keys, upstream accounts in active/refresh/reauth/paused
states, quota windows, request logs, invites, audit events, and job rows.
Common checks:
mix precommit
mix quality
docker compose -f docker-compose.dev.yml config
docker build .Helm chart validation lives with the published chart in the iCoreTech Helm repository when Kubernetes deployment behavior or values change.
mix test and mix precommit serialize database-backed test runs with a
PostgreSQL advisory lock keyed by the configured test database, so concurrent
local runs wait instead of deadlocking the shared sandbox database.

















{ "$schema": "https://opencode.ai/config.json", "model": "codex-pooler/gpt-6.1-sol", "agents": { "title": { "model": "codex-pooler/gpt-6-luna" } }, "providers": { "codex-pooler": { "package": "@opencode/ai/providers/openai/responses", "settings": { "baseURL": "http://localhost:4000/v1", "apiKey": "{env:CODEX_POOLER_API_KEY}", "transport": "http", "compaction": { "type": "summary" } }, "models": { "gpt-6-luna": { "modelID": "gpt-6-luna", "capabilities": { "tools": true, "input": ["text", "image"], "output": ["text"] }, "limit": { "context": 828400, "input": 828400, "output": 32000 }, "settings": { "reasoningEffort": "high", "reasoningSummary": "auto" } }, "gpt-6.1-sol": { "modelID": "gpt-6.1-sol", "capabilities": { "tools": true, "input": ["text", "image"], "output": ["text"] }, "limit": { "context": 828400, "input": 828400, "output": 32000 }, "settings": { "reasoningEffort": "high", "reasoningSummary": "auto" } }, "gpt-6-astra": { "modelID": "gpt-6-astra", "capabilities": { "tools": true, "input": ["text", "image"], "output": ["text"] }, "limit": { "context": 828400, "input": 828400, "output": 32000 }, "settings": { "reasoningEffort": "high", "reasoningSummary": "auto" } } } } } }