Repository navigation
v0.5.5 - WebSocket Quota Enforcement, Example Keys Retired, Legacy Portal Passwords Locked, and Codex Tool Bridge Opt-In
v0.5.5 - WebSocket Quota Enforcement, Example Keys Retired, Legacy Portal Passwords Locked, and Codex Tool Bridge Opt-In
Security release. Self-hosted instances should upgrade. Read the Upgrade Notes first: example client keys stop authenticating, and some legacy portal accounts are locked until an admin issues a password.
Highlights
1. Quota limits now apply to Responses WebSocket turns
- What was wrong:
GET /v1/responsesandGET /backend-api/codex/responsesupgrade to the Responses WebSocket. Eachresponse.createturn on that connection reaches the executor and spends upstream quota just like aPOST /v1/responses, but WebSocket turns went through no quota check. - The handshake: it is now refused with the same 429/503 a POST gets when a budget is already exhausted: daily requests, total quota, lifetime, daily and period spending.
- Every turn: each turn passes the full POST checks before it reaches the executor, including RPM, TPM and the concurrency cap. A refused turn gets an error event carrying the same status, error and
X-CliRelay-Quota-*headers, and the connection stays open for the next turn. - Shared logic: POST and WebSocket now use one admission path. POST responses are unchanged byte for byte, and a contract test pins every refusal branch.
- Found while verifying #1045. Thanks to @kta1kri for the report.
2. The model gate covers every accepted WebSocket upgrade
- What was wrong: the model-restriction middleware recognised WebSocket upgrades with a narrower check than the upgrader applies.
- The fix: it now uses the upgrader's own check, shared with the quota middleware.
allowed-models, models disabled in the catalog and channel-group allow lists hold on every accepted upgrade.
3. The example client API keys are no longer active
- What was wrong:
config.example.yamlhas shipped three active client keys (your-api-key-1..3) since v0.1.0. A fresh Docker Compose install copied that file, and the first start imported the keys into the database asapi-key-1..api-key-3. - Template: the
api-keysentries are now a commented-out example. A fresh install ships no client key and rejects client requests until one is created in the panel. - Rejected everywhere: the three example values never import and never authenticate, whether they come from
config.yamlor the database. They get the same401 Invalid API keyas an unknown key. Public usage lookups refuse them as well. - Startup warning: a
security warningat startup lists enabled database keys that still hold an example value. Rows are not deleted automatically. - Thanks to @gaizaor for the lead in #404.
4. Portal accounts still on the legacy backfill password are locked
- What was wrong: from v0.4.13 through v0.5.1, the one-shot API key → end-user backfill gave every account the same initial password from the published source and did not require a change. v0.5.3 fixed that for new databases. Accounts already created were left as they were.
- The fix: a one-time pass at startup locks the accounts from that batch whose password was never changed. They get a random password nobody knows,
must_change_password, and revoked sessions. It costs at most one bcrypt comparison on first boot. - Traffic is unaffected: API key traffic does not depend on the portal password, so requests through these accounts' keys continue. The startup log names every locked account.
5. Codex tool bridge is now opt-in per provider
- What was wrong: every tool-carrying request sent to an OpenAI-compatible upstream had eleven extra tool definitions appended, the Codex Desktop Computer Use functions and
mcp__node_repl__js. This happened whether or not the caller declared them. - The fix: requests now carry exactly the tools the caller declared.
openai-compatibilityandopencode-go-api-keyentries gaincodex-tool-bridge(defaultfalse) for operators who want those definitions for Codex Desktop. Providers without the setting are never bridged. - Thanks to @Android-Login-Analysis for the precise report (#1041).
6. Responses and tool-choice fixes
- Claude → Responses streaming:
response.output_text.done,response.content_part.doneandresponse.output_item.donenow carry the closing block's text. Codex CLI and pi take the reply fromoutput_item.done, so Claude models behind/v1/responsescame back empty. Thanks to @RUIIIOVO for the report and the reference implementation (#1053), credited as co-author. tool_choice: none: it now survives Claude → OpenAI, OpenAI → Claude and Responses → Claude with tools kept. Previously it was forced to, or silently became,auto. The Responses named choice{"type":"function","name":"x"}also reaches Claude with its name. This is step 1 of #1042. Thanks to @Android-Login-Analysis.
Detailed Changes
Backend CliRelay
- fix(quota): apply the POST quota checks to Responses WebSocket turns; the model gate covers every accepted WebSocket upgrade (#1084).
- fix(auth): stop shipping and accepting the example client API keys; refuse them in public usage lookups (#1082).
- fix(enduser): lock portal accounts still holding the legacy backfill password (#1083).
- fix(executor): make the Codex tool bridge a per-provider opt-in (#1081).
- fix(translator): carry block text in Claude → Responses done events (#1079).
- fix(translator): stop
tool_choice"none"from becomingauto; read the Responses named choice (#1080).
Upgrade Notes
-
Example keys: clients still sending
your-api-key-1..3get401after upgrading.- Create a key on the API Keys page of the panel (
/manage). - Move the clients to it.
- Delete the old
api-key-Nentries. The startup warning lists them.
An existing
config.yamlis not rewritten, but the example values in it are never imported or accepted. - Create a key on the API Keys page of the panel (
-
Legacy portal accounts: accounts locked by the one-time pass need a password issued through the end-user password reset before their owners can sign in. Their keys keep working.
- Check
last_login_aton the listed accounts. If anyone other than the owner signed in before the upgrade, rotate that account's keys.
- Check
-
WebSocket quotas: keys with limits that use the Responses WebSocket (for example Codex CLI) are now held to those limits.
- A handshake on an exhausted budget returns
429. - A refused turn gets an error event.
- A handshake on an exhausted budget returns
-
Codex tool bridge: operators who rely on the Computer Use / Browser definitions for Codex Desktop must set
codex-tool-bridge: trueon theopenai-compatibilityoropencode-go-api-keyentries that serve it.- The panel does not know this field yet. Saving those providers from the panel turns it back off, so set it in
config.yamlor through the management APIPATCH.
- The panel does not know this field yet. Saving those providers from the panel turns it back off, so set it in
-
Also worth checking: list your client keys (
GET /v0/management/api-key-entries) and remove any you do not recognise.
中文说明(点击展开)
v0.5.5 - WebSocket 配额生效、示例 Key 失效、遗留门户密码锁定与 Codex 工具桥接改为按需开启
安全更新。 自托管实例请尽快升级。升级前请先阅读下方"升级提示":示例客户端 Key 将无法再鉴权,部分遗留门户账号需要管理员重新发放密码。
核心更新亮点
1. Responses WebSocket 轮次开始执行配额限制
- 问题:
GET /v1/responses与GET /backend-api/codex/responses是 Responses WebSocket 的升级入口。连接里的每一轮response.create都会调用执行器、消耗上游额度,与POST /v1/responses完全一样,但此前 WebSocket 轮次不经过任何配额检查。 - 握手:额度已耗尽时(每日请求数、总额度、累计/每日/周期消费),握手返回与 POST 相同的 429/503。
- 每一轮:调用执行器之前执行完整的 POST 检查(含 RPM、TPM、并发上限)。被拒的轮次收到带相同状态码、错误对象和
X-CliRelay-Quota-*头的错误事件,连接保持可用。 - 同一套逻辑:POST 与 WebSocket 共用同一套准入逻辑,POST 的响应逐字节不变,有契约测试锁定全部拒绝分支。
- 此问题在核查 #1045 时发现,感谢 @kta1kri 的报告。
2. 模型门禁覆盖所有被接受的 WebSocket 升级
- 问题:模型限制中间件判断 WebSocket 升级用的条件比升级器更窄。
- 修复:现在与配额中间件共用升级器的同一判定。
allowed-models、模型目录禁用、渠道分组白名单在每个被接受的升级上都生效。
3. 仓库示例客户端 Key 不再生效
- 问题:
config.example.yaml从 v0.1.0 起带有三个生效的客户端 Key(your-api-key-1..3)。Docker Compose 全新部署会复制该文件,首次启动时把它们导入数据库,命名为api-key-1..api-key-3。 - 模板:
api-keys改为注释掉的示例。全新部署不再带任何客户端 Key,在面板创建 Key 之前拒绝所有客户端请求。 - 一律拒绝:这三个示例值不再导入、也不再能鉴权,无论来自
config.yaml还是数据库,返回与未知 Key 相同的401 Invalid API key。公开用量查询同样拒绝。 - 启动告警:启动时以
security warning列出数据库中仍启用的示例 Key,不会自动删除。 - 感谢 @gaizaor 在 #404 提供的线索。
4. 锁定仍使用遗留 backfill 初始密码的门户账号
- 问题:v0.4.13 至 v0.5.1 期间,API Key → 终端用户的一次性 backfill 给每个账号设置了公开源码中的同一个初始密码,且不强制修改。v0.5.3 修复了新库的情况,已创建的账号没有补救。
- 修复:启动时一次性锁定这一批中从未改过密码的账号:设为无人知晓的随机密码,标记必须改密,并吊销会话。首次启动最多一次 bcrypt 比对。
- 不影响业务调用:API Key 调用不依赖门户密码,这些账号名下 Key 的请求照常可用。启动日志会列出被锁定的账号。
5. Codex 工具桥接改为按渠道显式开启
- 问题:发往 OpenAI 兼容上游、且带工具的请求,此前都会被追加 11 个工具定义(Codex Desktop 的 Computer Use 函数与
mcp__node_repl__js),无论调用方是否声明。 - 修复:现在请求只携带调用方声明的工具。
openai-compatibility与opencode-go-api-key条目新增codex-tool-bridge(默认false),需要为 Codex Desktop 提供这些定义的运营者可按条目开启。没有该配置项的渠道一律不注入。 - 感谢 @Android-Login-Analysis 的详细报告(#1041)。
6. Responses 与工具选择修复
- Claude → Responses 流式:
response.output_text.done、response.content_part.done、response.output_item.done现在携带当前块的文本。Codex CLI 与 pi 以output_item.done为最终回复,此前经/v1/responses调用 Claude 模型会得到空回复。感谢 @RUIIIOVO 的报告与参考实现(#1053),已列为共同作者。 tool_choice: none:在 Claude → OpenAI、OpenAI → Claude、Responses → Claude 上正确保留,且保留工具定义;此前会被强制或静默变成auto。Responses 的具名选择{"type":"function","name":"x"}也能带着名字到达 Claude。这是 #1042 的第一步,感谢 @Android-Login-Analysis。
详细改动记录
后端 CliRelay
- fix(quota):Responses WebSocket 轮次执行与 POST 相同的配额检查;模型门禁覆盖所有被接受的 WebSocket 升级(#1084)。
- fix(auth):不再发放也不再接受示例客户端 Key,公开用量查询同样拒绝(#1082)。
- fix(enduser):锁定仍使用遗留 backfill 初始密码的门户账号(#1083)。
- fix(executor):Codex 工具桥接改为按渠道显式开启(#1081)。
- fix(translator):Claude → Responses 的 done 事件携带块文本(#1079)。
- fix(translator):
tool_choice的"none"不再变成auto,并读取 Responses 具名选择(#1080)。
升级提示
-
示例 Key:仍在使用
your-api-key-1..3的客户端升级后会收到401。- 在面板(
/manage)的 API 密钥页创建新 Key。 - 把客户端切换到新 Key。
- 删除旧的
api-key-N条目,启动告警会列出它们。
已有的
config.yaml不会被改写,但其中的示例值不会被导入或接受。 - 在面板(
-
遗留门户账号:被一次性修复锁定的账号,需要管理员通过终端用户"重置密码"发放凭据,其所有者才能再次登录;名下 Key 照常可用。
- 请检查被列出账号的
last_login_at:如果升级前有非本人登录,请轮换该账号名下的 Key。
- 请检查被列出账号的
-
WebSocket 配额:有限额、且通过 Responses WebSocket 使用的 Key(例如 Codex CLI)现在会受到限额约束。
- 额度耗尽时握手返回
429。 - 被拒的轮次收到错误事件。
- 额度耗尽时握手返回
-
Codex 工具桥接:依赖 Codex Desktop 的 Computer Use / Browser 插件的运营者,需要在对应的
openai-compatibility或opencode-go-api-key条目上设置codex-tool-bridge: true。- 面板暂不识别该字段,在面板保存这些渠道会把它重置为关闭,请通过
config.yaml或管理 APIPATCH设置。
- 面板暂不识别该字段,在面板保存这些渠道会把它重置为关闭,请通过
-
顺带自查:用
GET /v0/management/api-key-entries列出客户端 Key,删除不认识的条目。
Multi-arch binaries and the Docker image are published to GitHub Releases and GHCR. There is no panel release this time; codeProxy v0.5.3 remains current.