Skip to content

feat: add opt-in experimental Claude cch signing#2473

Merged
luispater merged 2 commits intorouter-for-me:mainfrom
edlsh:feat/experimental-claude-cch-signing
Apr 2, 2026
Merged

feat: add opt-in experimental Claude cch signing#2473
luispater merged 2 commits intorouter-for-me:mainfrom
edlsh:feat/experimental-claude-cch-signing

Conversation

@edlsh
Copy link
Copy Markdown
Contributor

@edlsh edlsh commented Apr 1, 2026

Summary

  • add an opt-in experimental-cch-signing Claude credential flag
  • keep legacy behavior unchanged by default so seed changes upstream do not break existing CLIProxyAPI users
  • sign the final outbound /v1/messages body with seeded xxHash64 only when the flag is enabled
  • scope replacement to the billing header so user content containing cch=00000 is left untouched

Motivation

This makes it possible to experiment with a Claude Code-style cch signing path without turning it into a hard dependency for every Anthropic request.

The safe default is deliberate: if Anthropic changes the seed or signing scheme, CLIProxyAPI continues to operate exactly as it does today unless an operator explicitly opts in.

Reference

Original implementation idea and reverse-engineering discussion:

Tests

  • go test ./internal/config
  • go test ./internal/runtime/executor -run 'TestClaudeExecutor_ExperimentalCCHSigning|TestCheckSystemInstructionsWithMode_|TestApplyClaudeHeaders_DisableDeviceProfileStabilization|TestClaudeExecutor_ReusesUserIDAcrossModelsWhenCacheEnabled|TestClaudeExecutor_ExecuteStream_AcceptEncodingOverrideCannotBypassIdentity|TestDecodeResponseBody_MagicByteZstdNoHeader'

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: 37249339ac

ℹ️ 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".

Comment on lines 1256 to 1259
if cloakMode == "" {
cloakMode = attrMode
strictMode = attrStrict
}
if len(sensitiveWords) == 0 {
sensitiveWords = attrWords
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 Preserve ClaudeKey strict/word settings when mode is omitted

When a claude-key has cloak with no mode (documented default auto), this branch now treats the empty mode as “use auth attributes” and unconditionally assigns strictMode = attrStrict and sensitiveWords = attrWords. In common deployments those auth attributes are absent, so configured strict-mode: true or sensitive-words from config.yaml are silently dropped unless mode is explicitly set, which regresses previous behavior that preserved configured strict/sensitive settings while only filling missing values.

Useful? React with 👍 / 👎.

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 introduces an experimental feature for signing Claude /v1/messages request bodies using a CCH signing algorithm, which is disabled by default. The changes include adding the xxHash dependency, updating the configuration schema to include experimental-cch-signing, and implementing the signing logic within the Claude executor. Additionally, the PR refactors configuration resolution and adds comprehensive tests for the new signing behavior and zstd response decoding. Feedback was provided regarding an opportunity to optimize performance by reducing redundant configuration lookups during the request lifecycle.

Comment on lines +1241 to 1245
useExperimentalCCHSigning := experimentalCCHSigningEnabled(cfg, auth)

// Get cloak config from ClaudeKey configuration

cloakCfg := resolveClaudeKeyCloakConfig(cfg, auth)
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

There's an opportunity to improve efficiency here. The resolveClaudeKeyConfig function is called multiple times for a single request:

  1. Once via experimentalCCHSigningEnabled inside applyCloaking.
  2. Once via resolveClaudeKeyCloakConfig inside applyCloaking.
  3. Again via experimentalCCHSigningEnabled in the calling functions (Execute and ExecuteStream).

This results in iterating over the ClaudeKey configurations three times per request.

Consider resolving the ClaudeKey configuration once in Execute and ExecuteStream and then passing the resolved config down to applyCloaking. This would avoid the redundant lookups.

For example, you could modify Execute and ExecuteStream like this:

// In Execute() and ExecuteStream()
claudeKeyCfg := resolveClaudeKeyConfig(e.cfg, auth)

// ...
// Pass the resolved config to applyCloaking
body = applyCloaking(ctx, claudeKeyCfg, auth, body, baseModel, apiKey)
// ...

if claudeKeyCfg != nil && claudeKeyCfg.ExperimentalCCHSigning {
    bodyForUpstream = signAnthropicMessagesBody(bodyForUpstream)
}

And then update applyCloaking to accept *config.ClaudeKey instead of *config.Config and use it directly:

func applyCloaking(ctx context.Context, claudeKeyCfg *config.ClaudeKey, auth *cliproxyauth.Auth, payload []byte, model string, apiKey string) []byte {
    var useExperimentalCCHSigning bool
    var cloakCfg *config.CloakConfig
    if claudeKeyCfg != nil {
        useExperimentalCCHSigning = claudeKeyCfg.ExperimentalCCHSigning
        cloakCfg = claudeKeyCfg.Cloak
    }
    
    // ... rest of the function using cloakCfg and useExperimentalCCHSigning
}

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: 15c2f274ea

ℹ️ 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".

}

cch := fmt.Sprintf("%05x", xxHash64.Checksum(body, claudeCCHSeed)&0xFFFFF)
return claudeBillingHeaderPlaceholderPattern.ReplaceAll(body, []byte("${1}"+cch+"${3}"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict cch substitution to the injected system header

signAnthropicMessagesBody applies ReplaceAll over the entire serialized request body, so any user content that happens to contain a full x-anthropic-billing-header: ... cch=00000; string will also be rewritten. This mutates user prompts and can change model behavior in otherwise valid inputs; the replacement should be scoped to the injected billing header field (for example, system[0].text) rather than every regex match in raw JSON.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@xkonjin xkonjin left a comment

Choose a reason for hiding this comment

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

Review: Experimental Claude CCH Signing

This is a clean implementation of opt-in CCH signing for Claude Code compatibility. The feature flag approach is the right call for experimental features.

Strengths:

  • Clean separation with new claude_signing.go file
  • Excellent test coverage with the new test cases
  • The xxHash64 implementation with the seed constant matches the documented algorithm
  • Config opt-in prevents breaking existing deployments

Security considerations:

  1. The regex pattern is anchored to cch=00000 - confirm this is always the placeholder value from generateBillingHeader()

  2. Potential bug: In generateBillingHeader(), when experimentalCCHSigning is true, you return early with cch=00000;, but the signing happens after cloaking in applyCloaking(). The order matters - verify the hash is computed on the fully-final body.

Code quality:

  • The resolveClaudeKeyConfig refactor is good - consolidates the lookup logic
  • Consider adding a comment explaining the seed value origin

Test gap: No test for the streaming path (ExecuteStream) with CCH signing enabled - the logic is duplicated there and should be verified.

Approving with minor notes.

@luispater luispater merged commit d09dd4d into router-for-me:main Apr 2, 2026
2 checks passed
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.

3 participants