Documentation
¶
Overview ¶
Claude thinking signature validation.
Spec reference: SIGNATURE-CHANNEL-SPEC.md
Encoding detection (Spec section 3)
Claude signatures use base64 encoding in one or two layers. The raw string's first character determines the encoding depth. This is mathematically equivalent to the spec's "decode first, check byte" approach:
- E prefix: single-layer, payload[0] == 0x12, first 6 bits = 000100, base64 index 4 = E.
- R prefix: double-layer, inner[0] == E (0x45), first 6 bits = 010001, base64 index 17 = R.
Valid signatures can be normalized to R-form (double-layer base64) before sending to the Antigravity backend.
Protobuf structure (Spec sections 4.1 and 4.2) in strict mode only ¶
After base64 decoding to raw bytes, the first byte must be 0x12:
Top-level protobuf |- Field 2 (bytes): container -> extractClaudeBytesField(payload, 2) | |- Field 1 (bytes): channel block -> extractClaudeBytesField(container, 1) | | |- Field 1 (varint): channel_id [required] -> routing_class (11 | 12) | | |- Field 2 (varint): infra [optional] -> infrastructure_class (aws=1 | google=2) | | |- Field 3 (varint): version=2 -> skipped | | |- Field 5 (bytes): ECDSA sig -> skipped, per Spec section 11 | | |- Field 6 (bytes): model_text [optional] -> schema_features | | `- Field 7 (varint): unknown [optional] -> schema_features | |- Field 2 (bytes): nonce 12B -> skipped | |- Field 3 (bytes): session 12B -> skipped | |- Field 4 (bytes): SHA-384 48B -> skipped | `- Field 5 (bytes): metadata -> skipped, per Spec section 11 `- Field 3 (varint): =1 -> skipped
Output dimensions (Spec section 8)
routing_class: routing_class_11 | routing_class_12 | unknown infrastructure_class: infra_default (absent) | infra_aws (1) | infra_google (2) | infra_unknown schema_features: compact_schema (len 70-72, no f6/f7) | extended_model_tagged_schema (f6 exists) | unknown legacy_route_hint: only for ch=11, legacy_default_group | legacy_aws_group | legacy_vertex_direct/proxy
Compatibility ¶
Verified against all confirmed spec samples (Anthropic Max 20x, Azure, Vertex, Bedrock) and legacy ch=11 signatures. Both single-layer (E) and double-layer (R) encodings are supported. Historical cache-mode modelGroup# prefixes are stripped.
Gemini thought signature validation notes.
The Antigravity Gemini request translator can preserve provider-compatible Gemini thought signatures and uses the skip sentinel only for synthetic or incompatible model parts.
Gemini 3 and later models can return thoughtSignature on model content parts. Function-call parts are the strict case: when a model functionCall is replayed with a following functionResponse, Gemini validates that the original functionCall part still carries its provider-issued thoughtSignature. Text or other non-functionCall parts may also carry a signature; those should be preserved when replaying native Gemini history, but they are not the primary validation gate.
Synthetic history and migration from other model families are different. If a functionCall part was not produced by Gemini API, there is no real signature to preserve. Gemini documents two bypass sentinels for that case:
- "skip_thought_signature_validator"
- "context_engineering_is_the_way_to_go"
This repo currently emits "skip_thought_signature_validator" for non-Claude Antigravity Gemini model parts that contain functionCall, thought, or an existing thoughtSignature. That is a request-shape compatibility policy, not a proof that the replaced signature was malformed.
This validator is intentionally more conservative than a decrypting verifier. Claude has a known E/R base64 envelope and a protobuf tree in this package. Gemini thought signatures are opaque provider state here, so local validation checks only the transport-level protobuf envelope and leaves the wrapped provider payload uninterpreted.
Validation tiers:
- Sentinel tier: accept the documented bypass sentinels only when the model functionCall is synthetic, migrated, or otherwise not traceable to a prior Gemini model response in the same conversation.
- Opaque-shape tier: for real Gemini signatures, require a non-empty string, bounded length, successful standard base64 decoding, and a known protobuf envelope when the caller needs provider compatibility. Observed samples currently include Gemini 3.x field-2 -> field-1 payloads and Gemini 2.5 repeated field-1 payloads. Base64 UUID payloads are classified separately and should be replaced with the bypass sentinel rather than replayed.
- Replay tier: real validation means preserving the exact model part that came from Gemini, including its thoughtSignature, id/name/function args, part index, and ordering relative to sibling parallel function calls.
- Tool pairing tier: functionResponse parts must match the preceding functionCall id/name and must not be interleaved between parallel calls. The valid shape is all model functionCalls first, then their responses.
- Compatibility tier: GPT-compatible Gemini traffic stores the same state under tool_calls[].extra_content.google.thought_signature. If that path is translated back to native Gemini, the value must stay attached to the same assistant tool call.
Important non-goals:
- Do not treat a Gemini thoughtSignature as a Claude signature. Similar base64 prefixes are not provenance.
- Do not attach a signature to user functionResponse/tool-result parts.
- Do not log complete signatures during validation failures; log only field paths, lengths, and redacted prefixes.
- Do not preserve client-provided signatures across model/provider/session boundaries unless the request pipeline can prove they came from the same Gemini conversation state.
Index ¶
- Constants
- func CompatibleAntigravityClaudeThinkingSignature(rawSignature string) (string, bool)
- func CompatibleSignatureForProvider(targetProvider SignatureProvider, rawSignature string) (string, bool)
- func CompatibleSignatureForProviderBlock(targetProvider SignatureProvider, rawSignature string, ...) (string, bool)
- func GeminiReplaySignatureOrBypass(rawSignature string, blockKind SignatureBlockKind) string
- func HasClaudeThinkingSignaturePrefix(rawSignature string) bool
- func HasDecodableClaudeThinkingSignature(rawSignature string) bool
- func IsGeminiThoughtSignatureBypass(rawSignature string) bool
- func IsSignatureCompatibleWithProvider(targetProvider SignatureProvider, rawSignature string) bool
- func IsValidClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) bool
- func IsValidGPTReasoningSignature(rawSignature string) bool
- func IsValidGeminiThoughtSignature(rawSignature string, opts ...GeminiThoughtSignatureValidationOptions) bool
- func NormalizeClaudeProviderNativeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error)
- func NormalizeClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error)
- func SanitizeGeminiRequestThoughtSignatures(payload []byte, contentsPath string) []byte
- func SignaturePayloadWithoutProviderPrefix(rawSignature string) string
- func StripInvalidClaudeThinkingBlocks(payload []byte, opts ...ClaudeSignatureValidationOptions) []byte
- func StripInvalidClaudeThinkingBlocksAndEmptyMessages(payload []byte, opts ...ClaudeSignatureValidationOptions) []byte
- func ValidateClaudeThinkingSignatures(inputRawJSON []byte, opts ...ClaudeSignatureValidationOptions) error
- func ValidateGeminiFunctionCallPairing(inputRawJSON []byte) error
- func ValidateGeminiThoughtSignatures(inputRawJSON []byte, opts ...GeminiThoughtSignatureValidationOptions) error
- type ClaudeMessagesSignatureSanitizeOptions
- type ClaudeSignatureTree
- type ClaudeSignatureValidationOptions
- type GPTReasoningSignatureInfo
- type GeminiThoughtSignatureEnvelope
- type GeminiThoughtSignatureInfo
- type GeminiThoughtSignatureValidationOptions
- type SignatureBlockKind
- type SignatureCompatibilityAction
- type SignatureCompatibilityDecision
- type SignatureProvider
- func DetectSignatureProvider(rawSignature string) SignatureProvider
- func DetectSignatureProviderForBlock(rawSignature string, blockKind SignatureBlockKind) SignatureProvider
- func SignatureProviderFromCachePrefix(prefix string) SignatureProvider
- func SignatureProviderFromModelName(modelName string) SignatureProvider
- func SplitSignatureProviderPrefix(rawSignature string) (SignatureProvider, string, bool)
- type SignatureSanitizeReport
- func SanitizeClaudeMessagesForClaudeUpstream(payload []byte, targetModel string) ([]byte, SignatureSanitizeReport)
- func SanitizeClaudeMessagesSignaturesForModel(payload []byte, targetModel string) ([]byte, SignatureSanitizeReport)
- func SanitizeClaudeMessagesSignaturesForTarget(payload []byte, opts ClaudeMessagesSignatureSanitizeOptions) ([]byte, SignatureSanitizeReport)
Constants ¶
const ( MaxGeminiThoughtSignatureLen = 32 * 1024 * 1024 GeminiSkipThoughtSignatureValidator = "skip_thought_signature_validator" GeminiContextEngineeringBypass = "context_engineering_is_the_way_to_go" )
const MaxClaudeThinkingSignatureLen = 32 * 1024 * 1024
const MaxGPTReasoningSignatureLen = 32 * 1024 * 1024
Variables ¶
This section is empty.
Functions ¶
func CompatibleAntigravityClaudeThinkingSignature ¶ added in v7.1.30
CompatibleAntigravityClaudeThinkingSignature returns the double-layer R-form required by Antigravity Claude replay. It only accepts signatures that are strictly identifiable as Claude, so Gemini E-prefixed envelopes cannot slip through the looser Antigravity bypass normalization path.
func CompatibleSignatureForProvider ¶
func CompatibleSignatureForProvider(targetProvider SignatureProvider, rawSignature string) (string, bool)
CompatibleSignatureForProvider returns a replayable provider-native signature for targetProvider. It strips this repo's provider prefix and normalizes Claude signatures to the format expected by the target when possible.
func CompatibleSignatureForProviderBlock ¶
func CompatibleSignatureForProviderBlock(targetProvider SignatureProvider, rawSignature string, blockKind SignatureBlockKind) (string, bool)
CompatibleSignatureForProviderBlock returns a replayable provider-native signature for targetProvider when the source block kind is known.
func GeminiReplaySignatureOrBypass ¶ added in v7.1.30
func GeminiReplaySignatureOrBypass(rawSignature string, blockKind SignatureBlockKind) string
GeminiReplaySignatureOrBypass returns a Gemini-replayable thoughtSignature. Compatible Gemini signatures are normalized and preserved. Missing, unknown, or cross-provider signatures are replaced with Gemini's bypass sentinel.
func HasClaudeThinkingSignaturePrefix ¶
HasClaudeThinkingSignaturePrefix reports whether rawSignature has the Claude E/R signature prefix after stripping an optional cache prefix.
func HasDecodableClaudeThinkingSignature ¶
HasDecodableClaudeThinkingSignature reports whether rawSignature has the Claude E/R shape and its expected base64 layer(s) can be decoded.
func IsGeminiThoughtSignatureBypass ¶
IsGeminiThoughtSignatureBypass reports whether rawSignature is one of Gemini's documented bypass sentinels for synthetic or migrated function-call history.
func IsSignatureCompatibleWithProvider ¶
func IsSignatureCompatibleWithProvider(targetProvider SignatureProvider, rawSignature string) bool
func IsValidClaudeThinkingSignature ¶
func IsValidClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) bool
IsValidClaudeThinkingSignature returns whether rawSignature is a valid Claude thinking signature under the requested validation options.
func IsValidGeminiThoughtSignature ¶
func IsValidGeminiThoughtSignature(rawSignature string, opts ...GeminiThoughtSignatureValidationOptions) bool
IsValidGeminiThoughtSignature returns whether rawSignature has a valid local Gemini thought-signature shape under opts.
func NormalizeClaudeProviderNativeThinkingSignature ¶ added in v7.1.30
func NormalizeClaudeProviderNativeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error)
NormalizeClaudeProviderNativeThinkingSignature strips any cache prefix, validates the signature, and returns the single-layer E-form expected by Claude-native providers.
func NormalizeClaudeThinkingSignature ¶
func NormalizeClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error)
NormalizeClaudeThinkingSignature strips any cache prefix, validates the signature, and returns the double-layer R-form expected by Antigravity bypass mode.
func SanitizeGeminiRequestThoughtSignatures ¶ added in v7.1.30
SanitizeGeminiRequestThoughtSignatures applies Gemini replay policy to a Gemini-shaped request. Model-turn functionCall, thought, and signed parts keep compatible Gemini signatures and use the bypass sentinel otherwise. User-turn functionResponse parts must not carry thoughtSignature fields.
func SignaturePayloadWithoutProviderPrefix ¶
SignaturePayloadWithoutProviderPrefix strips this repo's provider cache prefix when present. The returned string is the value that should be replayed to an upstream provider.
func StripInvalidClaudeThinkingBlocks ¶
func StripInvalidClaudeThinkingBlocks(payload []byte, opts ...ClaudeSignatureValidationOptions) []byte
StripInvalidClaudeThinkingBlocks removes Claude thinking blocks whose signatures are empty or not valid Claude thinking signatures after stripping an optional cache prefix, unless the validation options allow an empty thinking placeholder.
func StripInvalidClaudeThinkingBlocksAndEmptyMessages ¶
func StripInvalidClaudeThinkingBlocksAndEmptyMessages(payload []byte, opts ...ClaudeSignatureValidationOptions) []byte
StripInvalidClaudeThinkingBlocksAndEmptyMessages also removes messages whose content becomes empty after invalid thinking blocks are removed.
func ValidateClaudeThinkingSignatures ¶
func ValidateClaudeThinkingSignatures(inputRawJSON []byte, opts ...ClaudeSignatureValidationOptions) error
ValidateClaudeThinkingSignatures validates every thinking block signature in a Claude messages payload.
func ValidateGeminiFunctionCallPairing ¶
ValidateGeminiFunctionCallPairing validates the replay shape around Gemini functionCall and functionResponse parts. It checks id/name pairing and prevents response parts from being interleaved inside the same content as function calls. It allows a final pending functionCall group because callers may validate a freshly returned model step before tool outputs exist.
func ValidateGeminiThoughtSignatures ¶
func ValidateGeminiThoughtSignatures(inputRawJSON []byte, opts ...GeminiThoughtSignatureValidationOptions) error
ValidateGeminiThoughtSignatures validates thoughtSignature fields in a Gemini native payload. Function-call parts must have a valid signature. Other parts are optional, but if a thoughtSignature field is present it must be valid.
Types ¶
type ClaudeMessagesSignatureSanitizeOptions ¶
type ClaudeMessagesSignatureSanitizeOptions struct {
TargetProvider SignatureProvider
TargetModel string
DropEmptyMessages bool
DropToolSignatures bool
DropEmptyThinkingPlaceholders bool
}
type ClaudeSignatureTree ¶
type ClaudeSignatureTree struct {
EncodingLayers int
ChannelID uint64
Field2 *uint64
RoutingClass string
InfrastructureClass string
SchemaFeatures string
ModelText string
LegacyRouteHint string
HasField7 bool
}
ClaudeSignatureTree describes the protobuf fields currently used for Claude thinking signature routing.
func InspectClaudeDoubleLayerSignature ¶
func InspectClaudeDoubleLayerSignature(sig string) (*ClaudeSignatureTree, error)
InspectClaudeDoubleLayerSignature decodes and inspects a double-layer Claude thinking signature.
func InspectClaudeSignaturePayload ¶
func InspectClaudeSignaturePayload(payload []byte, encodingLayers int) (*ClaudeSignatureTree, error)
InspectClaudeSignaturePayload inspects the decoded Claude thinking signature protobuf payload.
func InspectClaudeSingleLayerSignature ¶
func InspectClaudeSingleLayerSignature(sig string) (*ClaudeSignatureTree, error)
InspectClaudeSingleLayerSignature decodes and inspects a single-layer Claude thinking signature.
type ClaudeSignatureValidationOptions ¶
type ClaudeSignatureValidationOptions struct {
// PrefixOnly only checks for an optional cache prefix followed by an E/R
// Claude signature prefix. Use it to preserve legacy shallow cleanup.
PrefixOnly bool
// Base64Only checks the optional cache prefix, E/R Claude signature prefix,
// and base64 layers without validating the decoded Claude marker or protobuf
// tree. Use it for conservative request cleanup.
Base64Only bool
// AllowEmptySignatureWithEmptyText preserves empty thinking placeholders with
// no signature and no thinking/text payload during strip operations.
AllowEmptySignatureWithEmptyText bool
Strict bool
}
ClaudeSignatureValidationOptions controls how far Claude thinking signatures are inspected. The base validation always checks the cache prefix, base64 layers, and decoded 0x12 Claude payload marker. Strict mode additionally verifies the known protobuf tree used by Claude thinking signatures.
type GPTReasoningSignatureInfo ¶
func InspectGPTReasoningSignature ¶
func InspectGPTReasoningSignature(rawSignature string) (*GPTReasoningSignatureInfo, error)
InspectGPTReasoningSignature validates the Fernet-like outer format used by GPT/Codex reasoning encrypted_content. This is only a transport-shape check; it does not prove decryptability.
type GeminiThoughtSignatureEnvelope ¶
type GeminiThoughtSignatureEnvelope string
const ( GeminiThoughtSignatureEnvelopeUnknown GeminiThoughtSignatureEnvelope = "unknown" GeminiThoughtSignatureEnvelopeProtobufField1 GeminiThoughtSignatureEnvelope = "protobuf_field_1" GeminiThoughtSignatureEnvelopeProtobufField2 GeminiThoughtSignatureEnvelope = "protobuf_field_2" GeminiThoughtSignatureEnvelopeASCIIUUID GeminiThoughtSignatureEnvelope = "ascii_uuid" )
type GeminiThoughtSignatureInfo ¶
type GeminiThoughtSignatureInfo struct {
IsBypassSentinel bool
BypassSentinel string
DecodedLen int
FirstByte byte
HasObservedMarker bool
KnownEnvelope bool
Envelope GeminiThoughtSignatureEnvelope
RecordCount int
OpaquePayloadLen int
}
GeminiThoughtSignatureInfo describes the locally inspectable properties of an opaque Gemini thought signature.
func InspectGeminiThoughtSignature ¶
func InspectGeminiThoughtSignature(rawSignature string, opts ...GeminiThoughtSignatureValidationOptions) (*GeminiThoughtSignatureInfo, error)
InspectGeminiThoughtSignature validates and inspects the local transport shape of a Gemini thought signature. It intentionally treats provider-issued signatures as opaque base64 payloads.
type GeminiThoughtSignatureValidationOptions ¶
type GeminiThoughtSignatureValidationOptions struct {
// AllowBypassSentinel accepts Gemini's documented synthetic-history bypass
// sentinels. Keep this false when validating provider-issued signatures.
AllowBypassSentinel bool
// RequireKnownEnvelope requires the decoded payload to match one of the
// protobuf envelopes observed in Gemini samples. This rejects opaque base64
// values such as base64 UUIDs.
RequireKnownEnvelope bool
// RequireObservedMarker requires the decoded payload to start with 0x12.
// Current Gemini 3.x samples show this marker, but Gemini 2.5 samples use a
// different protobuf prefix, so this should be used only for narrow Gemini 3
// experiments.
RequireObservedMarker bool
}
GeminiThoughtSignatureValidationOptions controls how much local validation is applied to Gemini thought signatures. This validation checks only the opaque transport envelope; it does not prove that a signature came from Gemini or can be decrypted by Gemini.
type SignatureBlockKind ¶
type SignatureBlockKind string
const ( SignatureBlockKindUnknown SignatureBlockKind = "unknown" SignatureBlockKindClaudeThinking SignatureBlockKind = "claude_thinking" SignatureBlockKindGeminiModelPart SignatureBlockKind = "gemini_model_part" SignatureBlockKindGeminiFunctionCall SignatureBlockKind = "gemini_function_call" SignatureBlockKindGPTReasoning SignatureBlockKind = "gpt_reasoning" )
type SignatureCompatibilityAction ¶
type SignatureCompatibilityAction string
const ( SignatureActionPreserve SignatureCompatibilityAction = "preserve" SignatureActionDropBlock SignatureCompatibilityAction = "drop_block" SignatureActionDropSignature SignatureCompatibilityAction = "drop_signature" SignatureActionReplaceWithGeminiBypass SignatureCompatibilityAction = "replace_with_gemini_bypass" SignatureActionNoCompatibleReplacement SignatureCompatibilityAction = "no_compatible_replacement" )
type SignatureCompatibilityDecision ¶
type SignatureCompatibilityDecision struct {
TargetProvider SignatureProvider
DetectedProvider SignatureProvider
BlockKind SignatureBlockKind
Compatible bool
Action SignatureCompatibilityAction
ReplacementSignature string
NormalizedSignature string
Reason string
}
func DecideSignatureCompatibility ¶
func DecideSignatureCompatibility(targetProvider SignatureProvider, rawSignature string, blockKind SignatureBlockKind) SignatureCompatibilityDecision
DecideSignatureCompatibility returns the safe handling policy for replaying a signed block into targetProvider.
type SignatureProvider ¶
type SignatureProvider string
const ( SignatureProviderUnknown SignatureProvider = "unknown" SignatureProviderClaude SignatureProvider = "claude" SignatureProviderGemini SignatureProvider = "gemini" SignatureProviderGeminiBypass SignatureProvider = "gemini_bypass" SignatureProviderGPT SignatureProvider = "gpt" )
func DetectSignatureProvider ¶
func DetectSignatureProvider(rawSignature string) SignatureProvider
DetectSignatureProvider classifies the provider family that can replay rawSignature. It intentionally uses Claude strict validation before Gemini detection because Gemini 3 signatures also decode from an E-prefixed base64 string and can look Claude-like under shallow prefix checks.
func DetectSignatureProviderForBlock ¶
func DetectSignatureProviderForBlock(rawSignature string, blockKind SignatureBlockKind) SignatureProvider
DetectSignatureProviderForBlock classifies rawSignature with block-kind context. UUID-shaped payloads are deliberately not classified as replay-safe provider signatures; callers targeting Gemini should replace them with the bypass sentinel.
func SignatureProviderFromCachePrefix ¶
func SignatureProviderFromCachePrefix(prefix string) SignatureProvider
SignatureProviderFromCachePrefix maps this repo's explicit provider-prefix envelope to a provider family. This is intentionally stricter than SignatureProviderFromModelName so arbitrary model names such as "claude-cache#..." cannot be mistaken for trusted provider provenance.
func SignatureProviderFromModelName ¶
func SignatureProviderFromModelName(modelName string) SignatureProvider
SignatureProviderFromModelName maps common model names to the provider family whose signed history can be safely replayed for that model.
func SplitSignatureProviderPrefix ¶
func SplitSignatureProviderPrefix(rawSignature string) (SignatureProvider, string, bool)
type SignatureSanitizeReport ¶
type SignatureSanitizeReport struct {
TargetProvider SignatureProvider
Preserved int
DroppedBlocks int
DroppedSignatures int
ReplacedSignatures int
Decisions []SignatureCompatibilityDecision
}
func SanitizeClaudeMessagesForClaudeUpstream ¶ added in v7.1.30
func SanitizeClaudeMessagesForClaudeUpstream(payload []byte, targetModel string) ([]byte, SignatureSanitizeReport)
SanitizeClaudeMessagesForClaudeUpstream prepares a Claude /v1/messages body for native Claude upstreams. Invalid thinking blocks are dropped, valid thinking signatures are normalized to Claude provider-native E-form, and tool_use blocks keep only their tool-call payload.
func SanitizeClaudeMessagesSignaturesForModel ¶
func SanitizeClaudeMessagesSignaturesForModel(payload []byte, targetModel string) ([]byte, SignatureSanitizeReport)
SanitizeClaudeMessagesSignaturesForModel removes or preserves Claude /v1/messages signed history according to the provider family implied by targetModel.
func SanitizeClaudeMessagesSignaturesForTarget ¶
func SanitizeClaudeMessagesSignaturesForTarget(payload []byte, opts ClaudeMessagesSignatureSanitizeOptions) ([]byte, SignatureSanitizeReport)
SanitizeClaudeMessagesSignaturesForTarget applies provider-aware signature compatibility rules to Claude /v1/messages history. Compatible thinking signatures are preserved. Incompatible thinking blocks are removed so a user can continue a conversation after switching between Claude, GPT/Codex, and Gemini models.