mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
docs: refresh v3 provider and deployment guides
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
[](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs)
|
||||
[](https://opensource.org/license/mit)
|
||||
|
||||
[快速开始](#快速开始) · [客户端接入](#客户端接入) · [运维](#运维) · [部署文档](deploy/README.md) · [架构](docs/architecture.md)
|
||||
[快速开始](#快速开始) · [客户端接入](#客户端接入) · [运维](#运维) · [部署](deploy/README.md) · [接口](docs/api.md) · [架构](docs/architecture.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -21,14 +21,15 @@
|
||||
|
||||
## 能力
|
||||
|
||||
| 领域 | 实现 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| 协议 | OpenAI Responses JSON、SSE、WebSocket 与模型目录 |
|
||||
| Provider | OpenAI OAuth、xAI OAuth,多账号隔离与自动刷新 |
|
||||
| 路由 | Client Key 平台绑定、Provider instance、模型映射与安全 fallback |
|
||||
| 延续 | OpenAI native/replay continuation;xAI 客户端完整历史 |
|
||||
| 管理 | Client Key、账号、模型目录、设置、观测、系统与 OAuth |
|
||||
| 计量 | 模型请求 Token、费用、延迟、账号与 Provider 归因 |
|
||||
| 领域 | 实现 |
|
||||
| -------- | --------------------------------------------------------------------------- |
|
||||
| 协议 | OpenAI Responses JSON、SSE、WebSocket 与模型目录 |
|
||||
| Provider | 固定 OpenAI、xAI 两个 Provider;各自管理 OAuth、账号、额度与模型目录 |
|
||||
| 路由 | Client Key 绑定 Provider、全局模型映射、会话亲和与同 Provider 安全 fallback |
|
||||
| 透明边界 | OpenAI 保持 Responses 语义透明;xAI 的协议适配只驻留在 `providers/xai` |
|
||||
| 延续 | OpenAI native/replay continuation;xAI 使用客户端提交的完整历史 |
|
||||
| 管理 | Client Key、账号、模型目录、设置、观测、系统更新与 OAuth |
|
||||
| 计量 | 模型请求 Token、费用、延迟、账号与 Provider 归因 |
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -47,15 +48,14 @@ sudo chown "$(id -u):10001" deploy/config.yaml
|
||||
chmod 0640 deploy/config.yaml
|
||||
```
|
||||
|
||||
分别生成三个密码:
|
||||
分别生成 PostgreSQL 与 Redis 密码:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 24
|
||||
openssl rand -hex 24
|
||||
openssl rand -hex 24
|
||||
```
|
||||
|
||||
把结果写入 `deploy/config.yaml`:
|
||||
把两个结果写入 `deploy/config.yaml`,并单独设置管理员初始密码:
|
||||
|
||||
| 配置 | 约束 |
|
||||
| -------------------------- | ---------------------------- |
|
||||
@@ -64,7 +64,7 @@ openssl rand -hex 24
|
||||
| `admin.default_password` | 至少 12 位,不能包含 `$` |
|
||||
|
||||
PostgreSQL、Redis 容器和应用通过同一份 `config.yaml` 使用这两个基础设施密码,不需要额外
|
||||
导出环境变量。
|
||||
导出环境变量。管理员密码只在首次创建管理员时生效。
|
||||
|
||||
Linux 需要允许容器组写入运行目录:
|
||||
|
||||
@@ -88,9 +88,10 @@ curl -i http://127.0.0.1:8080/healthz
|
||||
### 3. 初始化
|
||||
|
||||
1. 使用 `admin@cpr.local` 与初始密码登录。
|
||||
2. 创建 Provider instance,并通过 OAuth 或 JSON 导入 OpenAI、xAI 账号。
|
||||
3. 按平台配置客户端模型到上游模型的映射;未配置的模型名原样透传。
|
||||
4. 创建 `sk_...` 客户端 Key,绑定平台并设置模型范围、速率与并发限制。
|
||||
2. 在账号页选择对应 Provider:OpenAI 支持 OAuth、账号文件与 Agent Identity;xAI 支持 OAuth
|
||||
和兼容账号文件导入。
|
||||
3. 按需配置客户端模型到上游模型的全局映射;未命中映射的模型名原样透传。
|
||||
4. 创建 `sk_...` Client Key,绑定 `openai` 或 `xai`,并设置模型范围、速率与并发限制。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> xAI 使用 OAuth session,不支持把 xAI API Key 作为上游 credential。
|
||||
@@ -125,18 +126,22 @@ curl http://127.0.0.1:8080/v1/responses \
|
||||
<details>
|
||||
<summary>API 路由</summary>
|
||||
|
||||
| 路由 | 用途 |
|
||||
| --------------------------- | ------------------- |
|
||||
| `POST /v1/responses` | JSON 或 SSE 透明代理 |
|
||||
| `GET /v1/responses` | WebSocket 透明代理 |
|
||||
| `POST /v1/responses/review` | OpenAI review 请求 |
|
||||
| `GET /v1/models` | 启用的公开模型列表 |
|
||||
| `GET /v1/models/{model_id}` | 公开模型详情 |
|
||||
| 路由 | 用途 |
|
||||
| ---------------------------------- | ------------------------------ |
|
||||
| `POST /v1/responses` | JSON 响应或 SSE Responses 流 |
|
||||
| `GET /v1/responses` | Responses WebSocket 升级 |
|
||||
| `POST /v1/responses/review` | review 子代理请求 |
|
||||
| `GET /v1/models` | 当前 Client Key 可用的模型列表 |
|
||||
| `GET /v1/models/catalog` | Codex 客户端展示用模型目录 |
|
||||
| `GET /v1/models/{model_id}/info` | Codex 客户端展示用模型详情 |
|
||||
| `GET /v1/models/{model_id}` | OpenAI 兼容模型详情 |
|
||||
|
||||
所有 `/v1/*` 路由都需要客户端 API Key。
|
||||
|
||||
</details>
|
||||
|
||||
完整的客户端与管理端路由、鉴权和 mutation 规则见 [接口文档](docs/api.md)。
|
||||
|
||||
## 运维
|
||||
|
||||
```bash
|
||||
@@ -153,14 +158,27 @@ docker compose -f deploy/compose.yaml up -d
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> `.runtime/` 保存 PostgreSQL、Redis、自更新状态和日志。删除该目录会永久清除运行状态。
|
||||
> `.runtime/` 保存 PostgreSQL、Redis、日志、OpenAI 会话锚点和自更新状态。删除该目录会永久清除
|
||||
> 运行状态。
|
||||
|
||||
Compose 默认只绑定 `127.0.0.1`。公网接入应使用 HTTPS 反向代理,转发 WebSocket
|
||||
upgrade 与真实客户端 IP;不要暴露 PostgreSQL 或 Redis。
|
||||
|
||||
## 维护与发布
|
||||
|
||||
发布仓库版本必须从干净、已同步上游的发布分支运行:
|
||||
|
||||
```bash
|
||||
release/publish <version>
|
||||
```
|
||||
|
||||
脚本会更新 `release/version.yaml`、创建约定提交与带注释的 `v<version>` tag,并原子推送分支和 tag。
|
||||
不要手工修改版本后单独打 tag。
|
||||
|
||||
## 文档
|
||||
|
||||
- [部署](deploy/README.md)
|
||||
- [客户端与管理端接口](docs/api.md)
|
||||
- [架构与数据边界](docs/architecture.md)
|
||||
- [Release](https://github.com/zyycn/codex-proxy-rs/releases)
|
||||
- [容器镜像](https://github.com/zyycn/codex-proxy-rs/pkgs/container/codex-proxy-rs)
|
||||
|
||||
+37
-76
@@ -19,21 +19,21 @@ sudo chown "$(id -u):10001" deploy/config.yaml
|
||||
chmod 0640 deploy/config.yaml
|
||||
```
|
||||
|
||||
分别执行三次以下命令:
|
||||
为 PostgreSQL 与 Redis 分别生成一个密码:
|
||||
|
||||
```bash
|
||||
openssl rand -hex 24
|
||||
```
|
||||
|
||||
把结果写入 `deploy/config.yaml`:
|
||||
把两个 48 位十六进制结果分别写入 `deploy/config.yaml` 的:
|
||||
|
||||
- `store.database.password`
|
||||
- `store.redis.password`
|
||||
- `admin.default_password`
|
||||
|
||||
PostgreSQL 与 Redis 密码必须是 48 位十六进制字符。管理员初始化密码至少 12 位、不能是
|
||||
常见弱口令且不能包含 `$`。Compose 通过 `config.yaml` 的凭据桥接区引用同一密码,三个密码
|
||||
都不需要额外导出为环境变量,也不能嵌入连接 URL。
|
||||
另行设置 `admin.default_password`。它至少需要 12 个字符,不能是常见弱口令,也不能包含 `$`。
|
||||
|
||||
PostgreSQL 与 Redis 密码必须是 48 位十六进制字符。Compose 通过 `config.yaml` 的凭据桥接区
|
||||
引用同一密码;三个值都不需要额外导出为环境变量,数据库和 Redis 密码也不能嵌入连接 URL。
|
||||
|
||||
Linux 上应用容器以 `10001:10001` 运行,需要允许该组写入应用数据和日志目录:
|
||||
|
||||
@@ -45,6 +45,11 @@ chmod 0770 .runtime/data .runtime/logs
|
||||
`config.yaml` 通过 Compose `configs` 只读挂载。普通 Compose 对本地文件保留宿主机的
|
||||
UID/GID 和 mode,因此配置由当前用户持有,并只向容器组 `10001` 开放读取权限。
|
||||
|
||||
模板中的 `openai` / `xai` 只保留请求画像启动基线。OpenAI 的上游地址、WebSocket 池、额度刷新
|
||||
与 OAuth 设置,以及 xAI 的 OAuth、额度和模型目录策略,均由各自 Provider 使用代码内默认值管理;
|
||||
模板不重复列出这些默认项。运行后,OpenAI CLI/Desktop 与 xAI CLI 的官方发布检查只更新进程内
|
||||
版本字段,不回写 `config.yaml`;检查失败时继续使用上一份有效画像。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
@@ -78,80 +83,27 @@ cargo run -p codex-proxy-rs
|
||||
后端会从当前目录向上查找 `deploy/config.yaml`。相对数据和日志目录以该文件所在目录解析;
|
||||
Compose 只把监听地址和数据库、Redis 地址固定覆盖为容器内部服务名。
|
||||
|
||||
## 持久化
|
||||
## 持久化与备份
|
||||
|
||||
Compose 使用以下绑定目录:
|
||||
|
||||
- `.runtime/data` → 应用身份密钥、credential 加密 keyring 和更新状态
|
||||
- `.runtime/data` → OpenAI 会话锚点密钥、更新状态与临时更新目录
|
||||
- `.runtime/logs` → 应用文件日志
|
||||
- `.runtime/postgres` → PostgreSQL
|
||||
- `.runtime/redis` → Redis AOF
|
||||
|
||||
普通 `docker compose down` 不会删除这些目录。删除 `.runtime` 会永久清除本地状态。
|
||||
|
||||
PostgreSQL 由 `0001_initial.sql` 创建当前业务结构,Redis 保存可丢失的协调状态。
|
||||
PostgreSQL 是账号、Client Key、运行设置、请求记录与审计的权威存储;账号 credential 当前按
|
||||
Provider schema 以明文 JSON 保存于 PostgreSQL。Redis 只保存可重建、可过期的协调状态,例如
|
||||
会话亲和、lease、cooldown、OAuth pending flow 与套餐模型目录 cache。
|
||||
|
||||
备份必须包含 `.runtime/postgres`。若希望保留短期 Redis 状态、OpenAI 会话亲和和更新状态,
|
||||
同时备份 `.runtime/redis` 与 `.runtime/data`;后两者丢失不会造成账号 credential 丢失,但会使
|
||||
缓存和会话锚点重新建立。
|
||||
|
||||
完整运行时、Provider、revision 与恢复边界见 [架构文档](../docs/architecture.md)。
|
||||
|
||||
## Credential 加密 keyring
|
||||
|
||||
首次启动会在 `.runtime/data/credential_keyring` 生成:
|
||||
|
||||
- `active_key_id`:新 envelope 唯一使用的 active key ID。
|
||||
- `envelope_keys/<key-id>.key`:active key 与只读历史解密 key。
|
||||
- `credential_fingerprint_hmac_key`:稳定 credential 去重域,不能随 envelope key 轮换。
|
||||
- `resource_pseudonym_hmac_key`:稳定资源伪名域,不能随 envelope key 轮换。
|
||||
|
||||
目录权限固定为 `0700`,文件权限固定为 `0600`;符号链接、组/其他用户可读权限、无效 key ID
|
||||
或重复 key 材料都会让进程拒绝启动。Keyring 不进入 PostgreSQL,必须和 PostgreSQL 备份分开保存;
|
||||
丢失任何仍被数据库引用的 envelope key 后,对应 credential、Cookie 或 continuation 将无法恢复。
|
||||
|
||||
Envelope key 使用“读旧写新”两阶段轮换。多节点必须为每个节点执行相同步骤:
|
||||
|
||||
1. 生成相同的新 key ID 和 32-byte key,写入 `envelope_keys/<key-id>.key`,保持当前
|
||||
`active_key_id` 不变。
|
||||
2. 滚动重启全部节点,使每个旧进程都先把新 key 加载为历史解密 key。
|
||||
3. 在全部节点原子替换 `active_key_id`,再滚动重启。已重启节点只用新 key 写入,尚未重启节点
|
||||
仍能用第二步加载的 key 解密新 envelope。
|
||||
|
||||
单机 Compose 可按以下方式准备和激活新 key;不要把命令输出或 key 文件内容写入日志:
|
||||
|
||||
```bash
|
||||
new_key_id="ek-$(openssl rand -hex 16)"
|
||||
openssl rand -hex 32 | sudo sh -c \
|
||||
'umask 077; cat > ".runtime/data/credential_keyring/envelope_keys/$1.key"' sh "$new_key_id"
|
||||
sudo chown 10001:10001 ".runtime/data/credential_keyring/envelope_keys/$new_key_id.key"
|
||||
|
||||
# 先重启一次,让当前进程把新 key 作为历史 key 加载。
|
||||
docker compose -f deploy/compose.yaml restart codex-proxy-rs
|
||||
|
||||
sudo env NEW_KEY_ID="$new_key_id" sh -c '
|
||||
umask 077
|
||||
printf "%s" "$NEW_KEY_ID" > .runtime/data/credential_keyring/active_key_id.next
|
||||
chown 10001:10001 .runtime/data/credential_keyring/active_key_id.next
|
||||
mv -f .runtime/data/credential_keyring/active_key_id.next \
|
||||
.runtime/data/credential_keyring/active_key_id
|
||||
'
|
||||
docker compose -f deploy/compose.yaml restart codex-proxy-rs
|
||||
unset new_key_id
|
||||
```
|
||||
|
||||
历史 key 只有在所有引用计数均为零、备份也满足同一退役策略后才能删除:
|
||||
|
||||
```sql
|
||||
select 'upstream_credentials' as source, count(*) from upstream_credentials where secret_key_id = $1
|
||||
union all
|
||||
select 'codex_account_cookies', count(*) from codex_account_cookies where secret_key_id = $1
|
||||
union all
|
||||
select 'conversation_items', count(*) from conversation_items where secret_key_id = $1
|
||||
union all
|
||||
select 'continuation_bindings', count(*) from continuation_bindings where secret_key_id = $1;
|
||||
```
|
||||
|
||||
删除仍被引用的历史 key 不会降级或尝试其他 key;解密会以 unknown key fail closed。确认零引用后,
|
||||
从全部节点删除对应 `.key` 文件并滚动重启,才算完成退役。Fingerprint 与 resource pseudonym 两个
|
||||
HMAC key 不属于此轮换流程,不能用 envelope key 替换。
|
||||
|
||||
## 密码语义
|
||||
|
||||
- `admin.default_password` 只在首次创建管理员时使用。
|
||||
@@ -159,9 +111,10 @@ HMAC key 不属于此轮换流程,不能用 envelope key 替换。
|
||||
- Redis 在每次容器创建时使用 `redis.password`。
|
||||
|
||||
已有 PostgreSQL 数据目录后,直接修改 `database.password` 不会修改数据库用户密码,只会导致
|
||||
应用无法连接。轮换时必须先在 PostgreSQL 中修改用户密码,再同步更新 `config.yaml`。
|
||||
应用无法连接。轮换时必须先在 PostgreSQL 中修改用户密码,再同步更新 `config.yaml`。Redis
|
||||
密码变更后需要使用新密码重建或重新配置 Redis 数据目录。
|
||||
|
||||
## 构建与升级
|
||||
## 镜像升级与源码构建
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/compose.yaml build codex-proxy-rs
|
||||
@@ -175,6 +128,14 @@ docker compose -f deploy/compose.yaml pull codex-proxy-rs
|
||||
docker compose -f deploy/compose.yaml up -d --no-build
|
||||
```
|
||||
|
||||
仓库维护者发布新版本时必须从干净且已同步上游的分支运行:
|
||||
|
||||
```bash
|
||||
release/publish <version>
|
||||
```
|
||||
|
||||
该脚本负责更新 `release/version.yaml`、创建版本提交和带注释 tag,并原子推送分支与 tag。
|
||||
|
||||
构建元数据仍可作为一次性进程环境传入,不需要 `.env` 文件:
|
||||
|
||||
```bash
|
||||
@@ -184,22 +145,22 @@ CPR_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
docker compose -f deploy/compose.yaml build codex-proxy-rs
|
||||
```
|
||||
|
||||
### 管理端 Release 更新
|
||||
### 管理端在线更新
|
||||
|
||||
Compose 已显式装配正式更新所需的运行参数:
|
||||
Compose 已显式装配正式发布构建所需的运行参数:
|
||||
|
||||
- `CPR_UPDATE_REPOSITORY`:只接受 `owner/repository`;默认 `zyycn/codex-proxy-rs`。
|
||||
- `CPR_GITHUB_API_BASE`:正式环境必须为 `https://api.github.com/repos`。
|
||||
- `CPR_UPDATE_CHANNEL`:`stable` 会拒绝 prerelease。
|
||||
- `CPR_UPDATE_EXE_PATH`、`CPR_WEB_DIST_DIR`:分别指向容器内二进制和旧前端静态目录。
|
||||
- `CPR_UPDATE_EXE_PATH`、`CPR_WEB_DIST_DIR`:分别指向容器内二进制和前端静态目录。
|
||||
- `CPR_UPDATE_TEMP_DIR`、`CPR_UPDATE_STATE_FILE`、`CPR_UPDATE_LOCK_FILE`:全部位于持久化的
|
||||
`.runtime/data`。
|
||||
- `CPR_ENABLE_SELF_RESTART=true`:更新或回滚完成后允许管理端请求重启;Docker 进程退出后由
|
||||
Compose 的 `restart: unless-stopped` 拉起新进程。
|
||||
|
||||
Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.tar.gz` 和
|
||||
`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和 tar
|
||||
路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为
|
||||
Release 必须提供当前 OS/架构的 `codex-proxy-rs_<version>_<os>_<arch>.tar.gz` 与
|
||||
`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和
|
||||
归档路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为
|
||||
`*.backup`,管理端 rollback 会交换当前文件与这份备份。更新状态和跨进程锁可在以下位置排查:
|
||||
|
||||
```text
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# sudo chown "$(id -u):10001" deploy/config.yaml
|
||||
# chmod 640 deploy/config.yaml
|
||||
#
|
||||
# 三个密码建议分别执行 `openssl rand -hex 24` 生成。
|
||||
# PostgreSQL 与 Redis 密码分别执行 `openssl rand -hex 24` 生成;管理员初始密码单独设置。
|
||||
|
||||
schema_version: 1
|
||||
|
||||
@@ -51,8 +51,9 @@ api:
|
||||
request_id_header: 'x-request-id'
|
||||
|
||||
openai:
|
||||
# 上游地址、WebSocket 池、额度刷新和 OAuth 使用 Provider 内置默认值;模板只保留画像启动基线。
|
||||
wire_profile:
|
||||
# 该画像来自 Desktop 制品与 bundled Codex Core 的联合审计。
|
||||
# Provider 每 24 小时读取官方 npm metadata 与 Desktop appcast,仅在进程内更新版本字段。
|
||||
originator: 'Codex Desktop'
|
||||
codex_version: '0.145.0'
|
||||
desktop_version: '26.721.31836'
|
||||
@@ -64,8 +65,9 @@ openai:
|
||||
verified_at: '2026-07-25T00:00:00+08:00'
|
||||
|
||||
xai:
|
||||
# OAuth 端点、额度与模型目录策略由 xAI Provider 固定管理;这里只声明请求画像启动基线。
|
||||
wire_profile:
|
||||
# 对照 grok2api 当前推荐构建核验;更新前应重新抓包确认。
|
||||
# Provider 每 24 小时读取官方 npm metadata,仅在进程内更新 client_version。
|
||||
client_identifier: 'grok-shell'
|
||||
client_version: '0.2.106'
|
||||
client_mode: 'headless'
|
||||
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
# Codex Proxy RS 接口
|
||||
|
||||
本文列出 v3.0.0 当前公开 HTTP 接口。路由事实以
|
||||
`backend/crates/gateway-api/src` 中的 router 为准。
|
||||
|
||||
## 1. 鉴权与公共约定
|
||||
|
||||
### 客户端接口
|
||||
|
||||
所有 `/v1/*` 请求都使用管理端创建的 Client Key:
|
||||
|
||||
```http
|
||||
Authorization: Bearer sk_...
|
||||
```
|
||||
|
||||
Client Key 固定绑定一个 Provider:`openai` 或 `xai`。同一次请求不会跨 Provider fallback。
|
||||
|
||||
### 管理接口
|
||||
|
||||
除登录、会话状态和登出外,所有 `/api/admin/*` 请求都需要以下任一鉴权方式:
|
||||
|
||||
- 浏览器登录后得到的 `cpr_admin_session` Cookie;
|
||||
- `x-api-key: <admin-api-key>`。
|
||||
|
||||
已鉴权的管理请求还必须携带非空 `x-request-id`。管理端响应统一带
|
||||
`Cache-Control: no-store`。
|
||||
|
||||
普通成功响应使用以下信封:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "OK",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
错误响应仍使用 `code`、`message`、`data`,其中 `data` 为 `null`。稳定业务码包括参数错误
|
||||
`40001`、会话缺失 `40101`、资源不存在 `40401`、revision 冲突 `40901`、上游失败 `50201`
|
||||
和依赖不可用 `50301`。
|
||||
|
||||
### 配置 revision
|
||||
|
||||
会改变路由快照或安全配置的 mutation 使用 `expectedConfigRevision` 做乐观并发控制。客户端应从
|
||||
账号、Client Key 或设置查询结果读取最新 `configRevision`;收到 `40901` 后重新读取,不应盲目重试
|
||||
旧 revision。
|
||||
|
||||
## 2. 健康检查
|
||||
|
||||
| 方法 | 路由 | 鉴权 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET` | `/healthz` | 无 | Core、Store 和后台任务健康时返回 `204`,否则返回 `503` |
|
||||
|
||||
## 3. Responses 与模型目录
|
||||
|
||||
请求正文上限为 16 MiB。
|
||||
|
||||
| 方法 | 路由 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/v1/responses` | OpenAI Responses JSON;`stream=true` 返回 SSE,否则返回完整 JSON |
|
||||
| `GET` | `/v1/responses` | 通过 HTTP Upgrade 建立 Responses WebSocket |
|
||||
| `POST` | `/v1/responses/review` | 使用同一 Responses 合同发起 review 子代理请求 |
|
||||
| `GET` | `/v1/models` | 返回当前 Client Key 所属 Provider 的可用公开模型 |
|
||||
| `GET` | `/v1/models/catalog` | 返回 Codex 客户端使用的模型目录 |
|
||||
| `GET` | `/v1/models/{model_id}/info` | 返回 Codex 客户端使用的单模型信息 |
|
||||
| `GET` | `/v1/models/{model_id}` | 返回 OpenAI 兼容的单模型详情 |
|
||||
|
||||
OpenAI 路径保留客户端 Responses wire 语义;xAI 所需的协议转换只在 xAI Provider 内完成。模型映射
|
||||
是全局精确映射,未命中时模型名原样交给所属 Provider。
|
||||
|
||||
## 4. 管理员认证
|
||||
|
||||
| 方法 | 路由 | 请求 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `POST` | `/api/admin/auth/login` | `{ username?, password }` | 创建管理员会话并设置 Cookie |
|
||||
| `GET` | `/api/admin/auth/status` | 无 | 返回当前 Cookie 是否已认证 |
|
||||
| `POST` | `/api/admin/auth/logout` | 无 | 删除当前会话并清除 Cookie |
|
||||
|
||||
## 5. 账号
|
||||
|
||||
账号 API 使用统一路由,不存在 Provider Instance 或 Provider 专属账号路由。需要 Provider 的请求只接受
|
||||
`provider: "openai" | "xai"`。
|
||||
|
||||
| 方法 | 路由 | 主要 query/body | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET` | `/api/admin/accounts` | `page`、`pageSize`、`provider`、`search`、`status`、排序字段 | 分页查询账号与汇总 |
|
||||
| `GET` | `/api/admin/accounts/detail` | `accountId` | 查询账号详情、额度和本地用量 |
|
||||
| `GET` | `/api/admin/accounts/export` | `accountIds`、`confirm=export_sensitive_accounts` | 显式导出最多 200 个账号的敏感 Provider 文档 |
|
||||
| `POST` | `/api/admin/accounts/import` | `{ provider, expectedConfigRevision, data }` | 导入或按上游身份更新账号 |
|
||||
| `POST` | `/api/admin/accounts/refresh` | `{ accountId, expectedConfigRevision }` | 手工刷新账号 credential |
|
||||
| `POST` | `/api/admin/accounts/rotate` | OpenAI rotation 字段 | 手工替换 OpenAI OAuth token |
|
||||
| `POST` | `/api/admin/accounts/enable` | `{ provider, accountId, expectedConfigRevision }` | 启用账号 |
|
||||
| `POST` | `/api/admin/accounts/disable` | `{ provider, accountId, expectedConfigRevision }` | 禁用账号 |
|
||||
| `POST` | `/api/admin/accounts/delete` | `{ provider, accountIds, expectedConfigRevision }` | 批量删除 1–200 个账号 |
|
||||
| `GET` | `/api/admin/accounts/quota` | `accountId` | 读取当前额度,不强制访问上游 |
|
||||
| `POST` | `/api/admin/accounts/quota/refresh` | `{ accountId }` | 立即刷新该账号额度 |
|
||||
| `GET` | `/api/admin/accounts/models` | `accountId` | 优先读取该 Provider + 套餐的模型 cache,缺失时有限实时拉取 |
|
||||
| `POST` | `/api/admin/accounts/models/refresh` | `{ accountId }` | 强制拉取最新模型并覆盖 cache |
|
||||
| `GET` | `/api/admin/accounts/connection-test` | `accountId`、`modelId` | 通过 SSE 返回实时连接测试事件,不作为业务 Responses 用量记录 |
|
||||
| `POST` | `/api/admin/accounts/oauth/start` | OAuth start 字段 | 创建 OpenAI 或 xAI OAuth flow |
|
||||
| `POST` | `/api/admin/accounts/oauth/complete` | `{ provider, flowId, callbackUrl }` | 消费 OAuth callback 并写入账号 |
|
||||
|
||||
账号列表支持以下稳定值:
|
||||
|
||||
- `provider`: `all`、`openai`、`xai`;
|
||||
- `status`: `active`、`expired`、`quota_exhausted`、`disabled`、`banned`;
|
||||
- `sortBy`: `email`、`status`、`planType`、`usage`、`lastUsedAt`、`expiresAt`;
|
||||
- `sortDirection`: `asc`、`desc`。
|
||||
|
||||
导入的 `data` 必须是 JSON object,内部 schema 由目标 Provider 独占解释:
|
||||
|
||||
- OpenAI 接受 OAuth 账号文档、账号 bundle 和 Agent Identity;
|
||||
- xAI 从单账号 object 或 `accounts` 数组中提取 OAuth token;包装中的代理、并发、优先级等字段不参与认证;
|
||||
- xAI API Key 不是受支持的账号 credential;
|
||||
- 导入仍会通过 Provider 的身份或刷新链路校验,不能只凭文件外形写入账号。
|
||||
|
||||
OpenAI 的 CPR 导出保持 OAuth 账号的既有字段;Agent Identity 账号则输出
|
||||
`authMode: "agentIdentity"`、`agentRuntimeId`、`agentPrivateKey` 和可选的
|
||||
`taskId`,不伪造 OAuth token 或过期时间。一个 `sourceFormat: "cpr"` 文档可以包含两种
|
||||
认证类型,导入时按每个账号的认证模式分别解析。
|
||||
|
||||
```json
|
||||
{
|
||||
"sourceFormat": "cpr",
|
||||
"accounts": [{
|
||||
"id": "acct_...",
|
||||
"accountId": "...",
|
||||
"userId": "...",
|
||||
"authMode": "agentIdentity",
|
||||
"agentRuntimeId": "...",
|
||||
"agentPrivateKey": "...",
|
||||
"taskId": "..."
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
OpenAI rotation 请求字段为:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "openai",
|
||||
"accountId": "acct_...",
|
||||
"expectedConfigRevision": 1,
|
||||
"expectedCredentialRevision": 1,
|
||||
"accessToken": "...",
|
||||
"refreshToken": "..."
|
||||
}
|
||||
```
|
||||
|
||||
OAuth start 使用:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "openai",
|
||||
"expectedConfigRevision": 1,
|
||||
"name": "account name",
|
||||
"accountId": null,
|
||||
"expectedCredentialRevision": null
|
||||
}
|
||||
```
|
||||
|
||||
重新授权已有账号时,`accountId` 与 `expectedCredentialRevision` 必须同时提供。
|
||||
|
||||
## 6. Client Key
|
||||
|
||||
| 方法 | 路由 | 主要 query/body | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET` | `/api/admin/client-keys` | `cursor`、`limit`、`search`、`sortBy`、`sortDirection` | 游标分页查询 |
|
||||
| `POST` | `/api/admin/client-keys/create` | 创建字段 | 创建绑定 Provider 的 Client Key |
|
||||
| `GET` | `/api/admin/client-keys/reveal` | `id` | 显式读取完整明文 Key |
|
||||
| `POST` | `/api/admin/client-keys/update` | 更新字段 | 原子更新名称、Provider 和限额 |
|
||||
| `POST` | `/api/admin/client-keys/enable` | `{ id, expectedConfigRevision }` | 启用 |
|
||||
| `POST` | `/api/admin/client-keys/disable` | `{ id, expectedConfigRevision }` | 禁用 |
|
||||
| `POST` | `/api/admin/client-keys/delete` | `{ id, expectedConfigRevision }` | 删除 |
|
||||
|
||||
创建字段为 `expectedConfigRevision`、`name`、可选 `label`、`providerKind`、`maxConcurrency`、
|
||||
`requestsPerMinute`。更新请求再增加 `id`。创建和 reveal 响应会返回完整明文 Key,调用方必须立即安全保存。
|
||||
|
||||
## 7. 运行设置
|
||||
|
||||
| 方法 | 路由 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/admin/settings` | 读取运行设置与 `configRevision` |
|
||||
| `POST` | `/api/admin/settings/update` | 原子替换全部运行设置 |
|
||||
| `GET` | `/api/admin/settings/admin-api-key` | 只返回管理 API Key 是否存在 |
|
||||
| `POST` | `/api/admin/settings/admin-api-key/delete` | 删除管理 API Key |
|
||||
| `POST` | `/api/admin/settings/admin-api-key/regenerate` | 重新生成并一次性返回完整管理 API Key |
|
||||
|
||||
设置更新字段包括:
|
||||
|
||||
```text
|
||||
expectedConfigRevision
|
||||
modelMappings
|
||||
refreshMarginSeconds
|
||||
refreshConcurrency
|
||||
maxConcurrentPerAccount
|
||||
requestIntervalMs
|
||||
rotationStrategy
|
||||
usageRetentionDays
|
||||
opsEventRetentionDays
|
||||
auditRetentionDays
|
||||
```
|
||||
|
||||
`rotationStrategy` 可取 `smart`、`quota_reset_priority`、`round_robin`、`sticky`。
|
||||
|
||||
## 8. Dashboard、用量与错误
|
||||
|
||||
| 方法 | 路由 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/api/admin/dashboard/summary` | Dashboard 汇总;支持 `kind`、`startTime`、`endTime` |
|
||||
| `GET` | `/api/admin/dashboard/trend` | Dashboard 趋势;`kind=usage|latency|errors` |
|
||||
| `GET` | `/api/admin/usage/records` | 请求记录分页列表 |
|
||||
| `GET` | `/api/admin/usage/records/detail` | 按 `id` 查询请求详情 |
|
||||
| `GET` | `/api/admin/usage/records/summary` | 当前筛选条件的请求汇总 |
|
||||
| `GET` | `/api/admin/usage/insights/overview` | 用量、成本与成功率洞察 |
|
||||
| `GET` | `/api/admin/usage/insights/diagnostics` | 按维度聚合诊断 |
|
||||
| `GET` | `/api/admin/operations/errors` | 运维错误分页列表 |
|
||||
|
||||
用量查询可组合页码/游标、时间范围、Provider、Client Key、账号、模型、route、transport、状态码、
|
||||
request/response/upstream ID、outcome 与搜索文本。诊断 `dimension` 可取 `model`、`account`、
|
||||
`apiKey`、`provider`、`transport`、`failureClass`、`status`。
|
||||
|
||||
## 9. 版本、更新与重启
|
||||
|
||||
| 方法 | 路由 | 主要 query/body | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `GET` | `/api/admin/system/version` | 无 | 当前构建、部署模式和可用更新 |
|
||||
| `GET` | `/api/admin/system/update/detail` | `refresh=true|false` | 读取或强制刷新 Release 详情 |
|
||||
| `GET` | `/api/admin/system/update/events` | 无 | SSE 更新事件流 |
|
||||
| `POST` | `/api/admin/system/update` | 可选 `{ targetVersion }` | 开始在线更新 |
|
||||
| `GET` | `/api/admin/system/update/status` | 无 | 查询当前更新或回滚状态 |
|
||||
| `POST` | `/api/admin/system/rollback` | 无 | 回滚到保留的上一版本 |
|
||||
| `POST` | `/api/admin/system/restart` | 无 | 请求进程重启 |
|
||||
|
||||
在线更新仅在当前部署模式、Release 资产和进程重启能力都满足要求时可用。仓库发版流程见
|
||||
[部署文档](../deploy/README.md) 与根目录 [README](../README.md)。
|
||||
@@ -130,7 +130,7 @@ credential 更新使用 `credential_revision` CAS。认证永久失败、封禁
|
||||
| `admin_audit_events` | 管理 mutation 审计 |
|
||||
| `client_api_keys` | 客户端鉴权、限额与授权范围 |
|
||||
| `runtime_settings` | 全局配置与 `config_revision` |
|
||||
| `provider_accounts` | 账号资料、加密 credential、revision、quota、cooldown |
|
||||
| `provider_accounts` | 账号资料、Provider-owned 明文 credential JSON、revision、quota、cooldown |
|
||||
| `model_requests` | 请求、attempt、计费、交付与恢复事实 |
|
||||
| `ops_events` | 脱敏运行事件 |
|
||||
|
||||
@@ -138,7 +138,8 @@ credential 更新使用 `credential_revision` CAS。认证永久失败、封禁
|
||||
|
||||
- 一个事实只存一次;可表达关系使用真实 FK 与支持索引。
|
||||
- Provider 差异只进入受 schema/version 校验的 JSONB 边界。
|
||||
- secret 只保存加密 envelope;日志、Debug、API 和 audit 不输出 secret。
|
||||
- credential JSON 以 Provider 自己的 schema 明文保存在 `provider_credentials_json`;日志、Debug、API 和 audit
|
||||
不输出其中的 secret,Provider 只在需要发往上游时把值包装为运行时 secret。
|
||||
- stale recovery 只把超时 `running` 请求收敛为 `incomplete`,不重放业务请求。
|
||||
- `ops_events` 当前逐条同步持久化,`occurrence_count=1`;OpsFlush 可禁用,不存在隐藏内存聚合权威。
|
||||
- retention 只删除已满足保留规则的历史事实,不改变运行中请求。
|
||||
|
||||
@@ -19,9 +19,13 @@ description: Codex Proxy RS 仓库开发指南。Use when changing or auditing i
|
||||
- `gateway-api` 只做协议适配;`gateway-core` 编排;Provider 独占 credential、catalog 与 transport;`gateway-store` 实现持久化端口。
|
||||
- Provider 每次调用只选择一个 credential,不隐藏换号、业务 retry 或跨 Provider fallback。
|
||||
- PostgreSQL 是持久化权威;Redis 只保存可恢复协调状态和 OAuth pending flow。
|
||||
- 当前只有 `openai` 与 `xai` 两个 Provider;账号归属 Provider,不存在 Provider Instance 层。
|
||||
- OpenAI 的 OAuth 与 Agent Identity credential 由 `providers/openai` 独占解析;账号导出使用 CPR 文档,Agent Identity
|
||||
只输出运行所需身份材料,不伪造 OAuth 字段。
|
||||
- 测试放在各 package 的 `tests/`。禁止在生产 `src/` 写 test-only 代码。
|
||||
- Vue 使用 `<script setup lang="ts">`、现有基础组件和主题 token。
|
||||
- README 面向使用者,保持简短;长期架构只写入 `docs/architecture.md`。
|
||||
- 完整 HTTP/Admin 路由与敏感导入/导出格式写入 `docs/api.md`。
|
||||
- 不添加兼容 shim、重复状态机、第二套配置或补丁式旁路。
|
||||
|
||||
## 工作流
|
||||
@@ -54,4 +58,5 @@ docker compose -f deploy/compose.yaml config --quiet
|
||||
- 提交前检查 `git status --short`、cached/unstaged diff 和 `git diff --check`。
|
||||
- 提交带 `Co-authored-by: Codex <noreply@openai.com>`。
|
||||
- 发布以 `release/version.yaml` 和带注释的 `vX.Y.Z` tag 为准。
|
||||
- 正式发布使用 `release/publish <version>`,不要手工绕过发布脚本。
|
||||
- 发布完成后核对远端 `main`、tag、Actions、Release asset 与 GHCR。
|
||||
|
||||
@@ -32,19 +32,19 @@
|
||||
- Core 冻结 RuntimeSnapshot、RoutePlan、retry/fallback 与 downstream commit 边界。
|
||||
- Provider 每次 `execute` 只选择一个 credential 并返回 cold canonical stream。
|
||||
- 换号只由 Core 在下游 commit 前按重放安全性决定。
|
||||
- fallback 只允许同 instance 账号和同 Provider kind instance;不跨 Provider kind。
|
||||
- fallback 只允许同一 Provider kind 内的账号;不跨 Provider,也不存在 Provider Instance 层。
|
||||
- OpenAI continuation 为 native → replay owner → replay any;xAI 使用客户端完整历史。
|
||||
- Provider wire profile 以配置为启动基线,并由共享运行时状态统一发布。OpenAI CLI 读取 `@openai/codex`、Desktop 读取官方 appcast、xAI CLI 读取 `@xai-official/grok`;发现新版本后自动更新对应版本字段,所有消费边界不得维护独立常量。
|
||||
- `downstream_committed_at` 是不可撤回交付承诺,不是首字节已经写达的证明。
|
||||
|
||||
## 存储
|
||||
|
||||
- PostgreSQL 只有 `0001_initial.sql` 定义的八张终态业务表。
|
||||
- PostgreSQL 只有 `0001_initial.sql` 定义的七张终态业务表。
|
||||
- `config_revision` 只用于会改变调度快照或安全配置的管理 mutation。
|
||||
- quota、cooldown、catalog generation、自动 refresh 不推进全局 revision。
|
||||
- refresh 只推进账号 `credential_revision`;Redis cooldown 是可丢失热缓存。
|
||||
- OAuth pending 使用 Provider 域隔离 SHA-256 key、固定三字段 Hash 和原子一次消费。
|
||||
- secret 不进入日志、Debug、fixture、文档或 audit details。
|
||||
- 真实 secret 不进入日志、Debug、fixture、文档或 audit details;测试只能使用合成值。
|
||||
|
||||
## 前端
|
||||
|
||||
|
||||
Reference in New Issue
Block a user