本文件保留修改前的审查证据。当前本地修复与最终验证见 本地修复结果,服务器未更新。
日期:2026-09-14。范围:当前本地源码的默认配置、应用/切换/还原、普通及批量保存、HTTP/WSS 转发和前端展示。
结论:目前主要问题是保护配置没有在所有状态和转发路径上保持一致,不是缺少更多策略参数。已通过本地真实 WSS 回显和配置服务测试复现多个缺陷。优先修复身份应用、切换原子性和配置保存,再开展策略效果对比。
本次只增加专项报告和隔离复现材料,没有修改业务代码或线上账号。没有验证真实上游模型质量、风控或封号效果。
| 字段/层级 | 实际含义 | 需要注意 |
|---|---|---|
extra.anti_degradation |
新保护开关,DTO/界面主要按它判断 | 不是运行时逐项验收结果 |
extra.anti_degrade |
策略标记、并发上限、应用时间和还原快照 | 只有 mode1 使用正式 policy_version;不同路径的解析规则不统一 |
protection_scope / protection_mode |
能力范围与策略名称 | 两者不能代替设备身份、TLS 和语义检查的实际状态 |
codex_fingerprint_mode |
device、session、full 或 off |
不改变账号上游权限或模型能力 |
codex_fingerprint_seed |
系统管理的持久随机种子 | 正常启停会保留;新建/复制会重新生成;不应显示或导出为诊断信息 |
| TLS 账号配置 | 开关、内置模板或自定义模板 ID | 还受全局开关及插件路由影响 |
concurrency + marker 中的 max_concurrency |
当前并发及策略保护上限 | 保存更大并发可能被重新压回策略上限;16/8/4 是工程配置,不是上游证明的安全值 |
创建入口在 backend/internal/repository/account_repo.go:140 统一调用 PrepareNewAccountProtection:
generic_v1,仅持久限制并发,不具备 mode1 同等身份和语义守卫。以下是注册表声明的目标值,不能据此认定所有运行路径已实现。
| 策略 | 身份策略 | TLS 目标 | 并发上限 | mode1 语义检查 |
|---|---|---|---|---|
| 原生基线 | 恢复应用前配置 | 恢复应用前配置 | 恢复快照 | 不因“原生基线”额外开启 |
最小兼容 minimal_compat |
device | standard | 8 | 无 |
会话兼容 session_standard |
session | standard | 8 | 无 |
初代兼容 legacy |
session | nodejs24 | 16 | 无 |
兼容架构 v3 mode1 |
device | standard | 16,保留较低原值 | 有,主要在 Responses HTTP/WS |
完整收敛 mode2 |
full | nodejs22 | 8 | 无 |
Node.js 24 对照 tls_node24 |
device | nodejs24 | 8 | 无 |
低并发稳定 low_concurrency |
session | standard | 4 | 无 |
通用保护 generic |
保留原身份配置 | 保留原传输配置 | 16,保留较低原值 | 无 |
device 固定设备标识;session 将会话 ID 收敛为账号固定值,按客户端 session 派生 thread,客户端不提供 session 时 thread 会回退到固定 session;full 将会话和线程都收敛为固定值。因此 session/full 是诊断变量,不应理解为“数值更高、保护更强”。
“关闭”调用的是还原快照。如果应用保护前就已手动配置指纹或 TLS,关闭后仍可能启用这些手动配置。再次打开开关使用默认策略,并不记住上次选择的 legacy/mode2。模式一与其他策略的设备 ID 派生前缀也不同,即使种子不变,切换模式也可能改变最终设备 ID;这会影响对照实验的控制变量。
位置:backend/internal/service/openai_ws_v2_passthrough_adapter.go:765、:1032;openai_codex_fingerprint.go:37、:57;openai_ws_forwarder_payload.go:148。
原生 WS 透传路径只调用按账号隔离元数据的 applyCodexAccountIdentityClientMetadataRaw,没有像 HTTP 路径一样解析和共享策略指纹 IDs。头部构造器却会在 mode1 下另外生成固定设备 ID。
实际本地 WSS 复现:
6e9606b6-1807-4ada-bea7-80417e796b8a,回显请求体的安装 ID 是 3c8fb157-5088-4dea-a666-5be6d685c4ca,二者不相同。这些仅为本地测试生成的标识。response.completed,现有 mode1 语义守卫不比较这些身份字段,因而不能发现该问题。影响:同一配置在 HTTP 和原生 WSS 下的身份行为不同。“HTTP 正常、WS 异常”可能与此有关,但本次没有证明真实上游因此拒绝请求或降低输出质量。
解决:HTTP、HTTP→WS、原生 WS 池和透传路径使用同一策略解析结果;在生成首帧/握手头前计算稳定身份,首帧和后续帧都应用对应元数据转换。明确连接级稳定字段与每轮字段的生命周期,不通过修改 TLS 参数掩盖身份不一致。
验收:对每种策略抓取本地真实 HTTP/WSS 的头和帧体,校验设备/会话/线程规则一致;覆盖首帧、后续帧、重连、不同 API Key、账号切换及客户端缺省身份字段。
位置:backend/internal/service/account_anti_degrade.go:302,尤其第 317 行附近。
当前执行顺序是“提交还原旧策略 → 再读账号 → 再提交新策略”。这两个更新各自提交和刷新调度快照,并没有包在同一事务中,也没有策略版本条件更新。
本地在第二次更新注入失败,真实保护服务返回错误后,重新读取账号得到 AntiDegradationEnabled() == false。前端失败分支仅显示错误,不重新加载账号,因此还可能继续显示旧策略。
影响:数据库错误、取消或并发操作可能让账号停留在原生配置;即使最终切换成功,中间提交也可能被并发请求看见。代码注释中的“安全切换”不能证明事务原子性。
解决:在单个事务内锁定账号、验证目标资格、从原始快照计算目标配置,最后一次写入并提交;增加策略 revision/CAS 校验,处理并发启停。调度 outbox 与配置在同一事务提交,缓存只发布最终状态。前端失败后重新读取权威状态。
验收:在校验、写入和提交处注入失败,账号都保留原策略及完整快照;并发应用/关闭要么有序完成,要么返回明确冲突,不能产生隐式关闭窗口。
位置:backend/internal/repository/account_protection.go:31、:71;account_repo.go:576、:595。
最终保存阶段先根据“传入的旧账号对象”是否包含保护字段,决定要不要读取数据库中的保护状态。若旧请求在首次启用前读到 extra=nil 或没有任何保护键,accountMayHaveManagedProtection 返回 false,直接跳过数据库保护合并,之后整对象保存仍执行 SetExtra(extra)。
本地 SQL mock 边界测试已复现:数据库准备了已启用 mode1 的记录,传入无保护字段的快照后,合并函数不查询已提交状态,输出仍未启用;同样的旧快照如果显式含 anti_degradation:false,则会正确读取并保留当前保护。这解释了为什么现有 stale-write 用例通过仍会漏掉此场景。
解决:是否保护必须依据锁定后的数据库记录,不能依据传入对象。将当前保护字段合并进已有行锁查询,或者全部更新统一读取受管状态;身份种子也应在同一最终边界保留。
验收:真实 PostgreSQL 中,让凭据刷新/编辑先读到无标记旧行,再启用保护,最后释放迟到写入;保护、身份和并发均不得回退。本次验证了合并函数缺口,没有执行真实数据库竞态验收。
位置:backend/internal/service/account_protection.go:120;account_mode1_protection.go:53;backend/internal/repository/account_protection.go:15。
已复现的 P1:开启 mode2 后,通过普通 UpdateAccount 提交 codex_fingerprint_mode=off、enable_tls_fingerprint=false,保存成功;账号继续显示保护开启和 mode2,但指纹模式实际已变为 off。ProtectionManagedKeys 排除了 mode2,配置有效性校验也只针对 mode1。
另一个有明确源码证据的 P2:单账号 Go 合并会保护新增的最小兼容/会话兼容等策略,但用于 UpdateExtra/BulkUpdate 的 SQL 只为带版本标记的策略及 legacy 保留完整受管字段,漏掉 mode2 和新增诊断策略。批量保存可能保留策略名却覆盖其底层参数。本次未连接 PostgreSQL执行该 SQL 分支,不能把这部分称为数据库实测。
解决:所有策略从同一注册表声明受管字段和不变量;普通保存、key 合并、批量更新和后台刷新执行一致规则。对受管字段修改返回明确错误或明确回显拒绝项,避免“保存成功”但实际值不一致。
验收:对所有策略 × 所有写入入口测试,策略标记与身份/TLS/并发始终一致;mode2 不得只剩一个开启徽标。
位置:backend/internal/service/account_anti_degrade.go:348、:443、:453。
两种已复现的路径:
同一个通用还原分支也服务 generic 等非 mode1 策略,因此风险不限于 UI 中的某一个按钮。对照测试显示 mode1 自身完整快照的 TLS、模板和并发还原可以通过。
解决:统一使用完整、带字段存在信息的快照,区分“原来没有键”和“原来存在且为 false/null”。还原所有被该策略接管的字段;没有接管或没有快照的字段不能无条件删除。切换与普通关闭复用同一纯配置变换函数。
验收:每种策略从 TLS 开/关、自定义模板、内置模板和无模板状态应用再还原,除了系统管理种子/审计时间外,相关配置逐字段恢复。
位置:backend/internal/service/account_anti_degrade.go:38、:135;account_mode1_protection.go:175。
generic 是系统真实保存的模式,却不在 normalizeAntiDegradeMode 认可的策略中,因此读取时变成 mode1。随机代理 OpenAI 账号默认得到 generic;之后改成固定代理,再选择 mode1,会收到 MODE1_NOT_ELIGIBLE / 当前模式一配置无效。
这不是账号真的损坏,而是枚举与归一化规则不一致。已通过真实 AdminService 配置流程复现。现在绕行需要先还原/关闭再重新启用,但该操作涉及前述还原与切换缺陷,不适合作为长期方案。
解决:把 generic 作为合法持久态保留;区分默认参数与持久状态解析;对“generic → mode1”进行资格校验后原子迁移,不把未知状态伪装成 mode1。
位置:backend/internal/service/anti_degrade_strategies.go:18、account_anti_degrade.go:163、:302;前端 EditAccountModal.vue:3951。
注册表的 requires_openai_oauth、diagnostic_only 和风险等级没有形成完整的后端执行约束;前端只按 apply_supported 展示按钮,没有过滤平台。已复现 Gemini API Key 账号成功应用声明为 OpenAI 专属的最小兼容策略,实际仅并发变化,却保存为对应策略名。
另外,未知模式如 mode_typo 会被静默改为 mode1并应用,已复现。
解决:同一资格判断服务同时供预览和应用使用,按平台、类型、影子身份、代理条件返回明确可用性;未知非空模式返回 400。诊断策略明确展示实际改变的身份维度及风险,不让未支持的平台挂上同名能力标签。
位置:backend/internal/service/openai_plugin_transport.go:18、openai_ws_pool.go:864、openai_ws_v2_passthrough_adapter.go:878;预览服务没有注入全局配置/插件状态。
已复现:legacy 预览返回 tls_profile=nodejs24,全局 TLS 指纹关闭时,真正发送请求却走标准传输。全局关闭生效本身符合设计;不足之处是预览只说明账号目标配置,没有显示它被全局开关屏蔽。插件路由也可能接管 HTTP 传输,不能从账号模板字段推断插件实际握手。
解决:返回 configured(账号配置)、effective(按全局开关/插件计算的计划)、observed(最近实际传输证据,可为空)三种状态,并标明原因。没有握手证据就显示“未验证”,不要把模板名当作观测结果。
MODE1_CONFIGURATION_INVALID、MODE1_LOSSY_TRANSFORM、上游 401/429 等错误。只有先确定失败层,策略对照才有意义。第一批:统一 HTTP/WSS 的身份应用;将应用/还原/切换改成单事务;取消依赖旧快照字段的保护跳过优化。
第二批:统一所有策略及所有保存入口的受管字段规则,修复 TLS 快照还原、generic 解析和资格检查。
第三批:界面展示实际能力及全局覆盖原因,简化默认选项,把诊断策略单独标识,补充测试执行快照和跨协议验收。
建议把策略解析为一个明确的执行计划,包含:策略 ID/版本、身份模式、会话隔离规则、TLS 计划、有效并发、语义守卫范围。保存时验证一次不变量,实际出站各路径消费同一计划,避免再增加散落的 if mode1 特判。
verification/account-protection-review/,包含源码、Go overlay、说明和 service 原始日志。