{"id":48893110,"url":"https://github.com/lain39/codaze","last_synced_at":"2026-04-16T10:00:31.038Z","repository":{"id":349538083,"uuid":"1202698861","full_name":"lain39/codaze","owner":"lain39","description":"Aggregate multiple ChatGPT accounts into a local gateway that stays as close as practical to the official Codex client.","archived":false,"fork":false,"pushed_at":"2026-04-06T13:25:19.000Z","size":204,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-06T13:29:12.771Z","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lain39.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-06T09:55:29.000Z","updated_at":"2026-04-06T13:24:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lain39/codaze","commit_stats":null,"previous_names":["lain39/codaze"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lain39/codaze","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lain39%2Fcodaze","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lain39%2Fcodaze/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lain39%2Fcodaze/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lain39%2Fcodaze/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lain39","download_url":"https://codeload.github.com/lain39/codaze/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lain39%2Fcodaze/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31880882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T09:23:21.276Z","status":"ssl_error","status_checked_at":"2026-04-16T09:23:15.028Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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-16T10:00:18.050Z","updated_at":"2026-04-16T10:00:31.020Z","avatar_url":"https://github.com/lain39.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[English](README.md) | [简体中文](README.zh-CN.md)\n\n\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003eCodaze☆\u003c/h1\u003e\n  \u003cp\u003e\u003cstrong\u003eAggregate multiple ChatGPT accounts into a local gateway that stays as close as practical to the official Codex client.\u003c/strong\u003e\u003c/p\u003e\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/lain39/codaze/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/lain39/codaze?style=flat-square\u0026label=release\u0026sort=semver\" alt=\"Release\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/lain39/codaze/actions/workflows/release.yml\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/lain39/codaze/release.yml?style=flat-square\u0026label=release%20pipeline\" alt=\"Release Pipeline\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/lain39/codaze/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/lain39/codaze?style=flat-square\" alt=\"License\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/lain39/codaze/releases\"\u003e\u003cimg src=\"https://img.shields.io/badge/support-linux%20%7C%20macOS%20%7C%20windows%20%C2%B7%20amd64%20%7C%20arm64-0A7EA4?style=flat-square\" alt=\"Support Matrix\" /\u003e\u003c/a\u003e\n  \u003ca href=\"docs/DESIGN.md\"\u003e\u003cimg src=\"https://img.shields.io/badge/docs-DESIGN.md-1F6FEB?style=flat-square\" alt=\"Design Docs\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nDocs index: [docs/README.md](docs/README.md)  \nFriend link: \u003ca href=\"https://linux.do\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/LINUX-DO-FFB003?style=for-the-badge\u0026logo=linux\u0026logoColor=white\" alt=\"LINUX DO\" style=\"max-width: 100px\" /\u003e\u003c/a\u003e\n\n---\n\n## Why Codaze\n\n- **High-fidelity fingerprint alignment**: Reuses Codex's native Rust transport stack so outbound network behavior, HTTP headers, and websocket characteristics stay as close as practical to the official client, while still keeping the gateway intentionally lightweight.\n- **Failover and protocol surgery**: Switches accounts on quota or selected failure paths, and rewrites a few critical protocol-level errors when needed, such as converting `previous_response_not_found` into a client-reset-triggering error to prompt the downstream client to gracefully reset.\n- **Lazy initialization**: Accounts refresh on demand instead of during startup, which avoids noisy bulk refreshes.\n- **Zero-bloat local isolation**: No YAML-heavy control plane; the account directory acts as the durable source of truth, while public and admin APIs are physically separated onto different loopback ports.\n\n## Quick Start\n\nDownload the platform-specific `codaze` binary from [GitHub Releases](https://github.com/lain39/codaze/releases), then run it directly:\n\n\u003e [!NOTE]\n\u003e The published Linux binaries are `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu`, intended for reasonably recent `glibc`-based distributions.\n\u003e They are not guaranteed to run on every Linux distribution; `musl` environments such as Alpine should build from source.\n\n```bash\n./codaze\n```\n\n\u003e [!NOTE]\n\u003e If you need an upstream proxy, set `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and related environment variables before starting `codaze`.\n\u003e See [Proxy Environment Variables](#proxy-environment-variables) below for examples.\n\nThat starts with the defaults:\n\n- public listener: `127.0.0.1:18039`\n- admin listener: `127.0.0.1:18040`\n- accounts dir: `$HOME/.codaze` on Unix-like systems, `%USERPROFILE%\\\\.codaze` on Windows\n- Codex version: `0.121.0` for UA and related client-version fingerprinting\n- routing policy: `least_in_flight`\n- fingerprint mode: `normalize`\n\nIf you want to override defaults, pass flags explicitly:\n\n```bash\n./codaze \\\n  --listen 127.0.0.1:18039 \\\n  --admin-listen 127.0.0.1:18040 \\\n  --codex-version 0.121.0 \\\n  --routing-policy least_in_flight \\\n  --fingerprint-mode normalize\n```\n\n\u003e [!NOTE]\n\u003e The gateway is loopback-only and refuses to bind to non-local addresses.\n\u003e Public traffic and admin traffic use different local ports by default.\n\nThe process shuts down gracefully on `SIGINT` and `SIGTERM`.\n\nIf you are maintaining the project and want local builds or `cargo run`, see [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).\n\n## Run As A Background Service\n\nIf you want `codaze` to keep running in the background, use the provided service templates instead of keeping a shell session open:\n\n- Linux systemd: [docs/PACKAGING.md](docs/PACKAGING.md)\n- macOS launchd: [docs/PACKAGING.md](docs/PACKAGING.md)\n\nThe actual template files live under:\n\n- [`packaging/systemd/codaze.service`](packaging/systemd/codaze.service)\n- [`packaging/launchd/io.github.lain39.codaze.plist`](packaging/launchd/io.github.lain39.codaze.plist)\n\n## Import Accounts\n\nThe simplest path is to drop account files directly into `--accounts-dir`. Codaze rescans the directory periodically, so new files are picked up without restarting the service.\n\nMinimal account file example:\n\n```json\n{\n  \"refresh_token\": \"rt_xxx\"\n}\n```\n\n`label` and `email` are optional metadata. If omitted, Codaze can fill them later from refresh results.\n\nRuntime behavior:\n\n- refresh is lazy; importing does not immediately refresh the account, it refreshes only when the account is actually routed\n- duplicate imports are deduplicated by the current refresh token\n- invalid refresh tokens are moved into `trash/`\n- the in-memory `refresh_token` is authoritative at runtime; manually editing `refresh_token` in an already loaded file is not supported\n- there is no separate shutdown-time runtime-state flush; account-file mutations are persisted immediately on import, successful refresh, delete, and trash transitions, while `blocked_*` and `last_error` remain memory-only runtime state\n\nIf you want to import accounts dynamically while the process is already running, use HTTP:\n\n```bash\ncurl -X POST http://127.0.0.1:18040/admin/accounts/import \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"refresh_token\":\"rt_xxx\",\"label\":\"main\",\"email\":\"user@example.com\"}'\n```\n\n## Proxy Environment Variables\n\nCodaze's outbound client follows the standard process-level proxy environment variables unless it is running in the special `CODEX_SANDBOX=seatbelt` path, where proxy autodetection is explicitly disabled.\n\nTypical forms:\n\n```bash\nexport HTTP_PROXY=\"http://127.0.0.1:3128\"\nexport HTTPS_PROXY=\"http://127.0.0.1:3128\"\nexport ALL_PROXY=\"socks5h://127.0.0.1:1080\"\nexport NO_PROXY=\"127.0.0.1,localhost\"\n```\n\nIf you only want the proxy to apply to this one launch, you can inline it directly:\n\n```bash\nHTTP_PROXY=\"http://127.0.0.1:3128\" \\\nHTTPS_PROXY=\"http://127.0.0.1:3128\" \\\n./codaze\n```\n\nNotes:\n\n- for ordinary HTTP(S) upstream proxies, use `HTTP_PROXY` / `HTTPS_PROXY`\n- for SOCKS5 with remote DNS resolution, `ALL_PROXY=socks5h://host:port` is the safest form\n- if you run Codaze via systemd or launchd, set the same environment variables in the service definition\n\n## API Overview\n\n| Module | Method | Path | Description |\n| :--- | :--- | :--- | :--- |\n| **Public API (18039)** | `GET` | `/v1/models` | List available models |\n| | `POST` | `/v1/responses` | Responses over SSE |\n| | `GET` | `/v1/responses` | Responses over websocket |\n| | `POST` | `/v1/responses/compact` | Compact endpoint |\n| | `POST` | `/v1/memories/trace_summarize` | Present for compatibility; upstream is not available yet |\n| | `GET` | `/health` | Basic gateway health check |\n| **Admin API (18040)** | `GET` | `/admin/accounts` | Inspect account-pool state |\n| | `POST` | `/admin/accounts/import` | Import or update an account |\n| | `POST` | `/admin/accounts/wake` | Wake all blocked accounts |\n| | `POST` | `/admin/accounts/:id/wake` | Wake a specific blocked account |\n| | `DELETE` | `/admin/accounts/:id` | Remove an account |\n| | `GET/PUT` | `/admin/routing/policy` | Read or change routing policy |\n\n\u003e For full request and response shapes, see [docs/API.md](docs/API.md).\n\n\u003e [!IMPORTANT]\n\u003e If you call `POST /v1/responses` directly from a non-Codex client, include `\"stream\": true` explicitly in the JSON body.\n\u003e This upstream path currently expects streaming mode for non-Codex callers; otherwise a common response is `400 {\"detail\":\"Stream must be set to true\"}`.\n\n## Codex Compatibility\n\nCodaze intentionally reuses Codex's Rust transport stack instead of inventing a separate outbound client:\n\n- `codex_login::default_client::build_reqwest_client`\n- `codex_client::ReqwestTransport`\n- Codex-style request headers and endpoint paths\n\nThe default fingerprint mode is `normalize`. It only injects fields that real Codex requests stably carry, such as:\n\n- `store: false` on `/v1/responses`\n- `instructions: \"\"` when the caller omits `instructions` or sends `null`\n- model-derived `parallel_tool_calls`\n- identity headers derivable from `x-codex-session-source`, such as `x-openai-subagent` and `x-codex-parent-thread-id`\n- a stable `x-codex-installation-id` derived from the selected upstream `ChatGPT-Account-ID`\n\n`passthrough` only affects outbound request fingerprint shaping. It does not disable routing, refresh, error classification, or local admin behavior.\n\nImportant boundary:\n\n- if the caller already provides `x-codex-window-id`, Codaze forwards it\n- for non-Codex callers, Codaze does not synthesize `x-codex-window-id` or websocket `response.create.client_metadata` identity keys that depend on client-local thread state\n- `x-codex-installation-id` is handled differently from `x-codex-window-id`: in `normalize` mode Codaze rewrites it from the selected upstream account for `/v1/responses`, `/v1/responses/compact`, and websocket `response.create`; in `passthrough` mode Codaze does not add or override it\n- if a websocket request is retried on a different upstream account before commit, Codaze replays `response.create` with the replacement connection's derived `x-codex-installation-id`\n- `GET /v1/models` follows the response shape selected by `originator`: Codex callers receive `{\"models\":[...]}` on success and the gateway's own structured error surface on failure, while other callers receive OpenAI-style `{\"object\":\"list\",\"data\":[...]}` on success and OpenAI JSON error shape on failure\n- `/v1/responses` has endpoint-specific pre-stream failure rendering: Codex callers receive synthetic SSE, while non-Codex callers receive ordinary HTTP JSON errors\n- `/v1/responses/compact` always uses unary JSON response rendering for both success and failure; it does not use SSE error wrapping\n- public business endpoints collapse account-routing failures into a gateway-level unavailable shape with `code: \"server_is_overloaded\"`; they do not expose upstream `retry-after` or `resets_*`\n- for non-Codex HTTP responses, Codaze reshapes the surface toward the OpenAI API, but not as a byte-for-byte mirror; currently only `Content-Type` and `Cache-Control` are preserved downstream\n- on non-Codex `POST /v1/responses`, Codaze applies a small compatibility normalization set: it removes a few currently rejected fields, keeps only `service_tier: \"priority\"`, and rewrites legacy `web_search_preview*` aliases to `web_search`; see [docs/API.md](docs/API.md) for the exact rules\n\nFor the full design rationale, see [docs/DESIGN.md](docs/DESIGN.md).\n\n## Optional `_gateway` Control Field\n\n`POST /v1/responses`, `POST /v1/responses/compact`, and `POST /v1/memories/trace_summarize` accept a private `_gateway` object in the JSON body. It is consumed locally and stripped before forwarding.\n\nExample:\n\n```json\n{\n  \"_gateway\": {\n    \"session_source\": \"exec\"\n  }\n}\n```\n\n`_gateway.session_source` must match the JSON representation of `codex_protocol::SessionSource`.\n\n## Point Codex at Codaze\n\nUsing the built-in `openai` provider:\n\n`OPENAI_API_KEY=dummy` is not used for upstream calls here; it only satisfies Codex's provider-side config validation.\n\n```bash\nOPENAI_API_KEY=dummy \\\ncodex \\\n  -c 'openai_base_url=\"http://127.0.0.1:18039/v1\"' \\\n  -c 'model_provider=\"openai\"'\n```\n\nUsing a custom provider:\n\n```toml\nmodel_provider = \"cpa\"\nmodel = \"gpt-5.4\"\n\n[model_providers.cpa]\nname = \"My Proxy API\"\nbase_url = \"http://127.0.0.1:18039/v1\"\nenv_key = \"OPENAI_API_KEY\"\nwire_api = \"responses\"\nsupports_websockets = true\n```\n\nIf you omit `supports_websockets = true` on a custom provider entry, Codex falls back to HTTP/SSE.\n\nAdmin APIs stay on `http://127.0.0.1:18040` by default and are never exposed through the public `/v1` listener.\n\n## License And Docs\n\n- License: `Apache-2.0`\n- Attribution: [`NOTICE`](NOTICE)\n- Development and local builds: [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)\n- Release process: [docs/RELEASE.md](docs/RELEASE.md)\n- Service templates: [docs/PACKAGING.md](docs/PACKAGING.md)\n- GitHub Releases: https://github.com/lain39/codaze/releases\n\nProject maintenance docs:\n\n- [`CHANGELOG.md`](CHANGELOG.md)\n- [`CONTRIBUTING.md`](CONTRIBUTING.md)\n- [`SECURITY.md`](SECURITY.md)\n- [`docs/README.md`](docs/README.md)\n- [`docs/RELEASE.md`](docs/RELEASE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flain39%2Fcodaze","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flain39%2Fcodaze","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flain39%2Fcodaze/lists"}