Hermes Decision Trace

Claude Code Provider 指纹调整复盘:JUN 8335 恢复与 x666 8334 边界

JUN 8335 的 channel:client_restricted 不是单纯 User-Agent 问题,而是缺少 Claude Code / Stainless 客户端指纹;补齐 x-stainless-*、收敛 anthropic-beta、固定 Accept: application/json 后,non-stream、stream、tool_calls 均恢复 200。x666 8334 即使本地 adapter 补齐同样指纹仍返回 Go-http-client/1.1,说明被拦的是 x666 服务端出站 client,不是本机请求。

🧭
推荐路径

继续把 JUN 8335 作为可用 Claude Code-shaped provider;x666 8334 保留 adapter 与最新指纹补丁,但不接入 Hermes 正式 provider,直到 x666 后端出站层改掉 Go client 指纹或更换 channel。

🔎
关键依据

同一 key、同一模型下,JUN 单换 UA 全 403,但加完整 Stainless headers 后 /v1/messages/v1/chat/completions 均 200;x666 本地 adapter 加同样 headers 后,健康检查和模型列表 200,真实推理仍四路 403,错误仍显示 检测到:Go-http-client/1.1

🛠️
落地方式

已修改 /home/ht/code/jun_claude_code_shim.py/home/ht/code/x666_claude_code_adapter.py,并重启对应 systemd user service。JUN 已验证可用;x666 已验证仍不可用,作为上游后端问题留档。

证据摘要

  • 同一 key、同一模型下,JUN 单换 UA 全 403,但加完整 Stainless headers 后 /v1/messages/v1/chat/completions 均 200;x666 本地 adapter 加同样 headers 后,健康检查和模型列表 200,真实推理仍四路 403,错误仍显示 检测到:Go-http-client/1.1

行动清单

按需继续推进。

边界 / 风险

风险 / 边界

正文未抽取到明确风险;上线前仍需确认权限、回退路径与运行态影响。

完整记录

Claude Code Provider 指纹调整复盘:JUN 8335 恢复与 x666 8334 边界

背景

2026-06-15,JUN 8335 shim 开始返回:

Request failed: Forbidden This channel does not allow the current client Detected: claude-cli/2.1.74 (external, cli) code: channel:client_restricted

初始判断容易落到“换 UA”上。但实测显示,JUN 当前策略不是只看 User-Agent,而是看一组更接近 Claude Code Node SDK 的客户端指纹。

同一轮排障还回看了 x666 8334:此前 x666 无论直连、本地 adapter、Axon channel 都返回 channel:client_restricted,且错误显示 Go-http-client/1.1。这次用 JUN 恢复经验再次补齐 x666 adapter headers,确认问题仍在 x666 后端出站层。

当前组件状态

JUN 8335

  • service:jun-claude-code-shim.service
  • script:/home/ht/code/jun_claude_code_shim.py
  • local base_url:http://127.0.0.1:8335/v1
  • upstream:https://muyuan.do/v1/messages
  • inbound key:local-test
  • models:
  • claude-opus-4-8
  • claude-opus-4-6
  • claude-sonnet-4-6

x666 8334

  • service:x666-claude-code-adapter.service
  • script:/home/ht/code/x666_claude_code_adapter.py
  • local base_url:http://127.0.0.1:8334/v1
  • upstream:https://x666.me/v1/messages
  • inbound key:local-test
  • models:
  • claude-opus-4-8-cc
  • claude-sonnet-4-6-cc

探测过程

第一轮:只换 User-Agent,JUN 全部失败

https://muyuan.do/v1/messages 分别测试:

  • claude-cli/2.1.176 (external, cli)
  • claude-cli/2.1.176
  • claude-cli/2.1.74 (external, cli)
  • claude-cli/2.1.45 (external, cli)
  • claude-code/1.0.0
  • Claude-Code/2.1.176
  • anthropic-python/0.64.0
  • anthropic-typescript/0.60.0
  • curl/8.5.0
  • node
  • browser UA

结果:全部 403 或 Cloudflare 拦截。结论:单换 UA 没用。

第二轮:OpenAI endpoint 也不靠单 UA 恢复

https://muyuan.do/v1/chat/completions 做同样 UA 探测,结果仍全 403。结论:不是 /messages 单 endpoint 特有,JUN 当前 channel 对 client 指纹做更完整校验。

第三轮:补齐真实 Claude Code / Stainless headers 后 JUN 通过

参考公开抓包线索与实测,加入:

User-Agent: claude-cli/2.1.74 (external, cli) x-app: cli anthropic-beta: interleaved-thinking-2025-05-14,oauth-2025-04-20 x-stainless-lang: js x-stainless-package-version: 0.75.0 x-stainless-os: Linux x-stainless-arch: x64 x-stainless-runtime: node x-stainless-runtime-version: v24.3.0 x-stainless-retry-count: 0 x-stainless-timeout: 600 Accept: application/json Accept-Encoding: gzip, deflate

JUN /v1/messages 返回 200;JUN /v1/chat/completions 也返回 200。

第四轮:同样经验应用到 x666,仍失败

将 x666 adapter 改成同样 header 指纹,并加 gzip/deflate 解压。重启 x666-claude-code-adapter.service 后验证:

  • /health:200
  • /v1/models:200
  • claude-sonnet-4-6-cc non-stream:403
  • claude-opus-4-8-cc non-stream:403
  • stream:403
  • tool call:403

错误保持:

channel:client_restricted 该渠道不允许当前客户端使用(检测到:Go-http-client/1.1)

这个错误文本说明,被上游检测到的不是本机 adapter 的 Python/Claude headers,而是 x666 服务端出站请求的 Go client。

已落地修改

JUN shim

文件:/home/ht/code/jun_claude_code_shim.py

主要修改:

  1. 默认 anthropic-beta 从多 beta 收敛为:

```text

interleaved-thinking-2025-05-14,oauth-2025-04-20

```

  1. 增加 x-stainless-* headers。
  2. Accept 固定为 application/json
  3. Accept-Encoding 固定为 gzip, deflate
  4. 增加 gzip/deflate 解压,避免压缩响应进入 JSON 解析时乱码。
  5. systemd unit 固化:

```ini

Environment="JUN_CC_USER_AGENT=claude-cli/2.1.74 (external, cli)"

```

验证结果:

non_stream HTTP 200 content=ok stream HTTP 200 SSE 正常返回 tool_call HTTP 200 返回 tool_calls,参数 {"a":1,"b":2}

x666 adapter

文件:/home/ht/code/x666_claude_code_adapter.py

主要修改:

  1. 默认 UA 改为:

```text

claude-cli/2.1.74 (external, cli)

```

  1. 增加 anthropic-betax-stainless-* headers。
  2. Accept 固定为 application/json
  3. Accept-Encoding 固定为 gzip, deflate
  4. 增加 gzip/deflate 解压。

验证结果:

/health HTTP 200 /v1/models HTTP 200 non-stream HTTP 403 Go-http-client/1.1 stream HTTP 403 Go-http-client/1.1 tool_call HTTP 403 Go-http-client/1.1

判断

JUN 的根因

JUN 的 403 是本机 shim 发出的 client 指纹不完整。上游能看到并使用我们发出的 header,所以补齐 Stainless 指纹即可恢复。

对应链路:

Hermes / OpenAI SDK -> 127.0.0.1:8335 shim -> https://muyuan.do/v1/messages -> 上游识别 shim 出站 headers

x666 的根因

x666 的 403 不是本机 adapter headers 问题。即使本机 adapter 已发送完整 Claude Code / Stainless 指纹,上游错误仍显示 Go-http-client/1.1,说明 x666 服务端再转发到真实上游时使用了 Go 默认 client 或 Go-like 指纹。

对应链路:

Hermes / OpenAI SDK -> 127.0.0.1:8334 adapter -> https://x666.me -> x666 后端出站到真实 Claude channel -> 被识别为 Go-http-client/1.1 并拒绝

本机侧不能改变 x666 后端出站请求的 TLS/HTTP/UA 指纹,因此不能靠 Hermes adapter 解决。

后续建议

短期

  1. JUN 8335 继续作为可用 Claude Code-shaped provider 使用。
  2. x666 8334 保留本地 adapter,但不加入 Hermes 正式 provider。
  3. provider 验证不能只看 /v1/models/health,必须跑:
  • non-stream
  • stream
  • tool_calls
  • 至少两个模型

x666 若要恢复,需要上游后端改造

x666 服务端出站层至少需要:

User-Agent: claude-cli/2.1.74 (external, cli) x-app: cli anthropic-beta: interleaved-thinking-2025-05-14,oauth-2025-04-20 x-stainless-lang: js x-stainless-package-version: 0.75.0 x-stainless-os: Linux x-stainless-arch: x64 x-stainless-runtime: node x-stainless-runtime-version: v24.3.0 x-stainless-retry-count: 0 x-stainless-timeout: 600 Accept: application/json Accept-Encoding: gzip, deflate

如果仍失败,说明上游还检查 TLS / HTTP2 / header order / transport 指纹。那时 Go 里改 headers 不够,需要:

  • Node/undici sidecar;或
  • curl-impersonate / browser-like transport;或
  • 真 Claude Code sidecar;或
  • 直接换允许 server-to-server 调用的 channel。

Smoke Test 命令

JUN 8335

curl -sS http://127.0.0.1:8335/v1/chat/completions \ -H 'Authorization: Bearer local-test' \ -H 'Content-Type: application/json' \ -d '{"model":"claude-sonnet-4-6","messages":[{"role":"user","content":"只回复ok"}],"max_tokens":16}'

预期:HTTP 200,content 为 ok

x666 8334

curl -sS http://127.0.0.1:8334/v1/chat/completions \ -H 'Authorization: Bearer local-test' \ -H 'Content-Type: application/json' \ -d '{"model":"claude-sonnet-4-6-cc","messages":[{"role":"user","content":"只回复ok"}],"max_tokens":16}'

当前预期:HTTP 403,错误包含 Go-http-client/1.1。若未来变为 200,说明 x666 后端已修复或 channel 已更换。

经验规则

  1. channel:client_restricted 不要先假设是 key 或模型名问题,要看错误里的 detected client。
  2. 如果 detected 是本机发出的 UA,说明本机 adapter 还有机会修。
  3. 如果 detected 是后端出站 client,如 Go-http-client/1.1,而本机已发送不同 UA,则本机 adapter 无法直接修。
  4. /v1/models 200 只能证明模型目录可见,不证明推理链路可用。
  5. Claude Code-shaped provider 的最小验收是:non-stream、stream、tool_calls 三条都 200。
  6. headers 恢复后要处理压缩响应,否则 gzip/deflate 可能让 JSON 解析失败。