Skip to content

fix(fingerprint): update Claude Code fingerprint to v2.1.92 / sdk 0.80.0#2540

Open
0oAstro wants to merge 3 commits intorouter-for-me:devfrom
0oAstro:fix/claude-fingerprint-headers
Open

fix(fingerprint): update Claude Code fingerprint to v2.1.92 / sdk 0.80.0#2540
0oAstro wants to merge 3 commits intorouter-for-me:devfrom
0oAstro:fix/claude-fingerprint-headers

Conversation

@0oAstro
Copy link
Copy Markdown
Contributor

@0oAstro 0oAstro commented Apr 4, 2026

What

Updates the default Claude Code fingerprint from the stale v2.1.63 baseline to the current v2.1.92. All captures are live mitmproxy intercepts (socks5 mode) of actual requests to api.anthropic.com.

Changes

helps/claude_device_profile.go

Field Before After
User-Agent claude-cli/2.1.63 (external, cli) claude-cli/2.1.92 (external, sdk-cli)
@anthropic-ai/sdk 0.74.0 0.80.0
X-Stainless-Runtime-Version v24.3.0 v24.14.0
DefaultClaudeVersion fallback 2.1.63 2.1.92

Note: entrypoint suffix in UA changed from cli to sdk-cli in 2.1.92. parseEntrypointFromUA already handles this correctly — cc_entrypoint in billing header is derived from the incoming client UA, not the default constant.

claude_executor.go

  • Hardcoded "2.1.63" strings in checkSystemInstructions / checkSystemInstructionsWithMode"2.1.92"
  • Added missing beta flags: advanced-tool-use-2025-11-20, effort-2025-11-24
  • Added missing headers: Accept-Language: *, Sec-Fetch-Mode: cors (now sent by Stainless SDK 0.80.0)
  • Fixed accept-encoding order: gzip, deflate, br, zstdbr, gzip, deflate (SDK 0.80.0 dropped zstd, reordered)

helps/cloak_utils.go + helps/user_id_cache.go

metadata.user_id format changed in 2.1.92 from flat string to JSON string:

  • Old: user_[64hex]_account_[UUID]_session_[UUID]
  • New: {"device_id":"[64hex]","account_uuid":"[UUID]","session_id":"[UUID]"}

Added generateFakeUserIDWithSession() so metadata.user_id.session_id stays consistent with X-Claude-Code-Session-Id (both use CachedSessionID(apiKey)).


Live mitmproxy captures (3-way comparison)

Captured via mitmproxy in SOCKS5 mode intercepting api.anthropic.com. Auth redacted.

OLD cliproxy (eceasy/cli-proxy-api, pre-fix)
user-agent:                  claude-cli/2.1.63 (external, cli)
x-stainless-package-version: 0.74.0
x-stainless-runtime-version: v24.3.0
anthropic-beta:              claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05
accept-encoding:             gzip, deflate, br, zstd
accept-language:             [missing]
sec-fetch-mode:              [missing]
x-claude-code-session-id:    [missing]
x-client-request-id:         [missing]

billing: cc_version=2.1.63.922; cc_entrypoint=cli; cch=12b4d;
metadata.user_id: user_394ffa60b807...account_9b7c96bf...session_99de7788  (flat string)
NEW cliproxy (this branch, compiled from source)
user-agent:                  claude-cli/2.1.92 (external, sdk-cli)          ✓ fixed
x-stainless-package-version: 0.80.0                                          ✓ fixed
x-stainless-runtime-version: v24.14.0                                        ✓ fixed
anthropic-beta:              ...advanced-tool-use-2025-11-20,effort-2025-11-24,...  ✓ fixed
accept-encoding:             br, gzip, deflate                               ✓ fixed
accept-language:             *                                                ✓ fixed
sec-fetch-mode:              cors                                             ✓ fixed
x-claude-code-session-id:    002481fa-fb9b-4402-b171-0f6e4f8143e7           ✓ present
x-client-request-id:         0a14c7ea-8f3d-4bd3-b2f0-0404881b1879           ✓ present

billing: cc_version=2.1.92.a35; cc_entrypoint=sdk-cli; cch=3805e;
metadata.user_id: {"device_id":"51b5fc1c...","account_uuid":"...","session_id":"..."}  ✓ fixed
Direct claude-code 2.1.92 (reference / ground truth)
user-agent:                  claude-cli/2.1.92 (external, sdk-cli)
x-stainless-package-version: 0.80.0
x-stainless-runtime-version: v24.14.0
anthropic-beta:              claude-code-20250219,oauth-2025-04-20,context-1m-2025-08-07,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24
accept-encoding:             br, gzip, deflate
accept-language:             *
sec-fetch-mode:              cors
x-claude-code-session-id:    bfe87cb9-68dc-471a-a1e9-068a4ce31a48
x-client-request-id:         00b9b1b0-4c75-413d-9a16-86553adf7d77

billing: cc_version=2.1.92.7f9; cc_entrypoint=sdk-cli; cch=00000;
metadata.user_id: {"device_id":"466d68ff...","account_uuid":"786745f7...","session_id":"bfe87cb9..."}

Verification

  • go build ./... clean
  • go test ./... — only pre-existing TestEnsureQwenSystemMessage_MergeStringSystem failure (already failing on dev branch tip before this change, unrelated to fingerprint code)

Captured via mitmproxy interception of live claude-code requests vs
cliproxy-forwarded requests. Diffs observed:

- UA: claude-cli/2.1.63 (external, cli) -> claude-cli/2.1.92 (external, sdk-cli)
  (entrypoint suffix changed from 'cli' to 'sdk-cli' in newer builds)
- @anthropic-ai/sdk: 0.74.0 -> 0.80.0
- X-Stainless-Runtime-Version: v24.3.0 -> v24.14.0
- DefaultClaudeVersion fallback: 2.1.63 -> 2.1.92
- checkSystemInstructions hardcoded version: 2.1.63 -> 2.1.92

Additional missing headers added (observed in 2.1.92 direct traffic):
- Accept-Language: * (now sent by the Stainless SDK)
- Sec-Fetch-Mode: cors (browser-compat header added in newer builds)

Missing beta flags added to base set:
- advanced-tool-use-2025-11-20
- effort-2025-11-24

Both headers were absent from cliproxy-forwarded requests, creating a
detectable fingerprint delta vs first-party claude-code sessions.

Source: mitmproxy capture comparing cliproxy-forwarded vs direct
claude-code 2.1.92 requests to api.anthropic.com.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Claude executor and device profile to align with Claude Code version 2.1.92 and SDK 0.80.0. Key changes include adding new beta features to the headers, introducing Accept-Language and Sec-Fetch-Mode headers, and updating version-related constants and function calls. The review feedback recommends centralizing the hardcoded version string into a single constant within the helps package to prevent duplication and ensure consistency across multiple files.

Comment on lines +19 to +21
defaultClaudeFingerprintUserAgent = "claude-cli/2.1.92 (external, sdk-cli)"
defaultClaudeFingerprintPackageVersion = "0.80.0"
defaultClaudeFingerprintRuntimeVersion = "v24.14.0"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The version string 2.1.92 is now used in multiple locations across the codebase. It is better to define it as a single constant to ensure consistency and simplify future updates.

Suggested change
defaultClaudeFingerprintUserAgent = "claude-cli/2.1.92 (external, sdk-cli)"
defaultClaudeFingerprintPackageVersion = "0.80.0"
defaultClaudeFingerprintRuntimeVersion = "v24.14.0"
DefaultClaudeCLIVersion = "2.1.92"
defaultClaudeFingerprintUserAgent = "claude-cli/" + DefaultClaudeCLIVersion + " (external, sdk-cli)"
defaultClaudeFingerprintPackageVersion = "0.80.0"
defaultClaudeFingerprintRuntimeVersion = "v24.14.0"

return strconv.Itoa(version.major) + "." + strconv.Itoa(version.minor) + "." + strconv.Itoa(version.patch)
}
return "2.1.63"
return "2.1.92"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the DefaultClaudeCLIVersion constant instead of a hardcoded string to maintain a single source of truth for the version.

Suggested change
return "2.1.92"
return DefaultClaudeCLIVersion


func checkSystemInstructions(payload []byte) []byte {
return checkSystemInstructionsWithSigningMode(payload, false, false, "2.1.63", "", "")
return checkSystemInstructionsWithSigningMode(payload, false, false, "2.1.92", "", "")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the exported DefaultClaudeCLIVersion constant from the helps package to avoid duplication and potential drift during future version bumps.

Suggested change
return checkSystemInstructionsWithSigningMode(payload, false, false, "2.1.92", "", "")
return checkSystemInstructionsWithSigningMode(payload, false, false, helps.DefaultClaudeCLIVersion, "", "")


func checkSystemInstructionsWithMode(payload []byte, strictMode bool) []byte {
return checkSystemInstructionsWithSigningMode(payload, strictMode, false, "2.1.63", "", "")
return checkSystemInstructionsWithSigningMode(payload, strictMode, false, "2.1.92", "", "")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use the exported DefaultClaudeCLIVersion constant from the helps package to maintain consistency with other parts of the executor.

Suggested change
return checkSystemInstructionsWithSigningMode(payload, strictMode, false, "2.1.92", "", "")
return checkSystemInstructionsWithSigningMode(payload, strictMode, false, helps.DefaultClaudeCLIVersion, "", "")

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52e3eeecd0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

defaultClaudeFingerprintUserAgent = "claude-cli/2.1.63 (external, cli)"
defaultClaudeFingerprintPackageVersion = "0.74.0"
defaultClaudeFingerprintRuntimeVersion = "v24.3.0"
defaultClaudeFingerprintUserAgent = "claude-cli/2.1.92 (external, sdk-cli)"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep default billing entrypoint in sync with sdk-cli UA

Changing the default fingerprint UA to claude-cli/2.1.92 (external, sdk-cli) introduces an internal mismatch because billing-header generation still falls back to cc_entrypoint=cli when no Claude UA is present (see checkSystemInstructions*/generateBillingHeader in claude_executor.go). In auto-cloak flows for non-Claude clients and in CountTokens, this means the same outbound request now advertises sdk-cli in User-Agent but cli in x-anthropic-billing-header, which is a detectable fingerprint gap and undermines the purpose of this update.

Useful? React with 👍 / 👎.

0oAstro added 2 commits April 4, 2026 15:36
…g order

Two remaining fingerprint deltas vs claude-code 2.1.92 captured via
mitmproxy after the initial version bump:

metadata.user_id format (2.1.92 changed encoding):
  Old: user_[64hex]_account_[UUID]_session_[UUID]  (flat string)
  New: {"device_id":"[64hex]","account_uuid":"[UUID]","session_id":"[UUID]"}  (JSON string)

  generateFakeUserIDWithSession() added so session_id in metadata
  stays consistent with X-Claude-Code-Session-Id header (both derived
  from CachedSessionID per apiKey).

accept-encoding order (Stainless SDK 0.80.0 changed ordering, dropped zstd):
  Old: gzip, deflate, br, zstd
  New: br, gzip, deflate
Copy link
Copy Markdown
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Thanks for updating the Claude fingerprint baseline and for aligning the billing entrypoint with the resolved outbound UA. I found one blocking regression in the default cloaking path.

Blocking

  • internal/runtime/executor/claude_executor.go:1249 still calls helps.GenerateFakeUserID() when cache-user-id is false or unset.
  • internal/runtime/executor/helps/cloak_utils.go:20 generates metadata.user_id.session_id from a fresh UUID, while internal/runtime/executor/claude_executor.go:893 always sends X-Claude-Code-Session-Id from helps.CachedSessionID(apiKey).
  • Because the non-cached path is still active by default, metadata.user_id.session_id and X-Claude-Code-Session-Id diverge in the common configuration, so the fingerprint delta this PR is meant to remove is still present.

Non-blocking

  • The new tests cover the cache-enabled path and the CountTokens entrypoint fix, but they do not assert the default non-cached metadata.user_id.session_id == X-Claude-Code-Session-Id behavior.

Test plan

  • go test -run 'TestResolveOutboundClaudeEntrypoint_|TestClaudeExecutor_CountTokens_BillingEntrypointMatchesResolvedUserAgent|TestClaudeExecutor_GeneratesNewUserIDByDefault|TestClaudeExecutor_ReusesUserIDAcrossModelsWhenCacheEnabled|TestClaudeExecutor_Execute_SetsCompressedAcceptEncoding' ./internal/runtime/executor
  • go build -o test-output ./cmd/server
  • Added a temporary regression test in a clean PR checkout to assert metadata.user_id.session_id == X-Claude-Code-Session-Id for the default config; it failed on this branch with different UUIDs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants