{"id":48845489,"url":"https://github.com/labring/codex-gateway","last_synced_at":"2026-04-15T05:02:51.280Z","repository":{"id":349957881,"uuid":"1203538434","full_name":"labring/codex-gateway","owner":"labring","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-13T08:18:23.000Z","size":102,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T10:28:04.676Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/labring.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2026-04-07T06:06:51.000Z","updated_at":"2026-04-13T08:18:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/labring/codex-gateway","commit_stats":null,"previous_names":["che-zhu/codex-gateway","labring/codex-gateway"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/labring/codex-gateway","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/labring%2Fcodex-gateway","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/labring%2Fcodex-gateway/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/labring%2Fcodex-gateway/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/labring%2Fcodex-gateway/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/labring","download_url":"https://codeload.github.com/labring/codex-gateway/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/labring%2Fcodex-gateway/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31826907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T18:05:02.291Z","status":"online","status_checked_at":"2026-04-15T02:00:06.175Z","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":[],"created_at":"2026-04-15T05:02:45.851Z","updated_at":"2026-04-15T05:02:51.256Z","avatar_url":"https://github.com/labring.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Codex Gateway\n\nChinese version: [README_zh.md](./README_zh.md)\n\nThis repository is a minimal multi-session gateway for verifying that `codex app-server` can be exposed as a small HTTP/SSE service.\n\nArchitecture notes: [docs/architecture.md](./docs/architecture.md)\n\nAPI reference: [docs/api.md](./docs/api.md)\n\nThe current shape is:\n\n1. external clients call a Rust HTTP API\n2. the Rust service creates one Codex bridge per session\n3. each bridge spawns its own local `codex app-server` child process over `stdio`\n4. streamed notifications are forwarded back to that client over SSE\n\nOfficial references used while building this:\n\n- [Codex App Server](https://developers.openai.com/codex/app-server/)\n- [Codex CLI quickstart](https://developers.openai.com/codex/quickstart/#setup)\n- [Codex configuration reference](https://developers.openai.com/codex/config-reference/)\n- [Codex CI/CD auth](https://developers.openai.com/codex/auth/ci-cd-auth)\n\n## What is in here\n\n- `rust-src/main.rs`: Rust HTTP server with session APIs, SSE streams, health endpoints, and static file serving\n- `rust-src/bridge.rs`: reusable bridge for `initialize`, `account/read`, `model/list`, `thread/start`, `turn/start`, and notification handling\n- `rust-src/runtime.rs`: shared runtime helpers for API-key login and `openai_base_url` overrides\n- `rust-src/session_manager.rs`: multi-session lifecycle manager for bridges and TTL cleanup\n- `rust-src/cli.rs`: one-shot CLI smoke test for a single bridge\n- `src/*.mjs`: previous Node implementation retained temporarily as migration reference\n- `public/index.html`: minimal browser UI\n- `public/app.js`: browser behavior that creates its own API session and listens to its own SSE stream\n- `public/styles.css`: intentionally simple UI styling\n- `Dockerfile`: multi-stage image that builds the Rust gateway and installs the Codex CLI on Linux\n\n## Runtime model\n\nThis is no longer a single shared in-memory conversation.\n\n- `POST /api/sessions` creates a new session\n- each session owns one `CodexAppServerBridge`\n- each bridge owns one `codex app-server` subprocess\n- all `/state`, `/events`, `/turn`, and `/thread/new` calls are scoped to one session id\n- sessions expire after an idle TTL and are also removable explicitly with `DELETE /api/sessions/:id`\n\nThat makes the service usable by multiple callers without sharing one thread or transcript.\n\n## HTTP API\n\n### Health\n\n- `GET /healthz`\n- `GET /readyz`\n\n### Sessions\n\n- `POST /api/sessions`\n  - body: `{ \"model\": \"optional-model-id\" }`\n  - returns: `{ ok, sessionId, session, state }`\n- `GET /api/sessions/:id/state`\n  - returns the latest session metadata plus the current bridge state snapshot\n- `GET /api/sessions/:id/events`\n  - SSE stream for that session only\n- `POST /api/sessions/:id/turn`\n  - body: `{ \"prompt\": \"...\" }`\n- `POST /api/sessions/:id/turn/interrupt`\n  - requests cancellation of the current in-flight turn while keeping the session\n- `POST /api/sessions/:id/thread/new`\n  - body: `{ \"model\": \"optional-model-id\" }`\n- `DELETE /api/sessions/:id`\n  - closes the session and its child process\n\n### Important behavior\n\n- approval requests are still auto-declined\n- unsupported server-initiated requests are rejected\n- session state is in memory only\n- gateway auth is optional and is enabled only when `CODEX_GATEWAY_JWT_SECRET` is set\n- one session can only have one active turn at a time\n- in-flight turns can be interrupted without deleting the session\n\n## Local usage\n\n### Web UI\n\nStart the local server:\n\n```bash\nCODEX_GATEWAY_OPENAI_API_KEY=sk-... \\\nCODEX_GATEWAY_OPENAI_BASE_URL=https://sub2api-xnldrpuk.usw-1.sealos.app \\\nCODEX_GATEWAY_JWT_SECRET=replace-with-your-hs256-secret \\\ncargo run --bin codex-gateway\n```\n\nThen open:\n\n```text\nhttp://127.0.0.1:1317\n```\n\nThe page creates a fresh session automatically, subscribes to its own SSE stream, and tears the session down on tab close when possible. When JWT auth is enabled, paste a bearer token into the `Auth` panel before using the page.\n\n### CLI smoke test\n\nRun the one-shot harness:\n\n```bash\ncargo run --bin codex-gateway-cli --\n```\n\nOr with a custom prompt:\n\n```bash\ncargo run --bin codex-gateway-cli -- \"Reply with exactly the single word ready.\"\n```\n\n## Verification\n\nIf you want to verify the project manually, the shortest path is:\n\n1. Start the service with `cargo run --bin codex-gateway`.\n2. Check `http://127.0.0.1:1317/healthz`.\n3. Check `http://127.0.0.1:1317/readyz`.\n4. Open `http://127.0.0.1:1317` and wait for the page status to become `ready`.\n5. Send `Reply with exactly the single word ready. Do not call tools.` from the page.\n6. Confirm that the transcript shows `ready`.\n\nIf you want to verify the API directly instead of the page:\n\nCreate a session:\n\n```bash\ncurl -X POST http://127.0.0.1:1317/api/sessions \\\n  -H 'Content-Type: application/json' \\\n  -d '{}'\n```\n\nSend a turn:\n\n```bash\ncurl -X POST http://127.0.0.1:1317/api/sessions/\u003cSESSION_ID\u003e/turn \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"prompt\":\"Reply with exactly the single word ready. Do not call tools.\"}'\n```\n\nRead the latest state:\n\n```bash\ncurl http://127.0.0.1:1317/api/sessions/\u003cSESSION_ID\u003e/state\n```\n\nIf the transcript contains `ready`, the gateway, bridge, and `codex app-server` handshake are all working.\n\n## Environment variables\n\nGateway-owned settings use the `CODEX_GATEWAY_` prefix for better discoverability.\n\n- `CODEX_GATEWAY_HOST`: bind address for the Rust server. Defaults to `0.0.0.0`.\n- `CODEX_GATEWAY_PORT`: bind port. Defaults to `1317`.\n- `CODEX_GATEWAY_CWD`: working directory passed to `thread/start`. Defaults to the repository root.\n- `CODEX_GATEWAY_CODEX_BIN`: path to the `codex` executable if it is not on `PATH`.\n- `CODEX_GATEWAY_MODEL`: preferred default model for new bridges.\n- `CODEX_GATEWAY_OPENAI_API_KEY`: API key used at startup to run `codex login --with-api-key`.\n- `CODEX_GATEWAY_OPENAI_BASE_URL`: upstream OpenAI-compatible base URL. When set, the gateway configures Codex to use a custom provider with `supports_websockets = false`.\n- `CODEX_GATEWAY_MAX_SESSIONS`: maximum live sessions. Defaults to `12`.\n- `CODEX_GATEWAY_SESSION_TTL_MS`: idle session TTL. Defaults to `1800000`.\n- `CODEX_GATEWAY_SESSION_SWEEP_INTERVAL_MS`: cleanup sweep interval. Defaults to `60000`.\n- `CODEX_GATEWAY_CODEX_HOME`: Codex runtime home for auth cache, logs, history, and config. In Docker this defaults to `/codex-home`.\n- `CODEX_GATEWAY_DEBUG`: enables raw bridge message debugging when set to `1`.\n- `CODEX_GATEWAY_JWT_SECRET`: optional HS256 JWT secret. When set, the gateway requires a valid bearer token for all routes except `/healthz` and `/readyz`.\n\n## Docker\n\nThe container image builds the Rust gateway binary, then installs the Codex CLI on Linux with `npm install -g @openai/codex`, which matches the official Codex CLI quickstart.\n\nBuild the image:\n\n```bash\ndocker build -t codex-gateway .\n```\n\nRun it:\n\n```bash\ndocker run --rm \\\n  -p 1317:1317 \\\n  -e CODEX_GATEWAY_OPENAI_API_KEY=sk-... \\\n  -e CODEX_GATEWAY_OPENAI_BASE_URL=https://sub2api-xnldrpuk.usw-1.sealos.app \\\n  -e CODEX_GATEWAY_JWT_SECRET=replace-with-your-hs256-secret \\\n  -e CODEX_GATEWAY_HOST=0.0.0.0 \\\n  -e CODEX_GATEWAY_PORT=1317 \\\n  -e CODEX_GATEWAY_MAX_SESSIONS=8 \\\n  codex-gateway\n```\n\nNotes:\n\n- if `CODEX_GATEWAY_OPENAI_API_KEY` is set, the container runs `codex login --with-api-key` automatically before starting the gateway\n- `CODEX_GATEWAY_OPENAI_BASE_URL` is the preferred way to point Codex at a third-party OpenAI-compatible endpoint; the gateway maps it to a custom Codex provider instead of the built-in `openai` provider\n- if `CODEX_GATEWAY_JWT_SECRET` is set, clients must send `Authorization: Bearer \u003cjwt\u003e` on normal HTTP requests; the built-in Web UI also supports pasting the token into the sidebar\n- you do not need to mount `CODEX_GATEWAY_CODEX_HOME` for normal API-key-based startup; mount it only if you want Codex state to persist across container restarts\n- if you want Codex to operate on another workspace inside the container, set `CODEX_GATEWAY_CWD` and mount that path too\n- this is a PoC deployment shape, not a hardened public service\n- after the container starts, use the same health/API/Web UI verification flow described above\n\n## GitHub Container Registry\n\nGitHub Actions can publish this image to GHCR after pushes to `main` and version tags such as `v0.4.0`.\n\nPublished tags:\n\n- `ghcr.io/labring/codex-gateway:main` for the latest `main` branch image\n- `ghcr.io/labring/codex-gateway:sha-\u003ccommit\u003e` for each published commit\n- `ghcr.io/labring/codex-gateway:v0.4.0`, `0.4.0`, `0.4`, `0`, and `latest` when pushing a version tag\n\nPull the current `main` image:\n\n```bash\ndocker pull ghcr.io/labring/codex-gateway:main\n```\n\nRun it the same way as the local image:\n\n```bash\ndocker run --rm \\\n  -p 1317:1317 \\\n  -e CODEX_GATEWAY_OPENAI_API_KEY=sk-... \\\n  -e CODEX_GATEWAY_OPENAI_BASE_URL=https://sub2api-xnldrpuk.usw-1.sealos.app \\\n  -e CODEX_GATEWAY_HOST=0.0.0.0 \\\n  -e CODEX_GATEWAY_PORT=1317 \\\n  -e CODEX_GATEWAY_MAX_SESSIONS=8 \\\n  ghcr.io/labring/codex-gateway:main\n```\n\nIf the package is private, authenticate to GHCR before pulling it.\n\n## Current limitations\n\n- no built-in rate limiting\n- no durable session persistence\n- approval UI is intentionally absent\n- each live session consumes a `codex app-server` subprocess\n- browser clients reconnect with SSE, but session ownership is not persisted across page reloads unless the caller stores the session id\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flabring%2Fcodex-gateway","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flabring%2Fcodex-gateway","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flabring%2Fcodex-gateway/lists"}