Files
codex-proxy-rs/docs/plans/upstream-client-profiles.md
T
Zyy e6587b6ca2 feat(openai): add configurable upstream client identities (#156)
Add six Desktop and CLI presets, global defaults and per-key overrides, custom identity parameters, and official release tracking. Keep the resolved identity stable throughout each request and its retries.
2026-09-18 16:39:29 +08:00

21 KiB
Raw Blame History

上游客户端身份配置实施计划

状态:实现与本地验收完成,真实上游账号验收尚未覆盖。当前实现位于 feat/upstream-client-profiles 独立工作区。 代码核对基线:3328a435。

1. 目标与已确定的规则

为 OpenAI 上游请求提供 macOS、Linux、Windows 与 Desktop、CLI 组合的六套客户端身份预设, 支持自定义,并允许管理员在 Client Key 中单独选择,覆盖通用设置。

配置只保留两层:

Key 有独立配置 → 使用 Key 配置
Key 跟随通用设置 → 使用通用设置
  • 新安装的通用设置默认使用 macOS · Desktop · 自动最新,管理员可以修改。
  • 新建及升级前已有的 Key 默认「跟随通用设置」。
  • 内置默认值只用于初始化通用设置,不参与运行时配置优先级。
  • 修改通用设置后,跟随它的 Key 在新请求中自动生效;独立配置的 Key 保持自己的选择。
  • Key 切回「跟随通用设置」时清除独立覆盖,不保留参与运行时合并的隐藏字段。
  • 独立覆盖以完整配置为单位,不逐字段混合 Key 与通用设置,避免组合出不一致的身份。

本文中的客户端身份指网关发给上游的 User-Agent、originator、平台和版本等信息。 该功能不改变接入客户端的最低版本门禁、客户端安装方式或网关宿主操作系统。

2. 当前实现与可复用入口

现有能力 代码入口 本次需要补齐的部分
启动时的 Desktop 身份配置,默认 macOS OpenAI 配置 将用户选择纳入持久化通用设置,支持客户端类型与平台选择
共享请求画像、Desktop 发布检查及版本元组更新 请求画像 按类型与平台解析版本,支持固定版本以及每请求独立身份
模型、账号及 Desktop 专属请求的头部组装 请求头 按调用场景消费解析后的身份,保留不同接口的协议边界
通用设置管理 管理模型、持久化 保存全局选择,复用事务、审计及配置发布
Key 管理 管理模型、管理用例 保存可空的独立覆盖,并提供实际生效配置的投影
请求策略冻结与发布 运行快照、快照发布 携带配置选择,保持同一请求及重试的一致性
上游连接池已有身份区分与精确续写;模型目录已有缓存隔离 WebSocket、账号目录 复用现有机制,核对新增身份字段是否完整进入隔离维度
设置页及首页身份展示 设置页、身份卡片 增加配置入口,明确全局展示与 Key 实际生效值的区别

目前画像类型包含 Desktop 专属字段,自动更新通过同一 Desktop 制品取得应用版本、Core 版本及构建号。 因此,六套配置需要按官方身份语义扩展,不能仅替换现有 UA 的操作系统名称。

3. 预设、自定义与版本策略

3.1 六套预设

平台 Desktop CLI
macOS macOS Desktop macOS CLI
Linux Linux Desktop Linux CLI
Windows Windows Desktop Windows CLI

平台与客户端类型决定预设;系统版本、CPU 架构及终端信息是预设参数,不再展开成额外的顶级选项。 预设值由后端统一提供,前端不维护另一份默认值和 UA 拼接规则。

实施前逐项核对官方源码或官方制品,记录版本、提交或制品标识、请求格式和更新来源。 尤其需要核实 Linux Desktop 的可用制品及身份依据,不能因为有六个组合就宣称六套均已获得官方验证。 没有可靠官方依据的组合保留自定义配置入口,标明核验状态;不伪造官方发布版本或自动更新来源。

3.2 自定义

通用设置和 Key 独立配置共用一套编辑模型:选择预设后,可以调整客户端标识、系统版本、 架构、终端及适用的版本字段,并预览最终请求身份。

  • 首期将自定义内容保存在对应通用设置或 Key 中;命名模板库、共享模板引用及模板管理不属于本次范围。
  • 后端按客户端类型生成 UA 与相关身份头;不增加任意请求头透传或任意发布源 URL 配置。
  • 校验必填项、字段长度、HTTP 头合法性、控制字符及客户端类型对应的版本格式。
  • Desktop 与 CLI 使用各自适用的字段;CLI 不携带由 Desktop 默认值遗留的应用版本后缀。
  • 自定义值与「官方核验」状态分别展示,用户填写的值不能自行获得官方核验标记。
  • 认证信息、账号 ID、Cookie、请求位置及数据驻留仍由现有职责管理,不纳入身份自定义。

3.3 自动最新与固定版本

模式 行为
自动最新(默认) 跟随该客户端、平台及适用架构对应的最新已核验稳定发布;后台更新,不在业务请求中访问发布源
固定版本 使用保存的版本;后台发布检查不能覆盖固定值

自动最新只更新版本资料,不覆盖用户的平台、架构、终端等选择。手动修改版本时转为固定版本, 避免保存后又被后台任务覆盖。恢复自动最新后,重新采用对应发布源的有效版本。

  • Desktop 应用版本、内嵌 Core 版本和构建号作为同一制品的完整元组更新;不拼接独立 CLI 的最新版本。
  • CLI 使用对应官方 CLI 发布信息,不借用 Desktop 发布号。
  • 不假设不同平台的 Desktop 同步发布或共享构建号。
  • 更新失败保留同一预设上次成功的版本,并展示检查失败及检查时间;不得切到其他平台或客户端。
  • 首次离线启动可使用同一预设随版本附带的已核验版本资料,并标明未完成最新检查。 这些资料属于版本解析器的种子数据,不是第三层用户配置。
  • 没有有效发布源或启动资料的组合不能进入可用的「自动最新」状态;提供原因与固定自定义入口。
  • Redis 中的发布缓存至少区分客户端类型、平台和影响制品选择的架构,不再仅凭 Provider 区分。 缓存失效和重启后的恢复也不得覆盖用户的固定配置。

4. 配置合同与职责

4.1 持久化与管理 API

拟增加的概念字段如下,最终名称在实施时与现有 API 风格统一:

位置 字段 语义
通用设置 openaiClientProfile 必须存在的完整配置选择,包含预设或自定义参数、版本模式
Client Key openaiClientProfileOverride null 表示跟随通用设置;对象表示完整独立配置
只读投影 effectiveOpenaiClientProfile 返回当前解析结果、配置来源、版本来源及检查状态,不作为另一份可写配置

新建 Key 省略覆盖字段等同于 null;更新 Key 省略字段保留已有覆盖,显式 null 才清除。 旧调用方更新通用设置时省略新增字段,应保留现值,不能重置管理员已保存的选择。 通用设置不接受以 null 制造第三层回退的语义。

配置由 PostgreSQL 保存。管理写入沿用现有事务、脱敏审计、revision 和快照发布流程。 官方发布结果继续作为 Provider 的派生缓存,不将每次版本检查写成用户配置或推进全局配置 revision。

职责依照 系统架构:

  • Admin 负责全局与 Key 的管理规则和继承选择,复用同一解析入口生成生效投影。
  • Core 负责请求策略的携带与冻结,不依赖具体 Provider,也不解释 UA、官方制品或请求头。
  • OpenAI Provider 负责预设、官方版本解析、字段语义校验及上游头部生成,通过现有端口风格提供能力。
  • Store 实现配置、迁移与缓存存取,不解释客户端身份的业务规则。
  • API 负责 wire 校验及投影,前端负责编辑与展示,不重复推导最终身份。

4.2 生效范围

本次配置作用于 OpenAI Provider。Key 同时授权其他 Provider 时,不把 OpenAI 身份套用到 xAI。

由 Client Key 发起的 OpenAI 模型请求及其相关目录读取,消费该 Key 的有效身份; HTTP/SSE、WebSocket 以及 Images、Search 等入口按实际调用链逐一核对覆盖。 OAuth 与 API Key 上游认证模式继续共用适用的身份规则,认证字段仍按各自协议生成。

后台令牌刷新、额度查询、账号管理及 Desktop 专属接口没有 Client Key 上下文,不能被某个 Key 的选择污染。 实施时按官方接口要求划分共用身份与 Desktop 专属身份,保留必要的独立请求格式; 全局选为 CLI 时也不能让 Desktop 专属接口意外携带缺失或错误的应用版本。

4.3 请求一致性与资源隔离

本节以接入现有机制为主。当前鉴权已持有 RuntimeSnapshot,重试通过原路由计划传递冻结策略; WebSocket 连接池已将 originator、user-agent 等纳入连接身份,且已有 画像更新后保留精确续写所属连接的测试。 实施沿用这些快照、重试和续写机制,补齐新增配置的数据传递与必要隔离维度。

  • 将通用与 Key 的身份选择接入已有配置快照,沿现有请求上下文传入 Provider, 同一逻辑请求的重试与换号继续使用冻结的选择。
  • 自动发布版本目前属于 Provider 的共享运行时画像,response_headers 在组装头部时读取 self.profile.snapshot(),尚未由 RuntimeSnapshot 冻结。 为满足跨重试也使用同一解析版本的目标,在已有请求生命周期内保存首次解析结果并复用; 这部分与用户配置的快照继承分别验证,不改动重试决策和状态机。
  • 禁止通过临时修改共享 CodexWireProfileState 实现 Key 覆盖;并发 Key 使用独立的不可变请求身份。
  • 同一入站 WebSocket 的每个 response.create 按现有规则获取新策略。 普通连接获取复用现有身份匹配规则;精确续写继续使用持有响应状态的原连接, 保留已有的画像更新例外,不能因版本刷新强制断开或重建这类连接。
  • 检查新增字段是否已被现有连接身份和模型目录缓存键覆盖,仅补齐影响协议结果的缺失维度。 Key 授权边界沿用现有机制;身份切换继续遵循原有续写和重放限制,避免上下文丢失或越界重放。
  • 「当前生效」投影显示解析结果;请求观测使用本次冻结的摘要,不能事后用最新全局值替代。

5. 管理端交互

通用设置

新增「上游客户端身份」配置区,包含平台、客户端类型、预设/自定义、版本策略以及相应字段。 默认展示 macOS Desktop 自动最新;提供最终 UA、相关版本、发布检查状态及字段错误提示。 保存后通过现有设置发布机制对新请求生效,无需重启。

Key 创建与编辑

默认选择「跟随通用设置」,同时展示当前通用配置摘要。 选择「独立配置」后使用与通用设置相同的编辑组件;首次切换可用当前通用值填充表单草稿, 只有保存后才形成独立配置。摘要明确标出「通用设置」或「Key 独立配置」。

现有展示

首页身份卡片标明展示的是通用配置,不将其描述成所有 Key 的唯一身份。 Key 详情展示该 Key 当前有效身份;复用后端投影,不在浏览器拼装 UA 或解析版本。 复用现有基础组件及 管理端主题,提供保存失败、版本检查失败、无可用发布源等状态。

6. 升级与迁移

  • 按 迁移规则 新增编号迁移,更新冻结清单,不修改历史迁移。
  • 新安装在初始化阶段写入通用默认配置;存量 Key 的覆盖初始化为 null。
  • 升级需识别既有 openai.wire_profile 自定义值,首次导入通用配置时保留有效的环境与身份选择。 标准配置映射到 macOS Desktop 自动最新;显式自定义版本映射到固定自定义,并在升级说明交代。
  • SQL 迁移不负责读取部署文件;必要的一次性导入由启动组装流程经正式管理/存储端口完成, 在设置尚未初始化时原子写入。初始化标记避免重启或更新重新覆盖数据库中的设置。
  • 实施前核实配置加载器能否区分显式自定义与默认填充值,必要时在加载阶段保留该来源信息。
  • 初始化完成后,相关用户选择以数据库为唯一权威;清理旧配置的运行时覆盖路径并同步配置样例。 其他仍有效的 Provider 配置字段继续归原 owner 管理。
  • 备份与恢复包含通用配置和 Key 覆盖;发布缓存仍可重建,不成为配置恢复的前置依赖。

7. 实施顺序

  1. 核对官方依据与接口范围。 完成六个组合的来源矩阵,明确可自动更新的组合、 Desktop 专属接口、CLI 格式及旧配置来源识别方式,记录无法核验的项目。
  2. 定义合同并打通持久化。 确定完整配置对象、继承语义及只读投影;实现新增迁移、 一次性初始化、设置与 Key 管理 API,复用审计和快照发布。
  3. 扩展 Provider 解析。 复用现有 Desktop 版本核验,补充 CLI 及平台来源、固定版本、 自定义校验和分维度缓存;确保官方资料与用户配置分别存储。
  4. 接入实际请求链路。 复用现有快照与请求上下文携带有效选择,补齐解析版本的请求级复用, 覆盖相关传输与目录入口;复用连接池身份匹配与精确续写规则,仅补齐必要的隔离维度。
  5. 实现设置与 Key 界面。 复用一套编辑组件和服务端投影,调整首页身份卡片含义, 完成浏览器交互验收。
  6. 验证并同步正式文档。 完成下列矩阵;在行为可用后就地更新 API、架构、部署和用户入口的必要说明, 不提前将本计划中的拟定合同写成已发布能力。

8. 验收条件

范围 必须覆盖的场景与结果
默认与继承 新安装采用约定默认;新旧 Key 跟随;通用修改只影响跟随者;独立配置不受影响;显式清除后恢复继承
API 更新语义 省略覆盖字段保留原值,显式 null 清除;旧设置调用不重置新配置;非法配置不留下部分提交
预设与自定义 六个组合均有明确支持状态;已核验预设与对应官方证据一致;自定义可保存、重启恢复并生成合法头部
版本解析 自动最新按来源更新;固定值不被刷新;Desktop 元组一致;检查失败保留同组合旧版本;无来源状态如实显示
并发与冻结 两个不同配置的 Key 并发请求不串用身份;配置或发布刷新期间,已开始请求及其重试保持一致
上游传输 在可用的真实测试环境检查 HTTP/SSE、WebSocket 及受影响接口发出的身份与结果;API Key 认证不混入 OAuth 字段
缓存与续写 不同有效身份不误用目录结果或连接;配置切换后的新请求应用新身份;旧 continuation 无上下文丢失或越界重放
管理与后台 Key 覆盖不影响其他 Key、后台任务及 Desktop 专属操作;全局 CLI 与混合 Provider 授权场景可用
升级与恢复 标准配置、自定义配置、重复启动、数据库备份恢复和发布缓存缺失均按约定处理;历史迁移校验不变
界面 创建/编辑/清除覆盖、保存与加载失败、自动与固定切换、来源及核验状态正确;检查窄屏、深浅色和键盘操作

后端补充能验证上述真实行为的用例,按 贡献与审查 和 架构验收入口 执行适用的 Rustfmt、Clippy、测试、架构边界与迁移检查。 数据库与 Redis 使用专用测试实例,因环境缺失跳过的测试明确记录为缺口。

前端执行 pnpm --dir frontend format:check、pnpm --dir frontend build,并提供实际浏览器运行证据; 不为本功能引入项目当前不维护的独立前端测试体系。所有变更执行 git diff --check。 构建、单元测试或模拟上游通过不能替代真实协议验收;测试账号和上游环境不足时列明未覆盖组合。

9. 已核对的官方依据与实现边界

  • CLI UA 与 originator:官方 openai/codex 提交 4d205c7a4dc36b719679a0356a45b23133732265, codex-rs/login/src/auth/default_client.rs。CLI 不添加 Desktop 应用后缀。
  • CLI 版本:https://registry.npmjs.org/@openai%2Fcodex/latest,2026-09-18 返回 0.155.0, darwin/linux/win32 × arm64/x64 的依赖版本均配套。实现验证包名、稳定 SemVer 和六个依赖,逐制品缓存。
  • macOS Desktop:沿用官方 appcast 与同一 ZIP 的内嵌 Core 核验路径,支持 arm64。
  • Windows Desktop:分别读取官方 ChatGPT-x64.msix、ChatGPT-arm64.msix,从 app.asar/package.json 获取应用版本和构建号,从同包 codex.exe 核验架构和 Core 版本;MSIX 清单版本不作为应用版本。
  • Linux Desktop:依据官方安装说明,分别读取 amd64/arm64 DEB, 按 XZ 索引跳读 tar,核验同包 app.asar/package.json 与 ELF Core。Windows/Linux 的 Range 请求锁定 ETag, 未变更时只检查一个字节,变更后完整核验版本元组;独立缓存、检查失败保留已核验版本。

身份预览采用独立只读接口,由后端解析全局或所提交的完整覆盖;Key 列表保存并返回覆盖配置, 编辑器按预览结果显示当前生效身份,不在前端生成 UA。

10. 本地验收记录

2026-09-18,独立 PostgreSQL 18、Redis 8 与 127.0.0.1:28080 测试实例,使用合成账号与 Key。

  • RUST_MIN_STACK=16777216 cargo +1.97.0 test --manifest-path backend/Cargo.toml --test main --locked 全部 9 个测试套件通过,合计 2420 项,PostgreSQL/Redis 使用上述专用实例。 覆盖配置继承、首次导入、旧调用保留字段、清除覆盖、跨重试版本冻结、并发请求身份隔离、制品缓存隔离, 以及 Windows/Linux 归档解析、架构不符、损坏/截断、ETag 变化、XZ 跨块跳读和发布回退。
  • cargo +1.97.0 fmt --all --manifest-path backend/Cargo.toml -- --check、严格 Clippy(所有 target/feature)通过。
  • pnpm --dir frontend format:check、pnpm --dir frontend build、git diff --check 通过。
  • backend/migrations/.frozen-sha256 全部校验通过,历史迁移未修改。
  • Chrome 实测通用设置切换并保存 Linux CLI、创建默认继承的 Key、保存 Windows CLI 固定版本覆盖、 重载回显与清除覆盖后恢复继承;检查 1920 px 与 390 px、深浅色、Key 弹窗和键盘关闭下拉框。 新增表单宽度与相邻设置对齐,文案仅保留短标签与必要状态,浏览器无错误或警告。
  • Chrome 实测 Windows/Linux Desktop 自动模式与各自 UA,Windows 保存后重载回显;验收后恢复原 MacOS Desktop 通用配置。 下拉显示 MacOS,内部枚举仍为 macos,UA 的系统类型仍为 Mac OS。
  • 官方发布检查实际成功:macOS Desktop 26.915.31029 / Core 0.155.0-alpha.9,CLI 0.155.0。 Windows 两种架构为 Desktop 26.908.70816 / Core 0.154.0-alpha.6.2 / Build 9275; Linux 两种架构为 Desktop 26.915.31029 / Core 0.155.0-alpha.9 / Build 9771。 四个官方包首次核验耗时约 17~30 秒,重复检查约 67~74 毫秒,未重新扫描制品。
  • cargo audit --file backend/Cargo.lock 未通过:已有固定依赖 rustls 0.23.36 命中 RUSTSEC-2026-0285,另有原有 chacha20、wnaf 撤回警告;新增归档依赖未报告漏洞。 本次未改变受官方 TLS 指纹约束的依赖版本。

验证边界:没有真实上游账号,真实认证下的 HTTP/SSE、WebSocket、Images/Search 未进行端到端验收; 本地传输测试不能替代该验收。Windows/Linux Desktop 的 x86_64、arm64 官方制品解析和未变化复用均已实测,真实账号请求仍未覆盖。 完整备份恢复、生产部署环境及发布源故障下的界面交互尚未实测。