From 90ce54329585f3ec7e61638aa3091d1ffbff50d8 Mon Sep 17 00:00:00 2001 From: zyycn Date: Sat, 25 Jul 2026 03:26:18 +0800 Subject: [PATCH] docs: refresh v3 provider and deployment guides Co-authored-by: Codex --- README.md | 66 +++-- deploy/README.md | 113 +++------ deploy/config.example.yaml | 8 +- docs/api.md | 236 ++++++++++++++++++ docs/architecture.md | 5 +- skills/codex-proxy-rs/SKILL.md | 5 + .../codex-proxy-rs/references/repo-guide.md | 6 +- 7 files changed, 331 insertions(+), 108 deletions(-) create mode 100644 docs/api.md diff --git a/README.md b/README.md index 5cf063465..a6c0e94b4 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![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/architecture.md) +[快速开始](#快速开始) · [客户端接入](#客户端接入) · [运维](#运维) · [部署](deploy/README.md) · [接口](docs/api.md) · [架构](docs/architecture.md) @@ -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 \
API 路由 -| 路由 | 用途 | -| --------------------------- | ------------------- | -| `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。
+完整的客户端与管理端路由、鉴权和 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 +``` + +脚本会更新 `release/version.yaml`、创建约定提交与带注释的 `v` 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) diff --git a/deploy/README.md b/deploy/README.md index 932d6771f..b54850da0 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -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`: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`,保持当前 - `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 +``` + +该脚本负责更新 `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___.tar.gz` 和 -`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和 tar -路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为 +Release 必须提供当前 OS/架构的 `codex-proxy-rs___.tar.gz` 与 +`checksums.txt`。服务会在替换前再次查询远端最新版本,校验下载 host、声明大小、SHA-256 和 +归档路径;二进制或静态资源任一替换失败时恢复旧文件。成功后的旧二进制和旧静态目录分别保留为 `*.backup`,管理端 rollback 会交换当前文件与这份备份。更新状态和跨进程锁可在以下位置排查: ```text diff --git a/deploy/config.example.yaml b/deploy/config.example.yaml index b3eca6cdb..3645ecb45 100644 --- a/deploy/config.example.yaml +++ b/deploy/config.example.yaml @@ -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' diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 000000000..60589daaf --- /dev/null +++ b/docs/api.md @@ -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: `。 + +已鉴权的管理请求还必须携带非空 `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)。 diff --git a/docs/architecture.md b/docs/architecture.md index 46713a627..3a10dfaa1 100644 --- a/docs/architecture.md +++ b/docs/architecture.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 只删除已满足保留规则的历史事实,不改变运行中请求。 diff --git a/skills/codex-proxy-rs/SKILL.md b/skills/codex-proxy-rs/SKILL.md index 83343c642..523af6584 100644 --- a/skills/codex-proxy-rs/SKILL.md +++ b/skills/codex-proxy-rs/SKILL.md @@ -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 使用 `