news 2026/9/25 5:55:52

opencodex 管理 API 凭证契约全解:Codex 账户池、OAuth 多账号与 API-Key 池的调用协议

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
opencodex 管理 API 凭证契约全解:Codex 账户池、OAuth 多账号与 API-Key 池的调用协议

【免费下载链接】opencodex

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

项目地址:https://gitcode.com/gh_mirrors/ope/opencodex
点击查看免费下载

本文基于 opencodex 仓库中devlog/_fin/260720_issue180_cli_account_parity/003_management_api_contracts.md的调研结论,系统梳理代理进程暴露的/api/*管理端点:从客户端认证与端口发现、到 Codex(ChatGPT)账户池、通用 OAuth 多账号与 API-Key 池三大家族凭证契约,再到 provider 能力判别矩阵与 CLI 账号命令(list/current/use)的落地要点。读者读完可掌握为 CLI、GUI 或脚本对接 opencodex 管理面所需的全部请求形状、鉴权前提与语义边界。

一、管理面契约的总闸门:认证与跨域防线

所有/api/*管理路由都由代理进程自身提供,并统一穿过两道检查:

  1. 管理鉴权:requireApiAuth(req, config, "management")(见 src/server/index.ts 的调用入口)。即使用户环境不要求 API 认证(loopback 绑定),管理面也要求请求携带合法的 Host 与 Origin。
  2. 来源检查:handleManagementAPI在进入任何业务 handler 之前,先调用isAllowedManagementOrigin(req, config),跨域来源直接返回 403(src/server/management-api.ts):
if (!isAllowedManagementOrigin(req, config)) { return jsonResponse({ error: "cross-origin request blocked" }, 403, req, config); }

同时,所有 POST/PUT/PATCH 请求在 handler 缓冲 body 之前会拒绝超过 2 MiB 的载荷(返回 413request body too large),避免管理面被超大 JSON 拖垮(src/server/management-api.ts)。

  • 错误约定:一律返回 JSON{ "error": string };codex-auth相关端点有时额外附加code/reason字段。
  • 浏览器安全头:管理面响应带X-Frame-Options: DENY与Content-Security-Policy: frame-ancestors 'none'(src/server/auth-cors.ts)。
  • CORS 头:管理面额外放行X-OpenCodex-GUI-Origin、X-OpenCodex-CSRF-Token两个自定义请求头(src/server/auth-cors.ts)。

Origin校验采取“进程推导 origin 精确匹配”策略:无Origin头、与Host推导出的 origin 完全一致、或命中运维配置的corsAllowOrigins列表时才放行——后者专门覆盖 TLS 终结器把进程观测到的http://…变为外部https://…的场景(src/server/auth-cors.ts)。

二、客户端鉴权与端口解析:CLI 连代理的第一公里

2.1 绑定地址决定鉴权强度

isApiAuthRequired(config)的实现极简:绑定地址为 loopback(localhost/127.0.0.1/::1/[::1]之一)时返回false,否则返回true(src/server/auth-cors.ts):

export function isApiAuthRequired(config: Pick<OcxConfig, "hostname">): boolean { return !isLoopbackHostname(config.hostname); }

两种情形下管理面的要求不同:

绑定方式客户端要求
Loopback(默认127.0.0.1)无需 token;但Host必须是 loopback(isLoopbackRequestHost按 hostname 判定信任边界,端口可不同——ssh -L转发同样合法),且不得携带外部Origin(src/server/auth-cors.ts)
非 loopback 绑定必须携带x-opencodex-api-key、Authorization: Bearer或x-api-key三者之一,且值需匹配OPENCODEX_API_AUTH_TOKEN环境变量或config.apiKeys中某一项

这些头是跨数据面与管理面共享的白名单的一部分:STATIC_ALLOWED_REQUEST_HEADERS中明确包含X-OpenCodex-API-Key与X-Api-Key(src/server/auth-cors.ts)。CLI 侧的既有惯例是runningProxyUpdateHeaders()——读取configuredAdminToken()后注入X-OpenCodex-API-Key头(src/oauth/login-cli.ts)。

2.2 端口阶梯(canonical)

findLiveProxy()按固定优先级定位正在运行的代理(src/server/proxy-liveness.ts):

  1. PID 文件:readAlivePid(src/config.ts)——从 pid 文件读取存活进程;
  2. runtime-port.json:readRuntimePort(src/config.ts)——读取运行时记录的真实端口;
  3. /healthz身份探针:isOpencodexHealthz(src/server/proxy-liveness.ts)——对候选端口发起健康检查,确认是 opencodex 自己的/healthz;
  4. 兜底:config.port ?? 10100。

配置目录为OPENCODEX_HOME环境变量指定目录,缺省为~/.opencodex(src/config.ts)。

值得注意的是configuredPort()直接解析_corsOrigin(默认http://localhost:10100),且任何准入检查都不依赖端口号——loopback 谓词只认 hostname(src/server/auth-cors.ts)。

三、Family A —— Codex(ChatGPT)账户池契约

Codex 账户池路由由handleCodexAuthAPI统一处理(src/server/management-api.ts 动态导入 src/codex/auth-api/routes.ts)。其身份规则:

  • 主账户(Codex App 登录)id 恒为__main__(MAIN_CODEX_ACCOUNT_ID,src/codex/main-account.ts);
  • 池账户 id 必须匹配^[a-zA-Z0-9._-]{1,64}$;
  • token 永不离服务端,邮箱一律经maskEmail掩码后展示(src/lib/privacy.ts)。

完整端点契约如下表:

Endpoint形状 / 语义锚点
GET /api/codex-auth/accounts(?refresh=1强制刷新配额){ accounts: CodexAuthAccountDto[] },main-first;DTO 为{ id, email(masked), plan?, logLabel?, isMain, quota: { weeklyPercent?, monthlyPercent?, weeklyResetAt?, monthlyResetAt?, resetCredits?, updatedAt } \| null, needsReauth?, hasCredential };go/free 套餐只暴露月度字段routes.ts
POST /api/codex-auth/accounts手动导入 token;受OPENCODEX_ENABLE_UNVERIFIED_CODEX_IMPORT=1环境变量门控,未开启则 403manual_import_disabledroutes.ts
DELETE /api/codex-auth/accounts?id=恒 200{ ok: true }(不返回 404);清除凭证、配额与线程亲和,若删除的是当前 active 账户则同时清空activeCodexAccountIdsrc/codex/account-lifecycle.ts
GET /api/codex-auth/active{ activeCodexAccountId: string \| null, autoSwitchThreshold: number(默认 80), upstreamFailoverThreshold: number(默认 3) }routes.ts
PUT /api/codex-auth/activebody{ accountId: string \| null }"__main__"表示 Codex App 登录;池 id 必须存在,否则 400 "Account not found";null清除 pin(回落自动选择最低用量,src/codex/routing.ts)。响应{ ok, activeCodexAccountId }。只写配置,不清除内存中的线程亲和——已 pin 的线程在亲和过期前保持原账户(src/codex/routing.ts),即只作用于新线程/新会话routes.ts
PUT /api/codex-auth/auto-switchbody{ threshold: 0-100 }0表示禁用;持久化为autoSwitchThresholdroutes.ts
PUT /api/codex-auth/failoverbody{ threshold: 0-20 }连续上游失败次数阈值,达到后触发故障转移routes.ts
GET /api/codex-auth/quota{ quotas: { [accountId]: StoredAccountQuota } }(不含 token/邮箱)routes.ts
GET /api/codex-auth/reset-credits?accountId={ credits: [{ granted_at, expires_at }], available_count? };400/401/404 状态矩阵routes.ts
POST /api/codex-auth/reset-credits/consumebody{ accountId }成功返回{ code: "reset" }并强制刷新配额;可选operationId提供幂等语义(格式非法返回 400),池配额探测忙碌时返回 503server_busy且带Retry-After: 1routes.ts
POST /api/codex-auth/loginbody{ id? }浏览器 OAuth 流程(服务端拉起浏览器);返回{ ok, flowId, url, instructions? };流程进行中重复发起返回 409routes.ts
POST /api/codex-auth/login/cancel/GET /api/codex-auth/login-status?flowId={ ok, cancelled }/{ status: pending\|done\|error\|expired\|idle, accountId?, email?(masked), error? }routes.ts

实现细节佐证:配额查询直接遍历listAccountQuotas()内存表(routes.ts);reset-credits 消费端具备幂等 operationId 与 503 忙碌语义(routes.ts)。

四、Family B —— 通用 OAuth provider 账户契约

管理面在 src/server/management-api.ts 的 accounts/logout 区块(约 L1427-L1471)处理通用 OAuth 账户。有效 provider 集合由listOAuthProviders()给出:xai、anthropic、kimi、kiro、google-antigravity、cursor、github-copilot;chatgpt通过isPublicOAuthProvider排除在公开列表外(src/oauth/index.ts)。未知 provider 一律 400"unknown oauth provider"。

Endpoint形状 / 语义锚点
GET /api/oauth/providers{ providers: string[] }management-api.ts
POST /api/oauth/loginbody{ provider, addAccount? }addAccount: true强制拉起全新浏览器身份;返回{ url, instructions? };冲突时 409management-api.ts
POST /api/oauth/login/cancel/POST /api/oauth/login/codecancel 返回{ ok, cancelled };粘贴 code:{ provider, input? }→{ ok: true },被拒时 409(不适用于 chatgpt)management-api.ts
GET /api/oauth/status?provider={ loggedIn, email?(masked), source?, error?, done, activeAccountId?, accounts?: [{ id, email?(masked), active, needsReauth?, expiresAt? }] }src/oauth/index.ts
POST /api/oauth/logout?provider=通过 query 参数指定;只移除 ACTIVE 账户,并提升第一个剩余账户为 activesrc/oauth/store.ts
GET /api/oauth/accounts?provider={ activeAccountId: string \| null, accounts: OAuthAccountSummary[] }(掩码展示)management-api.ts
PUT /api/oauth/accounts/activebody{ provider, accountId }{ ok, provider, activeAccountId };缺 id 400、不存在 404 "account not found"。立即生效——getCredential每次请求重读 active 行src/oauth/store.ts
DELETE /api/oauth/accounts?provider=&id={ ok: true };缺 id 400;不存在 404;删除 active 时提升第一个剩余账户src/oauth/store.ts

存储层背景:OAuth token 存在~/.opencodex/auth.json,按 provider 名组织为ProviderAccountSet{ activeAccountId, accounts: [{ id, credential, needsReauth?, addedAt? }] };旧的单凭证形态({ access, refresh, expires, ... })在加载时自动归一化,首次以新形态落盘会先备份auth.json.pre-multiauth,防止降级加载器静默丢弃刷新 token(src/oauth/store.ts)。

五、Family C —— API-Key 池契约

API-Key 池仅对isKeyAuthProvider判定的 provider 开放——即鉴权方式既非 oauth 也非 forward 的已配置 provider(src/providers/api-keys.ts)。池定义在provider.apiKeyPool(src/types.ts),provider.apiKey镜像当前 active 条目。key id 为sha256(key)[:8]前 8 位(src/providers/api-keys.ts);掩码规则maskApiKey:first4****last4,长度 ≤ 8 时整体显示****,${ENV}引用原样展示(src/providers/api-keys.ts)。

Endpoint形状 / 语义锚点
GET /api/providers/keys?name={ activeId: string \| null, keys: [{ id, label?, masked, active, addedAt? }] };非 key provider 返回{ activeId: null, keys: [] };未知 name 返回 404management-api.ts / api-keys.ts
POST /api/providers/keysbody{ name, key, label? }201{ ok, id };新 key立即成为 ACTIVE;同时清空 model/quota 缓存与 key 冷却状态management-api.ts
PUT /api/providers/keys/activebody{ name, id }{ ok, name, activeId };缺 id 400;未知 provider/key 404。立即生效(镜像provider.apiKey)management-api.ts
DELETE /api/providers/keys?name=&id={ ok: true };删除 active 时提升第一个剩余 keymanagement-api.ts

相邻端点(注意区分):

  • GET /api/key-providers:key 登录选择器的元数据;
  • GET/POST/DELETE /api/keys:代理自身的准入 key(admission keys)——POST 返回完整ocx_…key,且只返回一次,用于外部客户端接入代理。

六、Provider 能力判别矩阵

程序化判别依据两个字段:authKind(src/providers/registry.ts)与codexAccountMode(值为"direct" | "pool",见 src/types.ts),二者均通过GET /api/provider-presets暴露(src/providers/derive.ts)。

ProviderauthKind账户能力
openai(内置)forwardCodex 池(默认)或仅主账户(codexAccountMode: "direct")
xaioauth多账户
anthropicoauth多账户
kimioauth多账户——JWTuser_id/sub稳定身份(src/oauth/kimi.ts);并非issue 假设的单槽
kirooauth替换式单槽——凭证中不含 accountId/email(src/oauth/store.ts)
google-antigravityoauth多账户
cursoroauth多账户(JWTsub)
github-copilotoauth多账户
全部authKind: "key"provider(openrouter、groq、google、cerebras、opencode、qwen-* 等)keyapiKeyPool多 key + active 镜像
ollama / vllm / lm-studiolocal无凭证

已知缺口:多账户与替换式单槽的差异无法仅凭 HTTP 判别。SINGLE_SLOT_PROVIDERS常量只覆盖chatgpt(src/oauth/store.ts);kiro 的替换行为来自无身份凭证分支(登录时未提取 accountId/email,src/oauth/store.ts)。因此 CLI 必须硬编码已知的替换式集合(kiro)或向用户展示通用提示。

七、服务端缺失契约与 CLI 落地结论

调研同时记录了五条对 CLI 账号命令有直接影响的“服务端缺口”:

  1. 没有跨 provider 的聚合账户视图——CLI 必须扇出:先GET /api/oauth/providers,再对每个 provider 调GET /api/oauth/accounts,并对每个 key-provider 调GET /api/providers/keys(Codex 池是唯一自带聚合的);
  2. Codex 池没有手动粘贴 code 的登录路径(仅浏览器流程)——openai 的account add保持浏览器流,超出最小命令集范围;
  3. 没有可经 HTTP 推导的多/替换标志(见矩阵缺口);
  4. PUT /api/codex-auth/active不强制立即切换——已 pin 线程保留原账户,CLI 输出必须注明"applies to new sessions/threads";
  5. 可容忍的不对称:DELETE /api/codex-auth/accounts与DELETE /api/keys对未知 id 返回 200;/api/oauth/logout走 query 参数。

最终结论:issue #180 的最小范围(list/current/use)不需要任何新的服务端契约——三大家族已经完整暴露了所需的读取与切换能力。这为 CLI 账号子命令的实现划定了清晰的集成边界:读操作走扇出式聚合,切换操作分别落在PUT /api/codex-auth/active(Codex 池)、PUT /api/oauth/accounts/active(OAuth 多账号)与PUT /api/providers/keys/active(Key 池)之上,并各自遵守上文表格中的鉴权、掩码与幂等语义。

【免费下载链接】opencodex

Universal provider proxy for OpenAI Codex & Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code

项目地址:https://gitcode.com/gh_mirrors/ope/opencodex
点击查看免费下载

相关推荐

上一篇:react-native-elements Switch 组件完全指南:props 详解与跨平台实现原理
下一篇:前端大文件上传终极优化指南:5个高效并发上传与进度控制技巧

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 5:54:53

杭州正规的全屋定制服务商合作实力参考,口碑好的靠谱企业甄选

在杭州改善型住宅市场中&#xff0c;越来越多精装房业主和家居升级需求的家庭&#xff0c;都在寻找知名的全屋定制品牌&#xff0c;希望通过实力强的全屋定制机构解决空间规划、风格搭配和交付协调的问题。面对市场上众多全屋定制机构推荐信息&#xff0c;如何筛选出正规靠谱的…

作者头像 李华
网站建设 2026/9/25 5:50:37

COMSOL仿真魔角光子晶体激光器:能带计算与参数化建模实践

直接进入主题。最近一段时间我密集地用COMSOL做了魔角光子晶体激光器的光学模型&#xff0c;从能带扫描到模式分析再到参数化几何建模&#xff0c;来回折腾了将近三个月&#xff0c;终于把一套相对稳定的仿真流程跑通。这篇文章把我在这个项目里的思路、参数设置、关键操作和踩…

作者头像 李华
网站建设 2026/9/25 5:49:54

用API声明文件搞定VS Code中cocos2d-x Lua补全

简介&#xff1a;面向VSCode下Cocos2d-x Lua项目开发的API提示工具包&#xff0c;专为使用Lua脚本编写游戏逻辑的开发者设计&#xff0c;可有效解决接口繁多、记忆困难、频繁翻阅文档的效率痛点。包内核心为coco2dx_lua_api提示数据&#xff0c;涵盖引擎公开Lua接口&#xff0c…

作者头像 李华
网站建设 2026/9/25 5:45:00

电磁辐射防护工程手册:距离、时间、材质三维度实操指南

简介&#xff1a;本资源是一份面向公众健康科普与工程防护实践的电磁辐射知识手册&#xff0c;适用于电子电气从业者、环境安全管理人员、高校相关专业师生及关注日常辐射防护的普通读者。内容系统梳理电磁辐射的多源性&#xff08;自然、医疗、家电、通信等&#xff09;、三类…

作者头像 李华