docs: refresh user-facing setup and existing guides

Refocus the README on installation, client setup and common questions. Consolidate native image generation, auth.json compatibility and obsolete Codex settings in the existing deployment guide.

Correct account import formats, API references, backup recovery and password rotation guidance. Synchronize architecture, theme, migration and repository skill documentation without adding new documents or changing temporary investigation notes.

Validated 53 local links, 22 shell examples, generated configuration parity and TOML syntax, Compose configuration, skill metadata and whitespace checks.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-09-06 15:28:32 +08:00
co-authored by Codex
parent a404c2e09e
commit 697f1870a0
8 changed files with 339 additions and 216 deletions
+82 -151
View File
@@ -5,39 +5,34 @@
# Codex Proxy RS
**基于 Rust 的多 Provider 透明 AI 网关**
集中管理 OpenAI、xAI 账号,为 Codex 和兼容客户端提供统一的访问地址与密钥。
[![CI](https://github.com/zyycn/codex-proxy-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/zyycn/codex-proxy-rs/actions/workflows/ci.yml)
[![Release](https://img.shields.io/github/v/release/zyycn/codex-proxy-rs?display_name=tag&sort=semver&style=flat-square)](https://github.com/zyycn/codex-proxy-rs/releases)
[![GHCR](https://img.shields.io/badge/GHCR-codex--proxy--rs-2496ED?logo=docker&logoColor=white&style=flat-square)](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs)
[![MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/license/mit)
[快速开始](#快速开始) · [客户端接入](#客户端接入) · [运行维护](#运行维护) · [部署](deploy/README.md) · [接口](docs/api.md) · [架构](docs/architecture.md)
[快速开始](#快速开始) · [客户端接入](#客户端接入) · [常见问题](#常见问题) · [部署说明](deploy/README.md)
</div>
> [!NOTE]
> 数据面支持 OpenAI Responses、Images 和模型目录协议,不提供 `/v1/chat/completions`。
## 功能
| 领域 | 当前能力 |
| --- | --- |
| 数据面 | Responses JSON、SSE、WebSocket(含 Codex 子代理语义)、Images 生成/编辑与模型目录 |
| Provider | 固定 `openai`、`xai` 两个 Provider,各自拥有 OAuth、credential、额度、目录与 transport |
| 路由 | Client Key 限定账号分组;按模型能力编译 Provider 候选;支持全局精确模型映射、会话亲和和安全 fallback |
| 透明边界 | OpenAI 保留未知请求字段及字段顺序,并原样转发 SSE、WebSocket 与 Images 业务字节;xAI 在 Provider 内转换协议 |
| 账号 | OAuth、OpenAI AT/RT 与账号文件导入,credential/额度刷新、账号恢复及主动额度重置卡 |
| 管理 | 账号与分组、Client Key、运行设置、用量/错误观测、S3/R2 备份、系统更新与回滚 |
| 计量 | Token、图片、费用、延迟、账号与 Provider 归因,并记录上游响应确认的实际 `service_tier` |
- 在网页中添加账号,查看额度、请求记录和费用,刷新或重新授权过期账号。
- 用账号分组控制不同密钥可使用的账号,设置并发和请求频率。
- 接入 Codex CLI、桌面端和支持 Responses API 的客户端。
- 支持流式对话、WebSocket、图片生成与编辑、Codex 独立搜索。
- 支持 S3/R2 数据库备份、同大版本在线更新和回滚。
架构边界、状态所有权和请求不变量见 [架构文档](docs/architecture.md)。
支持 OpenAI、xAI 账号,但图片与独立搜索目前仅通过 OpenAI 提供。模型和功能是否可用取决于上游账号权限。
**不支持 `/v1/chat/completions`**;仅支持该接口的客户端不能直接接入。
## 快速开始
需要 Docker Engine 与 Docker Compose Plugin。
以下命令适用于 Linux,需要 Git、Docker Engine 和 Docker Compose Plugin。已有部署请先看
[升级说明](deploy/README.md#镜像升级与源码构建),不要覆盖原配置。
### 1. 准备配置和目录
### 1. 下载并配置
```bash
git clone https://github.com/zyycn/codex-proxy-rs.git
@@ -48,34 +43,28 @@ install -d -m 0750 .runtime/postgres .runtime/redis
cp deploy/config.example.yaml deploy/config.yaml
sudo chown "$(id -u):10001" deploy/config.yaml
chmod 0640 deploy/config.yaml
```
分别生成 PostgreSQL 与 Redis 密码:
```bash
openssl rand -hex 24
openssl rand -hex 24
```
把两个结果分别写入 `deploy/config.yaml`,再设置管理员初始密码:
| 配置 | 约束 |
| --- | --- |
| `store.database.password` | 48 位十六进制 |
| `store.redis.password` | 48 位十六进制 |
| `admin.default_password` | 至少 12 位,不能包含 `$` |
PostgreSQL、Redis 和应用从同一份 `config.yaml` 读取基础设施密码,不需要额外导出环境变量。
管理员密码只在首次创建管理员时生效。
Linux 需要允许容器组写入运行目录:
```bash
sudo chown -R "$(id -u):10001" .runtime/data .runtime/logs
chmod 0770 .runtime/data .runtime/logs
```
### 2. 启动
分别生成数据库和 Redis 密码:
```bash
openssl rand -hex 24
openssl rand -hex 24
```
编辑 `deploy/config.yaml`,填好以下三项:
| 配置项 | 填写内容 |
| --- | --- |
| `store.database.password` | 第一个生成的 48 位十六进制密码 |
| `store.redis.password` | 第二个生成的 48 位十六进制密码 |
| `admin.default_password` | 管理员初始密码,至少 12 位,不能包含 `$` |
不需要创建 `.env`。管理员初始密码只在首次创建账号时使用。
### 2. 启动服务
```bash
docker compose -f deploy/compose.yaml config --quiet
@@ -84,145 +73,87 @@ docker compose -f deploy/compose.yaml up -d --no-build
curl -i http://127.0.0.1:8080/healthz
```
`204 No Content` 表示应用、PostgreSQL 与 Redis 均可用。管理端地址为
`http://127.0.0.1:8080`。
健康检查返回 `204 No Content` 后,打开 `http://127.0.0.1:8080`,
使用 `admin@cpr.local` 和刚设置的管理员密码登录。
### 3. 初始化网关
默认地址只能在服务器本机访问。从其他设备使用时,需要配置
[HTTPS 反向代理](deploy/README.md#公网访问)。
1. 使用 `admin@cpr.local` 与初始密码登录。
2. 导入或授权 Provider 账号:OpenAI 支持 OAuth、逐行 AT/RT 与 OAuth JSON;
xAI 支持 OAuth 和账号 JSON。
3. 创建账号分组并按需配置全局模型映射。未命中的模型名原样交给候选 Provider。
4. 创建 `sk_...` Client Key,选择可用账号分组,并设置速率和并发限制;空分组范围表示全部账号。
### 3. 添加账号和密钥
OpenAI RT 导入会先向官方 token endpoint 换取 AT;仅 AT 的账号不具备自动续期能力,
能否参与调度仍取决于 token 中可验证的身份与后续上游事实。账号导入和首次 OAuth 创建会尽力读取一次额度,
额度上游暂时失败不会回滚已提交的账号。
1. 在「账号」中添加 OpenAI 或 xAI 账号。OpenAI 支持 OAuth 授权、AT/RT 和账号 JSON;xAI 支持 OAuth 授权和账号 JSON。
2. 按需创建账号分组,并把账号加入分组。
3. 创建客户端密钥,选择可用分组。**不选分组表示可使用全部账号**。
4. 点击该密钥的「使用密钥」,复制客户端配置。
OpenAI 账号的主动重置卡只在用户打开相应弹窗或手工刷新时查询。消费成功后管理端重新查询卡片与额度;
系统不会通过修改本地 `reset_at` 伪造重置。完整账号合同见 [账号接口](docs/api.md#5-账号)。
> [!IMPORTANT]
> xAI 使用 OAuth session,不支持把 xAI API Key 作为上游 credential。
完整的权限、密码轮换、备份和升级规则见 [部署文档](deploy/README.md)。
只导入 AT 的账号不能自动续期。xAI 暂不支持用 API Key 导入上游账号。
## 客户端接入
### Codex CLI 和桌面端
在「使用密钥」中按系统复制 `config.toml` 和 `auth.json`,也可以通过 CCSwitch 导入。
已有文件先备份,再合并配置并重启 Codex。客户端使用代理密钥,OpenAI 账号登录状态由服务端管理。
新配置已启用原生生图。可以直接提出需求,例如:
> 帮我做一个咖啡店首页,生成一张咖啡主题配图并用到页面中。
是否调用生图由 Codex 根据任务决定,也可以明确要求生成图片。需要支持生图的客户端、模型和 OpenAI 账号;
普通对话可用不代表账号一定有生图额度。
完整配置和旧版配置迁移见 [客户端配置](deploy/README.md#客户端配置)。
### 其他客户端
选择支持 **Responses API** 的客户端,填写:
| 配置 | 值 |
| --- | --- |
| Base URL | `http://127.0.0.1:8080/v1` |
| API Key | 管理端创建的 `sk_...` |
| 鉴权 | `Authorization: Bearer <client-api-key>` |
| Base URL | `http://127.0.0.1:8080/v1`,远程使用时换成服务器的 HTTPS 地址 |
| API Key | 管理端创建的 `sk_...` 密钥 |
可先检查密钥是否能读取模型列表:
```bash
curl http://127.0.0.1:8080/v1/models \
-H 'Authorization: Bearer <client-api-key>'
curl http://127.0.0.1:8080/v1/responses \
-H 'Authorization: Bearer <client-api-key>' \
-H 'Content-Type: application/json' \
-d '{
"model": "<model-id>",
"input": "Reply with pong.",
"stream": false
}'
```
<details>
<summary>公开数据面路由</summary>
接口列表与请求示例见 [接口文档](docs/api.md)。
| 路由 | 用途 |
| --- | --- |
| `POST /v1/responses` | JSON 响应或 SSE Responses 流 |
| `GET /v1/responses` | Responses WebSocket 升级 |
| `POST /v1/images/generations` | OpenAI Images 生成 JSON 透明转发 |
| `POST /v1/images/edits` | OpenAI Images 编辑 JSON 透明转发 |
| `GET /v1/models` | 当前 Client Key 账号范围内的模型列表 |
| `GET /v1/models/{model_id}` | OpenAI 兼容模型详情 |
## 常见问题
所有 `/v1/*` 路由都需要 Client Key。Images 是 OpenAI Provider 自有端点,不读取模型字段,也不参与
文本模型映射。
### 仍然要求登录,或无法生成图片
</details>
重新从管理端复制配置,确认修改的是客户端实际读取的用户配置目录,然后完全退出并重启客户端。
保留仅含代理密钥的 `auth.json` 不会使新配置失效。详细检查步骤见
[登录与生图排查](deploy/README.md#登录与生图排查)。
完整的客户端与管理端路由、鉴权、请求字段和 mutation 语义见 [接口文档](docs/api.md)。
### 没有可用账号或模型
### Codex 原生生图
检查账号是否启用、凭据是否过期、额度是否耗尽,以及密钥绑定的分组是否包含可用账号。
「恢复账号」只清除本地错误状态,不能恢复上游权限或增加额度。
在管理端的「使用密钥」中复制配置,或通过 CCSwitch 导入,保存后重启 Codex。
新配置启用 `features.image_generation = true`,使用以下 Provider 认证方式:
### 如何更新或查看日志
```toml
[model_providers.OpenAI]
name = "OpenAI"
base_url = "http://127.0.0.1:8080/v1"
wire_api = "responses"
requires_openai_auth = false
# 使用管理端创建的代理密钥,不是 ChatGPT 账号的访问令牌。
experimental_bearer_token = "<client-api-key>"
[model_providers.OpenAI.http_headers]
# 声明账号认证由服务端托管;此标记本身没有鉴权能力。
X-OpenAI-Actor-Authorization = "proxy-managed"
[features]
image_generation = true
```
合并到已有配置时,请修改当前 `model_provider` 对应的 Provider 段,不要重复添加同名 TOML 表,
并保留其他个性化设置。配置文件包含代理密钥,请勿公开分享。
无需更改本地 `auth.json`;真实 ChatGPT 登录状态仍由服务端管理。网关始终校验 Bearer Client Key,
并过滤客户端的 Actor 认证标记,上游身份只来自调度选中的服务端账号。
此配置使用官方客户端的服务端托管认证能力,不修改或伪造客户端登录状态。
需要支持该能力的新版 Codex、支持图片输入及命名空间工具的对话模型,以及具备生图权限的 OpenAI 账号。
模型会在需要图片时调用原生 `image_gen.imagegen`,图片请求通过本代理的 Images 路由完成。
原来的 `auth.json` + `requires_openai_auth = true` 配置仍可用于已有请求,但不会因此自动获得原生生图能力。
## 运行维护
同一大版本内拉取发布镜像并重建应用容器:
```bash
# 同一大版本内升级发布镜像
docker compose -f deploy/compose.yaml pull codex-proxy-rs
docker compose -f deploy/compose.yaml up -d --no-build
# 查看日志
docker compose -f deploy/compose.yaml logs -f codex-proxy-rs
# 从当前源码构建
docker compose -f deploy/compose.yaml build codex-proxy-rs
docker compose -f deploy/compose.yaml up -d
docker compose -f deploy/compose.yaml up -d --no-build codex-proxy-rs
docker compose -f deploy/compose.yaml logs --tail=100 -f codex-proxy-rs
```
> [!IMPORTANT]
> `.runtime/` 保存 PostgreSQL、Redis、日志、OpenAI 会话锚点和在线更新状态。删除该目录会永久清除
> 本地运行状态。
> [!WARNING]
> 不支持跨大版本在线升级。升级到新的大版本时请使用全新的 `.runtime/` 数据目录,并重新导入或授权
> Provider 账号与 Client Key。
Compose 默认只绑定 `127.0.0.1`。公网接入应使用 HTTPS 反向代理,转发 WebSocket upgrade 与真实客户端
IP;不要暴露 PostgreSQL 或 Redis。当前运行拓扑为单副本,不能通过复制应用容器扩容。
## 维护与发布
仓库版本只能从干净、已同步上游的发布分支发布:
```bash
release/publish <version>
```
脚本依赖已登录的 GitHub CLI(`gh auth login`),会更新 `release/version.yaml`、创建约定提交和带注释的
`v<version>` tag,原子推送分支与 tag,再触发 Release 构建。源码提交、仓库发版和运行实例升级是三个
独立状态;脚本不会登录服务器或改变任何运行实例。
> 升级前先备份。跨大版本需使用新的数据目录重新部署,不支持直接沿用旧库在线升级。
> `.runtime/` 包含数据库和运行数据,请勿删除;账号文件、密钥和日志也不要公开分享。
## 文档
- [部署与运维](deploy/README.md)
- [客户端与管理端接口](docs/api.md)
- [客户端接入与生图](deploy/README.md#客户端配置)
- [部署、备份与恢复](deploy/README.md)
- [API 参考](docs/api.md)
- [系统架构](docs/architecture.md)
- [数据库迁移规则](backend/migrations/README.md)
- [Release](https://github.com/zyycn/codex-proxy-rs/releases)
- [容器镜像](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs)
- [管理端主题](docs/theme.md)
- [数据库迁移](backend/migrations/README.md)
+18 -5
View File
@@ -1,8 +1,10 @@
# 数据库迁移
sqlx 在服务启动时(`serve` 监听之前)按编号顺序执行本目录的迁移,并把每个
文件的 checksum 记入 `_sqlx_migrations`。此后每次启动都会重新校验:**已应用
迁移的文件字节与记录不一致时,服务直接拒绝启动**。
sqlx 在服务启动、监听请求之前按编号执行迁移,并把文件 checksum 记入 `_sqlx_migrations`。
已应用迁移的字节与记录不一致时,服务会拒绝启动。
`0001_initial.sql` 是初始基线,后续编号继续增加字段和索引;
当前完整清单以本目录 SQL 文件和 `.frozen-sha256` 为准,不要只执行 `0001`。
## 冻结规则
@@ -18,6 +20,16 @@ sqlx 在服务启动时(`serve` 监听之前)按编号顺序执行本目录
- 若同一大版本内确需修正已冻结迁移里的错误,用新迁移做补偿性变更(`alter` / 回填),
不要动原文件。
从迁移目录检查已登记文件:
```bash
cd backend/migrations
sha256sum --check --strict .frozen-sha256
```
CI 还会检查是否遗漏新 SQL 文件,并在 PR 中检查清单只增不改。
遇到 checksum 不一致,先核对运行版本和文件来源;不要修改数据库中的 checksum 来绕过校验。
## 本地测试库
`gateway-store` 的 PG/Redis 集成测试需要以下环境变量,未设置时在本地
@@ -28,5 +40,6 @@ export CPR_TEST_DATABASE_URL='postgres://<user>:<password>@127.0.0.1:5432/<db>'
export CPR_TEST_REDIS_URL='redis://:<password>@127.0.0.1:6379'
```
测试自建随机 schema / key 前缀做隔离,可安全指向开发库;凭据按本地部署
实际值填写(`docker inspect` 对应容器可查)。
测试自建随机 schema / key 前缀做隔离,但仍应使用专用开发或测试实例,不要连接生产库。
凭据从自己的部署配置或 CI Secret 中读取,不要粘贴未脱敏的 `docker inspect` 输出。
环境变量未设置导致的跳过不算数据库测试通过。
+140 -19
View File
@@ -1,11 +1,15 @@
# 部署
# 部署与运维
本目录只有两个配置入口:
首次安装可按 [快速开始](../README.md#快速开始) 操作。
本文补充客户端配置、权限、备份和升级;命令从仓库根目录执行。
需要维护的配置文件:
- `config.yaml`:应用行为与真实凭据,由 `config.example.yaml` 复制得到并被 Git 忽略。
- `compose.yaml`:镜像、容器网络、端口、目录映射、健康检查和资源限制。
项目不使用 `.env` 配置文件。Compose 中的少量环境变量只描述容器内部拓扑与发布链路装配,不是用户配置入口。
项目不使用 `.env` 配置文件。Compose 环境变量用于容器地址、镜像选择和构建发布;
应用设置与凭据保存在 `config.yaml` 中。已有部署不要重新复制模板覆盖配置。
## 准备
@@ -23,6 +27,7 @@ chmod 0640 deploy/config.yaml
```bash
openssl rand -hex 24
openssl rand -hex 24
```
把两个 48 位十六进制结果分别写入 `deploy/config.yaml` 的:
@@ -47,8 +52,8 @@ UID/GID 和 mode,因此配置由当前用户持有,并只向容器组 `10001
模板中的 `openai` / `xai` 只保留请求画像启动基线。OpenAI 的上游地址、WebSocket 池、额度刷新
与 OAuth 设置,以及 xAI 的 OAuth、额度和模型目录策略,均由各自 Provider 使用代码内默认值管理;
模板不重复列出这些默认项。运行后,OpenAI Desktop 与 xAI CLI 的官方发布检查只更新进程内
版本字段,不回写 `config.yaml`;检查失败时继续使用上一份有效画像。
模板不重复列出这些默认项。运行后,Provider 检查官方版本并更新运行时请求画像,
不回写 `config.yaml`;检查失败时继续使用上一份有效画像。版本检查不等于重新核验 TLS。
## 启动
@@ -65,11 +70,120 @@ docker compose -f deploy/compose.yaml ps
curl -i http://127.0.0.1:8080/healthz
```
`204 No Content` 表示应用、PostgreSQL 和 Redis 均可用。
`204 No Content` 表示应用、PostgreSQL、Redis 和后台任务的健康检查通过,不代表每个上游账号都可用。
不要把未脱敏的 `docker compose config` 或 `docker inspect` 输出上传到工单;它们会包含
PostgreSQL/Redis 启动密码。日常校验使用 `config --quiet`。
## 公网访问
Compose 默认只绑定 `127.0.0.1`。从其他设备访问时,在应用前配置 HTTPS 反向代理,
不要把 PostgreSQL 或 Redis 暴露到公网。
反向代理需要保留 `Authorization`,支持 `/v1/responses` 的 WebSocket Upgrade,
并关闭 SSE 响应缓冲。读取超时应覆盖长时间生成任务。
客户端使用 `https://你的域名/v1`,不要使用前端开发服务的 `5173/dev/v1`。
当前应用只支持单副本,不能通过复制容器扩容。
## 客户端配置
在管理端创建客户端密钥,打开「使用密钥」,按操作系统复制 `config.toml` 和 `auth.json`,
或通过 CCSwitch 导入。已有文件先备份,合并后完全退出并重启 Codex。
Linux/macOS 默认目录为 `~/.codex/`,Windows 为 `%USERPROFILE%\.codex\`;
设置过 `CODEX_HOME` 时以该目录为准。Provider 设置应写入用户配置,不要只写到项目目录。
配置层级见 [官方配置参考](https://learn.chatgpt.com/docs/config-file/config-reference)。
### config.toml
以下示例与当前管理端模板的配置项一致。替换地址和密钥,模型可改成该密钥有权限使用的模型:
```toml
model_provider = "OpenAI"
model = "gpt-5.6-terra"
review_model = "gpt-5.6-terra"
model_reasoning_effort = "max"
service_tier = "default"
[model_providers.OpenAI]
name = "OpenAI"
base_url = "http://127.0.0.1:8080/v1"
wire_api = "responses"
supports_websockets = false
requires_openai_auth = false
# 填写代理密钥,真实账号由服务端管理。
experimental_bearer_token = "<client-api-key>"
[model_providers.OpenAI.http_headers]
# 供客户端识别服务端托管认证,本身不是密钥。
X-OpenAI-Actor-Authorization = "proxy-managed"
[features]
image_generation = true
goals = true
```
`OpenAI` 是自定义 Provider ID,大小写要与 `model_provider` 一致。合并配置时修改已有表,
不要重复添加 `[features]` 或 Provider 表。更换模型时也要检查其支持的推理强度。
密钥以明文保存,文件仅供本人读取,不要提交到 Git。
### auth.json
```json
{
"OPENAI_API_KEY": "<client-api-key>"
}
```
这份文件可以保留,用于兼容已有客户端和 CCSwitch。新配置从 `experimental_bearer_token`
读取代理密钥,不会因为仅含 API Key 的 `auth.json` 存在而关闭生图。
真实 OpenAI OAuth 账号文件用于管理端账号导入,不要当作代理配置分发给客户端。
### 生图和 WebSocket
新模板已启用原生生图。Codex 可在任务需要图片时调用 `image_gen.imagegen`,
再通过代理的 Images 接口生成或编辑图片;也可以在需求中明确要求生成并使用图片。
需要支持该能力的客户端、支持图片输入的对话模型,以及有生图权限和额度的 OpenAI 账号。
代理不会增加上游权限,xAI 账号不能承接这些 Images 请求。
本项目已用 Codex CLI 0.153.4 验证过原生生图,这不是最低支持版本声明。
生图不要求开启 WebSocket。要启用客户端 WebSocket,把当前 Provider 的
`supports_websockets` 改为 `true`,并检查反向代理是否允许 Upgrade。
客户端到代理、代理到上游是两段独立连接。客户端关闭 WebSocket 后,
服务端仍可能用 WebSocket 访问上游;客户端开关不控制服务端连接池和 HTTP 回退策略。
### 旧版配置
仅含代理密钥的 `auth.json` 配合 `requires_openai_auth = true` 仍可用于已有请求,
但 API Key 登录本身不会启用原生生图。需要生图时换用上述 Provider 配置。
以已核验的 Codex 0.153.4 为准,管理端已移除以下旧字段:
| 字段 | 处理 |
| --- | --- |
| 顶层 `disable_response_storage` | 无有效配置定义,删除 |
| 顶层 `network_access = "enabled"` | 无有效配置定义,删除;不是客户端 API 的联网开关 |
| `features.responses_websockets_v2` | 已标记移除,使用 Provider 的 `supports_websockets` |
`image_generation` 和 `goals` 仍有效。不要为了接入代理,顺带扩大命令沙箱的联网或文件权限。
### 登录与生图排查
- 仍提示登录:确认实际读取的用户配置目录、选中的 Provider 和客户端版本,再完全退出重启。
新 Provider 使用 `requires_openai_auth = false`,不依赖本地 ChatGPT 登录。
- 没有生图工具:检查配置是否被覆盖、Actor 标记是否保留、模型是否支持图片输入。
官方客户端还会检查缓存登录状态;已核验版本在本地账号为 Free 时会隐藏生图。
先备份并区分本地真实账号文件与代理密钥文件,不要直接删除全部登录状态。
- 已调用生图但失败:查看服务端账号的凭据、权限、额度和请求错误,不能只凭文本对话成功判断。
- `401`:检查代理密钥是否正确、是否启用;Actor 标记不能代替密钥。
- `426`:客户端低于管理员设置的最低版本,或版本无法识别,需要更新客户端。
- 地址包含 `5173/dev/v1`:这是开发代理地址,依赖 Vite 服务。日常使用改成后端或 HTTPS 地址;
验证 WebSocket 时直接连接后端,当前 Vite 代理未显式开启 WebSocket 转发。
其他客户端使用 Responses API、`/v1` Base URL 和代理密钥即可,不需要 Actor 标记。
路由与请求格式见 [API 参考](../docs/api.md#3-openai-数据面与模型目录)。
## 优雅关停
收到停止信号后,应用先停止接收新连接并 drain 存量连接;整个 drain 共享一个从停止信号
@@ -115,7 +229,8 @@ Provider schema 以明文 JSON 保存于 PostgreSQL。Redis 只保存可重建
OpenAI 主动额度重置卡及其消费结果由上游持有,不写入 PostgreSQL/Redis,也不属于本地备份内容;
管理端只在当前浏览器会话中保留最近一次查询结果。
备份必须包含 `.runtime/postgres`。要保留 OAuth 的 AT/RT 恢复能力,必须保持
数据库可用管理端的 S3/R2 逻辑备份,或停库后备份 `.runtime/postgres`。
不要在 PostgreSQL 写入期间直接复制数据目录作为一致性备份。要保留 OAuth 的 AT/RT 恢复记录,需保持
`host.logging.file.enabled: true` 并备份 `.runtime/logs`;恢复记录位于独立文件集,仍按普通日志的
`retention_days` 和 `max_files` 分别约束。若希望保留短期 Redis 状态和会话锚点,也同时备份 `.runtime/redis` 与
`.runtime/data`。
@@ -130,7 +245,8 @@ OpenAI 主动额度重置卡及其消费结果由上游持有,不写入 Postgr
已有 PostgreSQL 数据目录后,直接修改 `database.password` 不会修改数据库用户密码,只会导致
应用无法连接。轮换时必须先在 PostgreSQL 中修改用户密码,再同步更新 `config.yaml`。Redis
密码变更后需要使用新密码重建或重新配置 Redis 数据目录。
密码变更后需要用新配置重新创建 Redis 和应用容器,不需要删除 Redis 数据目录。
安排维护窗口,避免应用和 Redis 在过渡期间使用不同密码。
## 镜像升级与源码构建
@@ -153,10 +269,11 @@ docker compose -f deploy/compose.yaml up -d --no-build
仓库维护者发布新版本时必须从干净且已同步上游的分支运行:
```bash
release/publish <version>
release/publish X.Y.Z
```
该脚本负责更新 `release/version.yaml`、创建版本提交和带注释 tag,并原子推送分支与 tag。
将 `X.Y.Z` 替换为待发布版本。脚本需要 Git、Ruby 和已登录的 GitHub CLI,
负责更新 `release/version.yaml`、创建版本提交和带注释 tag,并原子推送分支与 tag。
它不会登录任何服务器,也不会拉取镜像或调用管理端在线更新。
源码提交、仓库发版和运行实例升级是三种独立状态:本地 commit 不等于 Release,Release/tag 和镜像
@@ -199,7 +316,7 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.ta
## 备份与恢复
### 运行时事实
### 备份内容与限制
- 运行时镜像的 `pg_dump`/`pg_restore` 在构建期从与 compose 服务端同一固定
`postgres:18-bookworm` 镜像(`Dockerfile` 的 `POSTGRES_IMAGE`)COPY 到
@@ -226,15 +343,19 @@ Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.ta
工具离线恢复:
```bash
pg_restore --no-owner --no-privileges --dbname=<target> <archive>.dump
pg_restore --no-owner --no-privileges --password \
--dbname='postgresql://restore_user@127.0.0.1:5432/restore_db' backup.dump
```
恢复期间与恢复后的边界见 [架构文档](../docs/architecture.md#11-生命周期安全与恢复)。操作顺序为:
示例中的账号、空目标库和归档路径需按实际情况替换,密码由命令交互输入。
1. 恢复期间以维护模式启动,不运行备份 Worker(避免把快照中的中间状态当作当前任务继续处理)。
2. 恢复完成后清理备份控制记录的非终态(`queued/dumping/uploading/deleting`),并重新计算
`next_run_at`,避免按旧游标立即触发计划。
3. 重新执行当前 S3 配置的连接探针,再允许计划任务运行。
4. 不自动删除恢复前已经存在的远端对象。
恢复期间与恢复后的边界见 [架构文档](../docs/architecture.md#11-生命周期安全与恢复)。
当前没有在线恢复 API,也没有供部署者直接启用的“维护模式”开关:
首版不提供在线恢复 API;恢复属于人工运维操作。
1. 先备份现有数据库,并停止应用;在独立的空数据库中恢复归档,检查数据和迁移版本。
2. 应用保持停止,离线核对备份设置与记录。禁用快照中的旧计划,处理非终态记录
(`queued/dumping/uploading/deleting`)、旧调度游标和到期清理条件,再决定哪些记录保留。
3. 确认不会误执行旧任务或删除恢复前的远端对象后,再启动应用;重新测试 S3 连接并设置计划。
只关闭计划备份不会停止 Worker 的任务恢复和到期删除。无法确认这些记录的影响时,
不要把恢复后的数据库直接接入运行中的应用。具体处理应根据目标库数据制定,不提供清空生产记录的通用命令。
+30 -14
View File
@@ -1,7 +1,8 @@
# Codex Proxy RS 接口
本文列出 v3 当前公开 HTTP 接口。路由事实以
`backend/crates/gateway-api/src` 中的 router 为准。
本文列出 v3 源码中的公开 HTTP 接口,路由以
`backend/crates/gateway-api/src` 中的 router 为准。配置 Codex 请先看 [客户端配置](../deploy/README.md#客户端配置);
运行实例是否包含这些功能,应结合其版本和 revision 确认。
## 1. 鉴权与公共约定
@@ -13,6 +14,10 @@
Authorization: Bearer sk_...
```
Codex 原生生图配置还会携带 `X-OpenAI-Actor-Authorization: proxy-managed`。
它仅用于客户端识别服务端托管认证,不能代替 Client Key。网关和 OpenAI Provider 都会过滤该请求头,
上游账号身份只由服务端选中的账号提供;不要把真实账号 token 放进该标记。
Client Key 通过账号分组限定路由范围:未绑定分组时可使用全部账号,绑定一个或多个分组时只能使用
已启用分组成员的并集。分组可以混合 `openai` 与 `xai` 账号;同一请求只会在模型能力明确匹配且满足
重放安全边界时跨 Provider fallback。
@@ -22,7 +27,7 @@ Client Key 通过账号分组限定路由范围:未绑定分组时可使用全
低于对应门槛时,所有 `/v1/*` HTTP 请求和新 WebSocket 握手在访问上游前返回 `426 Upgrade Required`。
未知客户端保持兼容,不应用版本门禁。
低版本响应使用 OpenAI 风格错误合同:
低版本响应使用 OpenAI 风格错误格式:
```json
{
@@ -96,7 +101,7 @@ body 不进入这个通用信封。稳定业务码如下:
### 管理写入一致性
mutation 请求不要求客户端提供全局配置版本。会改变路由快照或安全配置的写入由后端在事务内推进
管理写入不要求客户端提供全局配置版本。会改变路由快照或安全配置的写入由后端在事务内推进
内部 `config_revision`,并用于快照发布与审计。账号更新和分组查询/写入的部分响应会返回
`configRevision` 作为已提交事实,但它不是客户端 mutation 的前置条件。
@@ -129,6 +134,10 @@ Responses WebSocket 仅接受文本 `response.create`,同一连接串行执行
这对齐 Codex 客户端 `stream_request` 持锁至本轮结束的串行行为,不表示支持额外控制消息类型。
接收队列容量为 32 个事件,超载仍关闭连接;Ping/Pong、客户端关闭和服务关闭不等待队列中的请求执行。
客户端使用 HTTP/SSE 时,OpenAI Provider 仍可能选择上游 WebSocket。
客户端配置的 `supports_websockets` 只控制第一段连接,不是服务端传输策略开关。
上游在响应终态前发送 Close 1000 仍属于失败,不能按“正常关闭”计为成功。
`GET /v1/models` 默认返回 OpenAI 兼容列表 `{"object": "list", "data": [...]}`;请求携带非空
`client_version` query 参数(Codex 客户端)时改为返回 Codex 专用目录合同 `{"models": [...]}`。
@@ -219,8 +228,10 @@ Responses wire 之间的协议转换层,转换只在 xAI Provider 内完成。
当前 xAI 导入上限为 16 MiB。内部 schema 由目标 Provider 独占解释:
- OpenAI 接受单账号 OAuth 文档、`accounts` 数组(最多 200 项)和 CPR 账号 bundle;
- OpenAI OAuth token 字段只识别 `accessToken`、`refreshToken`、`idToken`,可以嵌套在账号 object 内;
每项至少包含 AT 或 RT。RT-only 会在导入时换取 AT,AT-only 不具备自动续期能力;
- OpenAI OAuth token 字段接受 `accessToken`、`refreshToken`、`idToken`,以及官方
`auth.json` 中的 `access_token`、`refresh_token`、`id_token`,可以嵌套在 `tokens` 等账号 object 内;
每项至少包含 AT 或 RT。仅含 `OPENAI_API_KEY` 的客户端代理配置不是 OAuth 账号导入材料;
RT-only 会在导入时换取 AT,AT-only 不具备自动续期能力;
- xAI 从单账号 object 或 `accounts` 数组中提取 OAuth token;包装中的代理、并发、优先级等字段不参与认证;
- xAI 批量导入逐条独立校验:失败条目跳过并记录日志,不中断其余条目,仅当没有任何条目成功时整个导入才报错;
- xAI API Key 不是受支持的账号 credential;
@@ -277,8 +288,9 @@ OAuth start 使用:
### OpenAI 身份、额度与状态
- OAuth 文件导入的 canonical 凭据字段为 `accessToken`、`refreshToken`、`idToken`,不接受含义模糊的
`token`。仅有 refresh token 时先换取 access token。
- OAuth 文件导入接受 camelCase 与 snake_case 的三个 token 字段,内部统一保存为
`accessToken`、`refreshToken`、`idToken`,不接受含义模糊的 `token`。
仅有 refresh token 时先换取 access token。
- 身份补全复用官方 `token_data.rs::parse_chatgpt_jwt_claims`:优先解析 `idToken`,缺失字段再由
`accessToken` 补齐;`email` 优先 JWT 顶层值、其次 `https://api.openai.com/profile.email`,用户 ID
优先 `chatgpt_user_id`、其次 `user_id`。该路径不调用 `whoami`,也不信任导入文档顶层的
@@ -380,8 +392,8 @@ PostgreSQL 或 Redis。管理端只在用户打开弹窗或点击刷新时调用
| `POST` | `/api/admin/account-groups/delete` | `{ id }` | 删除未被 Client Key 引用的组 |
列表数据为 `{ items, page, configRevision }`,其中 item 返回 `memberCount`、按 Provider 聚合的
`providerCounts` 和 `clientKeyCount`。成员响应为 `{ id, items, total, configRevision }`;成员的
`providerKind` 描述账号自身 Provider,并不是分组属性。
`providerCounts` 和 `clientKeyCount`。查询分组成员使用账号列表的 `groupId` 筛选,
不提供独立的分组成员路由;账号的 Provider 不代表整个分组的 Provider。
## 7. Client Key
@@ -457,8 +469,8 @@ Windows 离线包接口固定解析 Microsoft Store Product ID `9PLM9XGG6VKS`
```
`source` 为 `microsoft_store` 或 `official_openai`。Store 的四段 package version 只用于下载展示,不参与
Desktop 三段 SemVer 门禁,也不会自动回写最低版本设置。完整安全与回退设计见
[客户端最低版本与下载方案](client-min-version-plan.md)。
Desktop 三段 SemVer 门禁,也不会自动回写最低版本设置。门禁规则见
[鉴权与公共约定](#1-鉴权与公共约定),解析器职责见 [架构文档](architecture.md#11-生命周期安全与恢复)。
## 9. 备份
@@ -555,6 +567,10 @@ request/response/upstream ID、outcome 与搜索文本。诊断 `dimension` 可
汇总与洞察中的请求数与 outcome 分布覆盖筛选范围内全部请求;token、缓存、延迟与成本聚合仅统计
已完整交付客户端的成功响应。
错误记录中的“已自动恢复”表示系统关联到了后续成功请求,不会把原来的失败记录改为成功。
`upstreamSendState = ambiguous` 表示无法确认该次上游执行结果,不代表后续恢复请求失败;
恢复关联也不等于逐字节验证过两次请求正文。
Dashboard 的 `accountUsage[]` 由后端提供 `usageWindow`、`metricLabel`、`metricValue`。
`usageWindow` 复用账号额度窗口合同,缺失额度事实时为 `null`;窗口标签、百分比、触顶状态、重置时间
和本地用量由 Provider/Admin 投影。前端不得从套餐缺失推断免费套餐,也不得从显示时舍入的百分比推断
@@ -578,5 +594,5 @@ priority 价格,缺少专用价格时回退到标准价格的 `2.00x`;Flex
| `POST` | `/api/admin/system/restart` | 无 | 请求进程重启 |
在线更新仅在当前部署模式、Release 资产和进程重启能力都满足要求时可用,且只在同一 major 版本内
提供:跨大版本目标会以 `40901` 冲突拒绝,需按发布说明手动迁移。仓库发版流程见
[部署文档](../deploy/README.md) 与根目录 [README](../README.md)。
提供:跨大版本目标会以 `40901` 冲突拒绝,需按发布说明重新部署。
实例升级和仓库发版见 [部署文档](../deploy/README.md#镜像升级与源码构建)。
+30 -6
View File
@@ -1,6 +1,6 @@
# Codex Proxy RS 架构
本文回答系统由哪些边界组成、请求如何流动、状态由谁拥有,以及修改时必须保持哪些不变量。
本文说明模块职责、请求流程、状态存储和必须保持的约束。
具体 HTTP 字段见 [接口文档](api.md),部署参数见 [部署文档](../deploy/README.md);上游 URL、超时、
重试间隔和 UI 布局属于源码或配置,不在架构文档重复维护。
@@ -177,6 +177,21 @@ Core 只理解 `Operation`、能力要求、Provider 候选、稳定错误和 ca
- 请求画像以配置为启动基线。OpenAI Desktop 与 xAI CLI 的官方版本检查只更新各自负责的运行时画像,
不回写 `config.yaml`。
### Codex 原生生图与认证
管理端导出的 Codex 配置使用代理 Bearer 密钥,并声明服务端托管账号认证。
客户端据此开放原生生图工具,图片生成和编辑仍进入现有 Images 路由,不新增独立的登录或图片代理服务。
这只解决客户端能力识别;模型能力、客户端限制和上游账号的实际生图权限仍分别检查。
`X-OpenAI-Actor-Authorization` 是客户端能力标记,不是账号凭据。API 解码和 OpenAI Provider
都过滤该 header,网关继续校验 Client Key,上游认证由服务端账号产生。
仅含代理密钥的本地 `auth.json` 可以与新配置共存。配置示例维护在
[部署文档](../deploy/README.md#客户端配置)。
客户端到代理与代理到上游的传输选择相互独立。客户端的 `supports_websockets = false`
不禁止 Provider 使用上游 WebSocket。响应终态前的 Close 1000 仍视为失败,
后续恢复请求成功也不改写原失败请求的结果。
### 错误与诊断三层边界
错误信息按用途分成三层,不能用同一个 `message` 同时承担协议、界面和诊断职责:
@@ -257,7 +272,8 @@ PostgreSQL schema 由迁移目录按编号管理。已应用迁移按字节冻
credential 与 quota 是两组独立事实:credential refresh 不等于 quota refresh,额度接口的 401/403
也不能单独证明 refresh token 永久失效。
- OpenAI 支持 OAuth、AT/RT 与 OAuth JSON;RT-only 导入先换取 AT,AT-only 导入没有
- OpenAI 支持 OAuth、AT/RT 与 OAuth JSON,导入识别 camelCase 和官方 `auth.json` 的 snake_case token 字段;
RT-only 导入先换取 AT,AT-only 导入没有
自动续期能力。OAuth 身份只从官方 JWT claims 投影,不信任导入文档顶层身份字段。
- xAI 使用 OAuth session;API Key 不是受支持的账号 credential。
- 新账号导入和首次 OAuth 在 credential 提交后尽力读取一次额度;失败只留下观测,不回滚账号事务。
@@ -303,13 +319,17 @@ HTTP Client 构造失败也不会阻断网关启动。外部解析在已认证
- Provider credential 以 Provider schema 的明文 JSON 保存在 PostgreSQL;数据库和备份必须按敏感数据保护。
- OAuth 恢复日志包含原始 AT/RT,`.runtime/logs` 同样属于敏感数据。
- `host.logging.request_dump` 默认关闭;开启后独立请求转储包含原始请求头和正文,
可能包含密钥及用户内容,只能在明确的排障范围内使用,不能作为普通日志公开。
- 真实 secret 不进入普通日志、Debug、fixture 或 audit details;明文只能通过账号导出、Key reveal、
备份设置等明确的敏感 Admin 合同返回。
- OAuth pending flow 使用有期限、带 owner 的一次性 claim;事务成功后才消费,失败释放 claim。
- 在线更新校验 Release host、大小、SHA-256 和归档路径,并只允许同一大版本内更新。
PostgreSQL 备份恢复属于人工维护操作:恢复时停用备份 Worker,清理快照中的非终态任务,重算计划游标并
重新验证对象存储;不会自动删除恢复前已经存在的远端对象。
PostgreSQL 备份恢复属于人工维护操作。当前没有部署级维护模式开关,需要先停止应用,
离线处理快照中的非终态任务、计划游标和到期清理条件,再重新验证对象存储并恢复计划。
关闭计划开关只阻止新计划任务,不会停止 Worker 的任务恢复和删除流程。
未经核对就启动旧快照,可能触发远端对象删除;操作步骤见 [部署文档](../deploy/README.md#人工恢复数据库)。
## 12. 修改与验收
@@ -320,12 +340,16 @@ PostgreSQL 备份恢复属于人工维护操作:恢复时停用备份 Worker
```bash
cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check
cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
pnpm --dir frontend format:check
pnpm --dir frontend build
docker compose -f deploy/compose.yaml config --quiet
```
线程栈设置与当前 CI 一致。PostgreSQL/Redis 集成测试需按
[迁移文档](../backend/migrations/README.md#本地测试库) 配置专用测试库;未设置环境变量时,本地相关测试会跳过。
前端以 lint、类型检查和构建验证,`build` 已包含类型检查,不维护独立前端测试代码。
行为、配置或边界变化必须同步其唯一文档 owner:用户入口写入根 README,HTTP 合同写入 `docs/api.md`,
部署操作写入 `deploy/README.md`,架构不变量保留在本文。
+15 -6
View File
@@ -5,15 +5,18 @@
# 管理端主题系统
**基于 Seed、Map、Alias 与 Component Token 的运行时主题架构**
浅色、深色、预置配色与自定义主题的配置和实现说明。
[设计原则](#设计原则) · [Token 模型](#token-模型) · [运行时架构](#运行时架构) · [主题编辑器](#主题编辑器) · [扩展指南](#扩展指南)
</div>
主题系统为 Codex Proxy RS 管理端提供浅色、深色、预置配色与自定义配色能力。颜色 Map 复用 Ant Design 的
Token 分层与十阶色板,再按项目的明暗角色规则派生;中性 Surface 由背景和文字 Seed 统一派生。不引入 `antd`、React、`ConfigProvider`
或 CSS-in-JS,Vue 组件继续通过 CSS Variables 与 Tailwind CSS 4 utilities 消费主题。
在管理端「主题」中调整颜色、字号、圆角和组件样式,保存后应用到当前浏览器。
设置保存在浏览器本地,不会同步到服务器或其他设备。只想换配色时,选择预置主题即可。
下文说明实现和扩展方式。颜色使用 Ant Design 十阶色板与项目的明暗角色规则,
中性表面从背景和文字 Seed 派生;Vue 组件通过 CSS Variables 与 Tailwind CSS 4 使用这些值。
项目不依赖 Ant Design 组件库或 CSS-in-JS。
> [!IMPORTANT]
> 主题的目标是调整颜色、密度、圆角与层级,不改变现有页面结构和产品语义。默认主题保留既有表面层级,并满足正常文字的可读性约束;
@@ -195,8 +198,13 @@ Token 直接覆盖时不会自动重算同组件的其他状态;需要保持
主按钮的颜色在 Component 派生入口统一生成:文字保留 `colorTextLightSolid`,默认背景从 Primary Seed 校正到
4.5:1,Hover/Active 在该背景上分别混入 8% / 16% 黑色,使白字对比度逐级增强;明暗模式共用此规则。
这组组件状态与全局 `colorPrimaryHover/Active` 分工明确,调整按钮不会反向改写主色 Seed。输入框 Hover 保留柔和
外圈,Focus 使用已有 `control-outline`,错误外圈使用 Error Border,普通状态继续保持无边设计。
这组组件状态与全局 `colorPrimaryHover/Active` 分工明确,调整按钮不会反向改写主色 Seed。
输入框 Hover 与 Focus 使用 Primary Container Hover 的同色、同宽外圈;
错误外圈使用带透明度的 Error 色,具体值由 Input Component Token 提供。
结构化浮层头部统一使用 `--cp-popover-header-bg`:浅色从 Secondary Fill 派生,
深色从 Tertiary Fill 派生。健康时间线朝向头部的箭头也使用该值;
不要把浮层头部重新绑定到表格斑马纹或普通悬停背景。该 Token 可在主题编辑器中单独调整。
Theme Editor 只开放真正由对应组件消费的 Component Token。全局 Alias 不放进组件目录,避免一次覆盖同时改变
多个无关组件。
@@ -350,6 +358,7 @@ utility / arbitrary variant;Vue Transition、跨浏览器 Range、动态富文
- 静态填充、Hover、Active 与 Selected 必须使用不同角色,不能复用一个变量制造所有层级。
- Input 的 Hover 与 Active 只改变内部填充;外圈反馈保持同色同宽,错误状态使用独立 Error Token。
- 表格斑马纹使用 `table-row-stripe-bg`,不借用 `row-hover-bg`。
- 结构化浮层头部使用 `popover-header-bg`,明暗模式分别检查它与表格背景的区分。
- Card 和选项默认不增加装饰性边框;键盘焦点必须保留可见反馈。
- 阴影保持中性,`shadowStrength` 只调节层级强弱,不给阴影染品牌色。
+11 -6
View File
@@ -23,6 +23,8 @@ description: Codex Proxy RS 仓库开发与审计指南。Use for its Rust gatew
| HTTP 路由、DTO、敏感导入/导出 | `docs/api.md` |
| 系统边界、数据流、状态 owner、不变量 | `docs/architecture.md` |
| Compose、密码、备份、更新与恢复 | `deploy/README.md` |
| Codex 配置、auth.json 与原生生图 | `deploy/README.md#客户端配置` |
| 主题配置与 Token | `docs/theme.md` |
| 迁移冻结与测试库 | `backend/migrations/README.md` |
| 具体目录和常见改动入口 | `references/repo-guide.md` |
@@ -39,20 +41,22 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段
- 已应用迁移按字节冻结;schema 变化新增编号迁移,并同步 `.frozen-sha256`。
- 真实 secret 不进入普通日志、Debug、fixture、audit details 或文档示例;明文 Admin 响应只能出现在
账号导出、Key reveal、备份设置等明确敏感合同中。
- 测试放在各 package 的 `tests/`,统一挂载到单一 `main` 集成测试目标;生产 `src` 不写 test-only 代码。
- 后端测试放在各 package 的 `tests/`,统一挂载到单一 `main` 集成测试目标;生产 `src` 不写 test-only 代码。
- 不添加兼容 shim、第二套状态机、重复配置或跨层旁路。
## 当前关键合同
- 数据面公开 Responses JSON/SSE/WS(含 Codex 子代理语义)、Images generation/edit 和模型目录;不提供 Chat Completions。
- OpenAI Responses/Images 保持业务 wire 透明;xAI 在 Provider 内完成 Grok/Responses 转换。
- 数据面公开 Responses JSON/SSE/WS(含 Codex 子代理语义)、Images generation/edit、standalone Search 和模型目录;不提供 Chat Completions。
- OpenAI Responses/Images/Search 保持业务 wire 透明;xAI 在 Provider 内完成 Grok/Responses 转换。
- Images 固定走 OpenAI Provider 自有端点,不要求模型,也不参与文本模型映射。
- Codex 生图模板保留代理密钥 `auth.json`,使用服务端托管认证标记;该标记不能代替 Bearer 鉴权,
并在 API 与 Provider 层被过滤。客户端 WebSocket 开关与上游传输策略相互独立。
- OpenAI 账号支持 OAuth、AT/RT 和 OAuth JSON;xAI 只接受 OAuth session/账号 JSON,
不接受 API Key。
- 主动额度重置卡只由 OpenAI 上游持有。查询由用户显式触发,消费使用 UUIDv4 幂等键;不得写本地卡库存
或直接改 quota reset 时间。
- credential 与 quota 是独立事实;刷新 token 不等于刷新额度,quota 401/403 也不等于 RT 永久失效。
- 当前业务 schema 由 `0001_initial.sql` 建立。
- `0001_initial.sql` 建立基线,后续编号迁移继续演进;完整清单以 `.frozen-sha256` 为准。
- 运行拓扑是单副本;worker lease 不是完整多副本 leader election,自更新也只替换当前进程。
## 前端约束
@@ -61,6 +65,7 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段
- `frontend/src/api` 只保留 wire DTO 与请求函数;页面查询、缓存和交互状态留在对应 view/composable。
- 修改页面前先检查共享组件和相邻调用方;复用既有视觉语言,保持紧凑、低噪声和键盘可访问。
- 上游查询不得由列表渲染或隐藏轮询意外触发。不可逆动作需要明确确认、loading 和不确定结果恢复路径。
- 当前不维护前端测试代码或独立测试命令,使用 lint、类型检查、构建和必要的界面核验。
## 验证
@@ -68,8 +73,8 @@ README 保持用户导向;不要把上游 URL、重试常量、数据库字段
```bash
cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check
cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
pnpm --dir frontend format:check
pnpm --dir frontend build
docker compose -f deploy/compose.yaml config --quiet
+13 -9
View File
@@ -43,11 +43,13 @@
- Responses:`POST /v1/responses`、WebSocket `GET /v1/responses`;
- Images:`POST /v1/images/generations`、`POST /v1/images/edits`;
- Codex 独立搜索:`POST /v1/alpha/search`;
- 模型:`GET /v1/models`、`GET /v1/models/{model_id}`。
Responses adapter 解析参与路由的最少语义,并保留原始请求;review 等子代理仍走 `/v1/responses`,
类型由 `x-openai-subagent` 携带。Images adapter 不解析模型或重建 JSON,直接创建
`GenerateImage` operation。两者都进入同一 Core execution lifecycle。
`GenerateImage` operation。Search 同样保留原始正文,不参与模型映射。
这些请求都进入同一 Core execution lifecycle。
定位请求问题时按以下顺序追踪:
@@ -69,8 +71,8 @@ gateway-api adapter
- account retry 与跨 Provider 候选推进由 Core 管理;
- response ID 和原始 wire 不因观测模型而重写。
OpenAI 保持 Responses/SSE/WS/Images 业务字节透明;xAI 在自己的 Provider 内完成 Grok/Responses 转换。
Images 固定调用 OpenAI Provider 自有端点,候选不带 `upstream_model`。
OpenAI 保持 Responses/SSE/WS/Images/Search 业务字节透明;xAI 在自己的 Provider 内完成 Grok/Responses 转换。
Images 和 Search 固定调用 OpenAI Provider 自有端点,候选不带 `upstream_model`。
## 路由与 continuation
@@ -85,14 +87,14 @@ OpenAI continuation 按 native/replay-owner/replay-any 推进,xAI 使用客户
## 账号控制面
账号路由和 DTO 在 `gateway-api/src/admin/accounts.rs`,业务编排在
账号路由和 DTO 在 `gateway-api/src/admin/accounts/` 的 handlers、credentials、wire 与 presenter 中,业务编排在
`gateway-admin/src/use_case/accounts.rs`,Provider 解析/验证后由 Store 事务提交。
### 导入与授权
- 公共导入合同始终是 `{ provider, data }`,其中 `data` 为 Provider-owned JSON object。
- OpenAI 支持 OAuth JSON、`accounts` 数组、AT-only、RT-only 和 AT+RT+可选 ID Token;
最多 200 项。RT-only 先交换 AT,AT-only 不会凭空获得刷新能力。
最多 200 项。token 字段接受 camelCase 和官方 snake_case;RT-only 先交换 AT,AT-only 不具备刷新能力。
- OpenAI OAuth 身份来自 `parse_chatgpt_jwt_claims`,不信任导入 object 顶层的 user/account ID。
- 前端 AT/RT 标签只把“每行一个 token”转换成 `accounts` JSON,没有第二条导入 API。
- xAI 接受 OAuth 账号 object/数组,逐项验证;API Key 不是 credential。
@@ -115,7 +117,7 @@ OpenAI continuation 按 native/replay-owner/replay-any 推进,xAI 使用客户
## 存储与迁移
PostgreSQL 业务 schema 由 `0001_initial.sql` 建立。`.frozen-sha256` 覆盖全部迁移,
PostgreSQL 业务 schema 由 `0001_initial.sql` 和后续编号迁移共同组成。`.frozen-sha256` 覆盖全部迁移,
已应用文件不可回改。
主要状态边界:
@@ -148,7 +150,9 @@ continuation 等正确性依赖的协调写入不经该 best-effort 队列。
- 页面状态:对应 `views/*/composables/`;
- 页面展示转换:对应组件或相邻 presenter/util;
- 通用交互:`components/base/`;
- 主题:`styles/tokens.css` 与既有 `cp-*` token。
- 主题派生:`frontend/src/theme/`;样式桥接在 `styles/index.css`,`styles/tokens.css` 只保存静态基元。
- Codex 配置:`views/api-keys/utils/codexConfig.ts`,界面与 CCSwitch 共用;
保留 API Key auth.json,只用 Provider 的 `supports_websockets` 控制客户端 WebSocket。
不要把页面状态塞进 API module,也不要为单一页面创造第二套通用层。修改共享组件前检查所有调用方;
异步上游动作必须区分 loading、明确失败和结果不确定。
@@ -157,8 +161,8 @@ continuation 等正确性依赖的协调写入不经该 best-effort 队列。
```bash
cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check
cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
RUST_MIN_STACK=16777216 cargo +1.97.0 clippy --manifest-path backend/Cargo.toml --all-targets --all-features --locked -- -D warnings
RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked
pnpm --dir frontend format:check
pnpm --dir frontend build
docker compose -f deploy/compose.yaml config --quiet