TLS指纹及客户端更新
- 运行时和 SDK 版本更新
- Bun 从
1.4.3升到1.4.4 x-stainless-package-version从0.112.1升到0.128.0- API 请求的 UA 更新为
claude-cli/2.1.303 (external, cli) - SDK 仍报告
x-stainless-runtime-version: v26.3.0 anthropic-version仍为2023-06-01
- TLS 握手变化:签名算法增加,key share 增加
signature_algorithms 从 9 项变为 11 项,增加 ed25519(0x0807)和 ecdsa_secp521r1_sha512(0x0603)。key_share 从 1 项变为 2 项:原来发送 X25519MLKEM768,现在同时发送 X25519MLKEM768 和 X25519。http/1.1 ALPN 的客户端握手,指纹是:- 主请求的 beta 列表增加两个标记,移除一个旧标记
inline-tools-2026-09-15、message-threads-2026-08-12;旧的 mid-conversation-system-2026-04-07 在本次主请求中消失。mid-conversation-system-clear-at-2026-08-21 仍存在,它是另一个标记。anthropic-dispatch-id 消息分发策略标记
v2d:服务端下发的 D 模式开关开启。优先使用它,主对话、子代理、辅助请求都可以带。
v2s:D 开关关闭、S 模式开关开启,且请求属于主对话或子代理。辅助请求不走这个分支。
v2p:失败后的回退值。例如原请求带着v2d遇到 5xx 或连接错误,客户端重试时改用v2p。原先没带这个头、遇到 503,也可能切到它。
- 不携带:两个开关都没开,或请求不满足添加条件。正常低优先级请求也会跳过添加。
tengu_dreamy_frost:控制v2d。开启后优先选择它。
tengu_cedar_lattice:控制v2s。仅在前者关闭时生效,而且辅助请求不使用v2s。
v2d 优先。dreamy_frost:字面是“梦幻霜冻”。
cedar_lattice:字面是“雪松格栅”。
x-claude-code-prompt-id
这个东西很早就有了,这里顺便一起分析了吧~
crypto.randomUUID() 生成 UUID v4,不需要根据 prompt 内容计算。- 新一轮输入:生成新 ID,保存在用户消息和当前请求记录中。
- 工具执行后的续请求、同轮重试:复用当前轮的 ID。
- 子代理:自身消息没有 ID 时,可以继承
parentPromptId。
- 没有关联 prompt 的请求:省略这个头。
prompt.id 和 hook 的 prompt_id 中,用来关联这一轮产生的请求与事件。- Claude Code 使用 API Key 直连默认官方 API:有有效
promptId时,默认发送。
- 设置自定义
ANTHROPIC_BASE_URL:通常取决于tengu_splendid_sutton开关,默认值为false。
- 显式设置
CLAUDE_CODE_GATEWAY_HINT_HEADERS:优先采用这个值,可开启或关闭这组请求头。
x-claude-code-request-class
x-claude-code-request-class 表示本次模型请求的用途/来源。Claude Code 根据内部调用来源和代理上下文选择固定标签,不需要计算哈希或生成随机值。main:主对话。来源以repl_main_thread开头,或者是sdk。工具执行后继续主对话,仍属于main。
subagent:子代理。来源以agent:开头,或者是hook_agent。
workflow:工作流中的子代理。基础分类为subagent,且上下文满足agentType === "subagent"、存在workflowRunId。
compaction:上下文压缩,来源为compact。
auxiliary:其他辅助调用,例如生成会话标题、模型验证、hook_prompt。
side_reply:调用方显式指定的特殊分类。目前定位到的是artifact_patch_edit路径:配置sideCallClass: "side_reply"后使用它,否则默认属于auxiliary。
requestClass 则优先使用;否则按上述规则推导。prompt-id 相同:- 官方 API 默认开启,OAuth 和 API Key 都适用。
- 自定义
ANTHROPIC_BASE_URL通常取决于tengu_splendid_sutton。
- 显式配置
CLAUDE_CODE_GATEWAY_HINT_HEADERS优先于上述判断。
suffix算法
text取内部消息中第一条非isMeta用户消息的首个文本块,没有则用空串。
- 下标
4、7、20从零开始,按 UTF-16 code unit 取值;越界补字符"0"。
- salt 仍为
59cf53e54c78,结果仍是 SHA-256 十六进制的前 3 位。
- 版本号参与哈希,所以相同文本升级后值会变化。例如
hello:280 为790,303 为958。
cch算法
- 保留
cch=00000占位符参与计算。
- 对原始 JSON 字节做归一化:将
model的值置空,排除fallbacks、fallback_credit_token、max_tokens字段。
- 使用 xxHash64,seed 为
0x4D659218E32A3268。
- 取结果的低 20 位,格式化为 5 位小写十六进制,回填 CCH。
x-cc-compaction-request/x-claude-code-compaction
compactionRequestKind,表示“本次请求用于生成上下文压缩摘要,以及压缩的触发类型”:manual:用户手动触发,例如/compact,也包含手动部分压缩。
auto:达到自动压缩阈值,主动进行压缩。
reactive:遇到上下文超限等情况后,触发补救性压缩。
x-cc-compaction-request:通过官方直连判定时发送。
x-claude-code-compaction:开启 gateway hint headers 时发送。官方直连默认开启;自定义网关受CLAUDE_CODE_GATEWAY_HINT_HEADERS和tengu_splendid_sutton控制。
x-cc-atis
Claude Code thinking summaries stopped working due to experiment | Marcos Damasceno posted on the topic | LinkedIn
If your Claude Code thinking summaries went dark this week, you probably did what I did and reread your own settings.json looking for what changed in the latest update, the problem is that nothing really changed regarding thinking summaries. Mine stopped at 9am on the dot this morning according to my traces, which is too round a number for a bug so my software engineer spider senses right away thought, it's probably an experiment, so I had Fable pull apart the 2.1.233 binaries. Turns out there's a server-assigned experiment cached locally, in my case claude_code_ribbon_boulevard_experiment, and the binary reads its atis value and attaches it to every request as an x-cc-atis header. Server sees the token, keeps the signed thinking block for accounting, blanks the readable summary. Drop the assignment from your local config and the summaries come straight back. The setting was on the whole time and it was being sent correctly, I was just allocated into an experiment and there was no way to find that out from inside Claude Code. So I stripped experimentKey/atis from clientDataCacheSlots in ~/.claude.json right away and thinking summaries were back. The only thing that sucks is that it can be fetched again at anytime during a session. So... I kicked off a session in Splice to add flag stripping from Claude Code requests right away. PR will be up soon!
x-cc-atis 不是客户端计算出来的签名/哈希。 它是服务端在 bootstrap 响应
client_data.atis 中下发的不透明 token,客户端按会话锁存后,原样回显到
first-party API 请求头中。客户端不对该值做任何密码学运算。- 作为 GrowthBook 用户属性
atisPin:atisPin: R ? Gdn(R) : void 0;
- 列入 GrowthBook 的
cacheKeyAttributes(["id","organizationUUID","slackTagConnected","atisPin","ccrDerivedSurface"]),用于服务端 remote eval 的实验分桶;
- 与响应头
x-cc-atis-current做一致性比较。
x-claude-code-wiggly-dove
x-claude-code-wiggly-dove。其中的 jy 字段能够携带可读的 host/owner/repo 仓库标识。线上的字典格式
v=1 是整数,yw=jm 是 token,ob="2.1.303" 是字符串,fu=?1 是布尔值,tt=(lr) 是内部列表。相关类型和语法可参考 RFC 9651 的结构化字段类型定义。这里只确认观察到的序列化行为,不将这个自定义实现等同于完整 RFC 实现或官方登记的标准字段。Map,最后用 ", " 连接 key=value。字符串中的双引号与反斜杠被转义,普通字符串的非可打印 ASCII 字符被替换成问号;token 则必须通过对应字符集合检查。重复键会更新值,但保留首次插入的位置。解析时不能简单把整个字符串按逗号切开,因为带引号的成员值可以包含逗号。字段与枚举的完整映射
键 | 类型与典型值 | 从代码可确认的含义 | 条件与边界 |
v | 整数 1 | 格式版本 | 本构建固定为 1。 |
yw | token jm | 固定类型标记 | 源码未给出可据以展开的业务名称。 |
hg | token wm | 客户端类别相关标记 | 普通入口写 wm;非 server tenant 的 sdk-ts/sdk-py 省略。代码还保留 yi 分支,但采样上下文将 tenant 固定为 false。 |
yl | token qi 或 bx | SDK 与其他入口的类别区分 | 非 server tenant 的 sdk-ts/sdk-py 为 bx,其余为 qi。 |
ob | 字符串 "2.1.303" | 客户端版本 | 来自构建常量。 |
sk | 字符串 "wb" 等 | 入口类型 | cli 为 wb;未知且非空的入口回退为 vr;入口为空则省略。 |
rf | 字符串分支 | 预留在构造器中的成员 | 对应局部变量始终为 undefined,当前构造不输出;不宜猜测它的用途。 |
wl | token ki/ny/tn/ng | 请求来源类别 | 主会话与子代理共享 ki;摘要压缩类 ny;特定分类器 tn;其他 ng。 |
tt | 列表 () 或 (lr) | 自动权限策略相关标记 | 不是 permissionMode 的逐值编码;确切逻辑见下一节。 |
bf | token fy/zr/oh/oz/rl/vt | 容器或工具沙箱的环境类别 | 有明确分支优先级,不能仅凭它推断所有限制是否激活。 |
vd | token mz/ia/fs 等 | 宿主环境类别 | Kubernetes 对应 mz,CI/GitHub Actions 对应 ia,普通未分类环境 fs;检测函数的 ax 分支在本构建固定不可达。 |
xf | 字符串 "ph" 等 | 部分环境分支的补充标记 | 普通未分类 Linux 为 ph;构造器还保留其他平台的 yb/nv/zb 分支。本构建采样 platform 固定为 linux。 |
zx | token ii/fz/vy/bm | 网络出口约束 | 姿态未知 ii;未限制 fz;限制且强制白名单 vy;限制但未声明强制白名单 bm。 |
zi | token kb | 网络出口限制的附加标记 | 仅在 egressRestricted 为 true 时输出。 |
fu | 布尔值 ?1 | Computer Use 已绑定 | 采样检查 computer binding 是否存在;未激活时省略。 |
rt | token | Computer Use 分支附带的宿主类别 | 与 fu 一起输出,使用 hostEnvironment 或回退 fs。 |
mo | 内部列表,可能含 vb/ma | 额外环境属性 | 代码分别关联 monorepoCheckout 与 internetReachable;本构建的采样值为 false 和 undefined,通常不输出。 |
jy | 字符串 | 可读的规范化 Git 仓库标识 | 来自 origin,经过校验、规范化和截断;没有哈希。 |
请求类别并不等于工具名称
wl=ki 同时覆盖主请求与子代理,因此它不足以区分两者。compact、agent_summary、away_summary、tool_use_summary_generation 被归入 ny。auto_mode、auto_mode_investigator、compact_fab_check、agent_classifier、subagent_config_classifier 被归入 tn。toolNames,这个构建的 Sw()/Iw() 没有把工具名数组写进该头。仅凭参数名或遗留的扩展变量,不能声称它上传了完整工具清单。展开 sk 入口类型的全部静态映射
入口值 | sk 的字符串值 |
cli | "wb" |
mcp | "sw" |
sdk-cli | "wj" |
sdk-ts | "xu" |
sdk-py | "um" |
bench | "sa" |
claude-vscode | "su" |
claude-code-github-action | "ho" |
local-agent | "zg" |
local_agent | "xb" |
claude-desktop | "kh" |
remote | "zl" |
remote_baku | "xs" |
remote_cowork | "ym" |
remote_trigger | "np" |
remote_cowork_trigger | "om" |
remote_desktop | "pf" |
remote_mobile | "bt" |
remote_projects | "vo" |
claude_in_slack | "iy" |
claude-in-slack | "zz" |
claude-in-teams | "bo" |
claude-desktop-3p | "ql" |
claude-security | "mb" |
ssh-remote | "pd" |
claude-coworker | "bq" |
claude-coworker-terminal | "tg" |
自动构造受三个条件约束
_m() 首先调用 Ma(),要求 provider 是 firstParty,并通过基础 URL 的第一方判断。随后读取缓存型功能开关 tengu_wiggly_dove,默认值为 false。最后通过 Aw()/Dmt() 对配置目标做进一步判断,涉及 OAuth 或账户配置选择的 base URL;内部的第一方假定值可以覆盖部分检查。_f() 只比较 new URL(value).host 是否等于 api.anthropic.com,并未在这个谓词里检查协议。离线运行原函数时,http://api.anthropic.com 也返回 true。因此不应将它描述成“只有通过 HTTPS 验证的官方服务器才能接收”的安全保证。这不等于本文实际向该 HTTP 地址发送过请求,也没有证明整个传输栈不存在其他约束。x-anthropic-billing-header
字段清单:2 个基础字段,11 个条件字段
F 表示 provider === "firstParty" && xi():既在首方 provider 分支,也通过首方地址判定或内部首方假定。字段 | 取值与条件 | 实际含义 |
cc_version | 2.1.303.<3 位 hex>;billing 启用时输出。 | 客户端版本与用户文本采样后缀;不是完整构建 SHA。 |
cc_entrypoint | 宿主 entrypointOnRequests() → CLAUDE_CODE_ENTRYPOINT → unknown,按 ?? 回退。 | 请求来自哪个产品入口;这里不做混淆编码或固定枚举校验。 |
cch | F 或 provider === "vertex" 时写入 00000。 | 供发送链路使用的占位字段;JS 未给出最终 token 算法。 |
cc_workload | MLr() 返回真值时输出;常见值 cron。 | 当前异步执行上下文的工作负载标签。 |
cc_is_subagent | 仅当 agentType === "subagent" && !isMainSession,输出 true。 | 当前是非主会话子代理调用;条件不成立时省略,不写 false。 |
cc_prev_req | F,且匹配 ^req_[A-Za-z0-9_-]{1,36}$。 | 当前历史中最近一个非 API 错误 assistant 的请求 ID。 |
cc_prompt_id | F,且为 8-4-4-4-12 的十六进制 UUID 字符串,大小写均可。 | 触发本次执行的 prompt 标识;子代理可回退到父 prompt ID。 |
cc_turn_origin | F,且匹配 ^[a-z][a-z_]{0,31}$。 | 触发来源,例如人工、SDK、计划任务、其他代理或系统。 |
cc_prompt_index | F,并与 turn index 一起通过校验;整数,范围 0~10,000,000。 | 代码维护的人工 prompt 计数位置。 |
cc_turn_index | F,并与 prompt index 一起通过校验;整数,范围 1~10,000,000。 | 输入触发的执行轮次位置;不是每个 HTTP 请求的递增序号。 |
cc_gateway | 1 或 env;受网关识别与地址排除规则控制。 | 网关路径归因标签;不包含网关地址或令牌内容。 |
cc_project | 第七参数带有效项目对象,且通过严格首方、本地及归因启用条件。 | 形如 claude_proj_01… 的项目归因 ID;不是 Git URL。 |
cc_project_src | 与 cc_project 成对输出;值须在 7 项来源集合中。 | 项目归因参数的来源层级。 |
cc_gateway
1 和 env。构造器不会把网关 URL、域名、认证 token、JWT payload 或 Git 地址拼进该字段。不过,网关识别结果本身确实进入请求体,因而请求接收方可以看到这个归因标签。Ve:pP(e) 实际为 !!e && !e.unpinned。已选且未标记 unpinned 的网关凭据可直接视作已识别。jwt,否则看 ANTHROPIC_AUTH_TOKEN。可去掉 sk-ant-si- 前缀,再按三段 JWT 解析 payload;aud 等于 claude-gateway 或数组包含该值即命中。这个辅助识别函数只解码,不验签,不能把它解释为令牌可信性的完整认证。VBs 比较当前网关 URL 与已记录的 seenAt。相关读取可由响应中的 x-cc-gateway-version 等提示更新;构造 billing 时使用已存在的状态,GPo 本身不发起网络请求。claude.fedstart.com、claude.palantirfedstart.com,不写标签。网关凭据的 hostVerdict 只要存在 vouched 键也排除;判断是键是否存在,不是键值是否为 true。场景 | cc_gateway |
默认首方直连 | 省略 |
只有普通自定义 ANTHROPIC_BASE_URL | 通常省略;自定义 URL 本身不是充分条件 |
gateway provider,存在认可的网关身份 | 1 |
gateway provider,但未被识别,例如 unpinned 且无其他识别信号 | env |
自定义地址加可识别的 gateway audience | 1 |
命中上述地址或 hostVerdict 排除条件 | 省略 |
env 是一个固定分类值,不表示环境变量内容被上传。字段名说明的是客户端归因;服务端会不会据此做账单分类、统计、限流或风控,仅凭这些客户端代码无法确认。