This repository contains a FastAPI gateway for Codex automation.
This project is managed with uv and uses pyproject.toml for dependencies and command entry points.
uv syncuv run --extra dev pytest -quv run codex-gateway --repo /abs/path/to/repo --host 0.0.0.0 --port 8000uv tool install .After installation, the CLI entry point is:
codex-gatewayThe project exposes the codex-gateway script through [project.scripts] in pyproject.toml.
-
REPO- Purpose: repository root used by gateway file and git endpoints.
- Default: current process working directory.
-
HOST- Purpose: HTTP bind host.
- Default:
0.0.0.0.
-
PORT- Purpose: HTTP bind port.
- Default:
8000.
-
CODEX_TIMEOUT_SECONDS- Purpose: timeout for CLI command execution (
backend=exec). - Default:
120.
- Purpose: timeout for CLI command execution (
-
model(request payload field)- Purpose: overrides model for a single
runCodexTaskcall.
- Purpose: overrides model for a single
-
reasoning_effort(request payload field)- Purpose: overrides reasoning depth for a single
runCodexTaskcall. - Supported values:
low,medium,high,xhigh.
- Purpose: overrides reasoning depth for a single
-
GATEWAY_MAX_OUTPUT_CHARS- Purpose: maximum returned size for
stdoutandstderrin command results. - Default:
60000.
- Purpose: maximum returned size for
-
GATEWAY_JOB_POLL_AFTER_SECONDS- Purpose: recommended polling interval returned by async job endpoints.
- Default:
15.
-
GATEWAY_JOB_FIRST_POLL_GRACE_SECONDS- Purpose: allows one early poll shortly after job creation.
- Default:
3.
-
GATEWAY_JOB_TTL_SECONDS- Purpose: retention window for completed/failed async jobs in memory.
- Default:
7200.
-
GATEWAY_JOB_MAX_ITEMS- Purpose: max number of async jobs stored in memory before old entries are pruned.
- Default:
500.
-
GATEWAY_JOB_LONG_POLL_ENABLED- Purpose: enables server-side long polling for job status endpoint.
- Values:
1enable,0disable. - Default:
1.
-
GATEWAY_JOB_LONG_POLL_MAX_SECONDS- Purpose: max hold time for a single status request when polled too early.
- Default:
20.
-
GATEWAY_CODEX_SYNC_MAX_WAIT_SECONDS- Purpose: max wait for
POST /codexbefore returningin_progresswithjob_id. - Default:
20.
- Purpose: max wait for
-
GATEWAY_JOB_DEBUG_TRACE_ENABLED- Purpose: capture compact WS event trace per job for diff parser diagnostics.
- Values:
1enable,0disable. - Default:
1.
-
GATEWAY_JOB_DEBUG_TRACE_MAX_ITEMS- Purpose: max number of debug trace rows stored per job.
- Default:
400.
-
APP_SERVER_URL- Purpose: default WebSocket URL for
backend=app_server_ws. - Default:
ws://127.0.0.1:4500.
- Purpose: default WebSocket URL for
-
APP_SERVER_BEARER_TOKEN- Purpose: optional bearer token passed when connecting to app-server.
- Default: unset.
-
APP_SERVER_TIMEOUT_SECONDS- Purpose: timeout for waiting app-server messages.
- Default:
180.
-
CODEX_GATEWAY_API_KEY- Purpose: inbound bearer token for gateway HTTP auth.
- Usage: clients must send
Authorization: Bearer <token>. - Default: empty (auth inactive unless key is set).
-
GATEWAY_API_KEY_ENV- Purpose: env var name used by CLI flag
--api-key-env. - Default:
CODEX_GATEWAY_API_KEY.
- Purpose: env var name used by CLI flag
-
GATEWAY_API_KEY_HEADER- Purpose: alternative header name for API key auth.
- Default:
x-api-key. - Note: gateway accepts both
Authorization: Bearer <token>and this header.
-
GATEWAY_DISABLE_AUTH- Purpose: disable auth check even if API key is configured.
- Values:
1disables auth, any other value keeps auth behavior. - Default:
0.
-
GATEWAY_SPAWN_APP_SERVER- Purpose: auto-start
codex app-serveras a subprocess. - Values:
1to enable. - Default:
0.
- Purpose: auto-start
-
GATEWAY_SPAWN_APP_SERVER_LISTEN- Purpose: listen URL used for managed app-server process.
- Default: inherited from
APP_SERVER_URL.
-
CODEX_BIN- Purpose: Codex executable path used by managed app-server mode.
- Default:
codex.
GATEWAY_PROTOCOL_SCHEMA_CODEX_BIN- Purpose: Codex binary used for protocol schema generation.
- Default:
codex.
-
GATEWAY_DEBUG- Purpose: enable debug mode.
- Values:
1to enable. - Default:
0.
-
GATEWAY_LOG_LEVEL- Purpose: log severity level.
- Values:
DEBUG,INFO,WARNING,ERROR. - Default:
INFO.
-
GATEWAY_LOG_FILE- Purpose: optional log file path.
- Default: unset (stdout logging only).
-
GATEWAY_LOG_REQUESTS- Purpose: request logging toggle (method, path, status, duration).
- Values:
1to enable. - Default:
1.
Structured lifecycle logs:
- Protocol registry access:
event=protocol.listevent=protocol.get
- Approval lifecycle:
event=approval.requestevent=approval.decisionevent=approval.applied
gpt_action_schema.template.json: OpenAPI schema template for Custom GPT Actions.gpt_system_instruction.template.txt: system instruction template for the Custom GPT.GET /gpt-system-instruction.txt: returns current system instruction text used for GPT setup/testing.
The imported GPT OpenAPI is intentionally minimal:
getGatewayHealthgetCapabilitiesgetContextOptionsswitchProjectContextcreateCodexJobgetCodexJobgetCodexJobEventspostCodexJobApprovalgetCodexJobResult
Dynamic capabilities:
GET /capabilitiesreturns runtimerequest_patchin OpenAI Responses API format (input+tools).- Capability rules are encoded in each function
description. - GPT should refresh capabilities before write/admin steps and after recoverable errors.
Gateway exposes a ready-to-import schema endpoint:
GET /gpt-action-schema.json
Behavior:
- Returns JSON schema based on
gpt_action_schema.template.json. - Replaces
https://<GATEWAY_HOST>with the current request host/protocol (supports reverse proxy headers). - Returns schema revision hash in body field
x-schema-hashand headersETag/X-Schema-Sha256. - Intended for ChatGPT "Import from URL" flow.
- Endpoint is protected by gateway auth by default.
- Set
GATEWAY_PUBLIC_SCHEMA=1only if you explicitly want unauthenticated schema access. - Optional import-only query key:
GATEWAY_SCHEMA_IMPORT_KEY=<value>GATEWAY_SCHEMA_IMPORT_KEY_PARAM=<param_name>(default:schema_key)- Example:
https://cdx.avfserv.net/gpt-action-schema.json?schema_key=<value>
Startup logs also print ready-to-use URLs for:
- action schema import
- system instruction endpoint
Critical limitation:
- Keep the Custom GPT chat in the base Chats area in ChatGPT.
- Moving this Custom GPT conversation into Projects can break Actions execution for this integration.
- If Actions suddenly stop working after moving the chat, move back to a regular chat and re-import/re-open the action configuration.
The gateway exposes an in-memory protocol schema registry:
GET /protocol/schemas- Returns full schema index (
id,name,group,path,sha256,size_bytes).
- Returns full schema index (
GET /protocol/schema?schema_id=<id>- Returns full JSON schema by id.
Registry lifecycle:
- startup generates schemas via Codex into a temporary directory, loads them into memory, then removes temporary files,
- temporary generated schema files are deleted after loading,
- requests are served from memory only.
For non-interactive environments (like gateway-driven workflows), use bootstrap before the first Codex task in a new directory:
POST /projects/bootstrap- Creates a new project folder in
base_path(or in parent directory ofREPOif omitted). - Optionally initializes git with
git init -b main(enabled by default). - Returns ready-to-use
cwdfor/codexor/codex/jobs.
- Creates a new project folder in
Request example:
{
"project_name": "auto-tokenizer",
"base_path": "/home/anfedoro/projects",
"git_init": true
}To avoid HTTP/proxy timeouts for long Codex operations, use async job endpoints:
POST /codex/jobswith body:{ "payload": { ...CodexRequest..., "diff_mode": "live|final_only|off" } }
- Receive
job_idand immediate status (queuedorrunning). - Poll
GET /codex/jobs/{job_id}everypoll_after_seconds(default 15s).- Prefer
retry_after_secondsfrom response when present. - Endpoint always returns
200for existing jobs and includes retry hints. - If polled too early, gateway can hold the request (long-poll) and respond later to reduce request spam.
- Long-poll is event-driven only for significant updates (completed assistant message, turn completion, failures).
- High-frequency WS noise (deltas/started/token-usage updates) does not wake polling early.
- Prefer
Event buffering and thread correlation:
- Gateway keeps buffered per-job events in memory (sequence-based) so updates are not tied to an active HTTP poll call.
- Read buffered deltas via:
GET /codex/jobs/{job_id}/events?since_seq=<n>
- Job status includes:
event_seqlast_drained_seqpending_events_count
- If a new
resumeaction for the samethread_idis requested while pending buffered events exist, gateway returns409and asks to drain events first. - Thread isolation is strict (
thread_id -> jobs -> events); jobs/events are not mixed across threads.
- For diff orchestration:
- If
diff_mode=live: callGET /codex/jobs/{job_id}/diff/live?since_version=<n>whendiff_live_versionincreases. - If
diff_mode=final_only: wait fordiff_final_available=true, then callGET /codex/jobs/{job_id}/diff/final. - If
diff_mode=off: skip diff endpoints unless diagnostics are needed.
- If
- When status is
completed, callGET /codex/jobs/{job_id}/result. - If status is
waiting_approval, ask user and submit decision:POST /codex/jobs/{job_id}/approval- body:
request_iddecision:approve_once | approve_all_similar | deny | guidanceguidance_text(required forguidance)
Notes:
GET /codex/jobs/{job_id}/resultreturns:200for completed jobs,409if the job is not finished yet,502if the job failed.
- Polling endpoints are read-only and should not require repeated user confirmations in GPT flow after job creation.
GET /codex/jobs/{job_id}returns:200with status andretry_after_seconds/next_poll_after_athints.- Includes
progress_summary.items[]with filtered job milestones (seq,kind,elapsed_sec,elapsed_label,text). - Includes diff signals:
diff_live_availablediff_live_versiondiff_final_availablediff_hintdiagnostic_diff_available
- Includes approval signals:
approval_requiredapproval_requestapproval_policy_count
GET /codex/jobs/{job_id}/diff/live:- Returns only incremental diff updates after
since_version.
- Returns only incremental diff updates after
GET /codex/jobs/{job_id}/diff/final:- Returns final diff (
409until ready). - On failed jobs may return diagnostic diff when available.
- Returns final diff (
GET /codex/jobs/{job_id}/debug-trace:- Returns compact WS-event trace (method + key-shape hints) to diagnose why diff extraction did or did not trigger.
POST /codex remains available for compatibility.
For backend=app_server_ws, gateway now:
- validates
cwdearly; if the directory does not exist, returns400immediately, - starts internal async job,
- waits up to
GATEWAY_CODEX_SYNC_MAX_WAIT_SECONDS, - if finished quickly: returns normal final Codex result,
- otherwise returns:
status: "in_progress"job_id- current
jobstatus includinglast_update_textwhen available.