Model Detection Evidence
The requests sent by the detector, the fields it reads, and the rules it applies, organized by protocol path.
Choose the protocol you are testing
We hide probes that do not run on that path. Evidence grades, weights, and decision rules stay unchanged.
17 scored cards · 10 unscored evidence
Evidence grades
Every probe carries a grade. It tells you how hard the evidence is — not how important we think it is.
Grounded in fields or mechanisms explicitly defined in the official API docs — you can re-check our rule against the spec.
The capability is official, but which channels support it comes from our own measurements and can drift over time.
Based on keyword lists, refusal patterns and ID prefixes we maintain. Promptable and changeable upstream — corroborating evidence only, never a verdict on its own.
Native Lineage
Runs only against Anthropic-native endpoints. Weights inside this group total 100 — it is our strongest evidence on whether a relay is wrapping something else.
Signature Verification
thinking_signatureForged backends without access to Anthropic's real signature-validation path. Faking something that looks like a thinking block is trivial; producing a signature that survives Anthropic's server-side validation is not. OAuth or subscription credential pools still call a genuine Anthropic backend, so their signatures pass and this probe cannot rule them out.
Signature Verification
thinking_signatureForged backends without access to Anthropic's real signature-validation path. Faking something that looks like a thinking block is trivial; producing a signature that survives Anthropic's server-side validation is not. OAuth or subscription credential pools still call a genuine Anthropic backend, so their signatures pass and this probe cannot rule them out.
How it works
We first send an arithmetic question that requires reasoning, with thinking parameters attached (thinking: {type: "adaptive"} plus effort: high first; on a 400 we fall back to {type: "enabled", budget_tokens: 1024}), and pull out the standalone thinking block carrying a signature field. A second request then replays that exact signed block as assistant history and asks a follow-up. A genuine Anthropic backend validates the signature with its own key and continues; a forged or transcribed signature is rejected outright.
What it catches
Forged backends without access to Anthropic's real signature-validation path. Faking something that looks like a thinking block is trivial; producing a signature that survives Anthropic's server-side validation is not. OAuth or subscription credential pools still call a genuine Anthropic backend, so their signatures pass and this probe cannot rule them out.
Decision rule
Replay accepted (2xx) → pass. Transient 429 / 5xx → warning, no verdict. Replay explicitly rejected → fail, and that alone is enough to call it. If round one never returned a signed thinking block, we record it as not verifiable — no score deducted, no red flag. A model that simply did not think, or a relay that stripped the block, is not evidence of forgery.
Basis & limits
Grounded in Anthropic's documented extended-thinking behavior: thinking blocks carry a signature, validated server-side on replay. The limit: a passing signature only proves the backend can validate Anthropic signatures. It does not separate Console from Bedrock or Vertex, and it does not rule out an OAuth credential pool — those hit the real Anthropic API, so signatures pass fine. Separating channels needs the web_search probe, response-header fingerprints and the system-override check read together.
LLM Fingerprint / Client Identity
canary + client_identityCredential pools reselling Claude Code OAuth quota as an API. These relays prepend Claude Code's prompt before ours; the model knows, and will say so if asked the right way. Three phrasings run in parallel to defeat keyword filtering — a relay can block the literal Chinese sentence, but blocking the English paraphrase and the tool-list route at the same time is much harder.
LLM Fingerprint / Client Identity
canary + client_identityCredential pools reselling Claude Code OAuth quota as an API. These relays prepend Claude Code's prompt before ours; the model knows, and will say so if asked the right way. Three phrasings run in parallel to defeat keyword filtering — a relay can block the literal Chinese sentence, but blocking the English paraphrase and the tool-list route at the same time is much harder.
How it works
Two sub-probes. Canary: we generate a fresh 10-character uppercase code each run and ask for it back verbatim, watching for anyone rewriting content in the middle. Client identity: we ask what's in your system prompt three different ways — verbatim Chinese, paraphrased English, and a tool-list request — and any hit on Claude Code's exclusive markers (including the tool names its injection drags along) is decisive.
What it catches
Credential pools reselling Claude Code OAuth quota as an API. These relays prepend Claude Code's prompt before ours; the model knows, and will say so if asked the right way. Three phrasings run in parallel to defeat keyword filtering — a relay can block the literal Chinese sentence, but blocking the English paraphrase and the tool-list route at the same time is much harder.
Decision rule
Canary echoed verbatim → pass; rewritten or truncated → fail. Client identity hitting a Claude Code marker → fail (confirmed wrapper); a clean response body → pass; all three phrasings refused or empty → recorded as inconclusive and excluded from the denominator.
Basis & limits
The keyword and refusal lists are heuristics we maintain, hence the grade. Note the asymmetry: a hit is strong evidence (a normal API does not spontaneously mention Claude Code's tool names), a miss is not proof of innocence — a relay that knows what we ask can filter all of it. The canary uses a random code rather than a fixed token precisely so it cannot be allow-listed.
Behavioral Verification
structured_output + server_tool + multi_turnThin wrappers that only forward text and cannot do native work. web_search is the sharpest of the three — it is Anthropic first-party only, unavailable on both Bedrock and Vertex, so a genuine execution is positive evidence of a direct Console connection.
Behavioral Verification
structured_output + server_tool + multi_turnThin wrappers that only forward text and cannot do native work. web_search is the sharpest of the three — it is Anthropic first-party only, unavailable on both Bedrock and Vertex, so a genuine execution is positive evidence of a direct Console connection.
How it works
Three sub-probes. Structured output: force a tool call via tool_choice against a strict enum and see whether the response honors it. web_search: send one search request with the web_search_20250305 server tool attached and look for server_tool_use or web_search_tool_result blocks. Multi-turn: have the model memorize 74631, then replay the history and ask for it back.
What it catches
Thin wrappers that only forward text and cannot do native work. web_search is the sharpest of the three — it is Anthropic first-party only, unavailable on both Bedrock and Vertex, so a genuine execution is positive evidence of a direct Console connection.
Decision rule
Scored separately (structured output 8, web_search 10, multi-turn 5). A valid response that simply did not trigger a search → warning, not failure: the tool requires explicit enablement, and not having it enabled is not fraud. Multi-turn hitting 429 / 5xx → warning.
Basis & limits
All three capabilities are officially defined, but which channel supports what comes from our own testing — hence the mixed grade. The limit: all three can be legitimately trimmed by a relay. A well-behaved gateway might simply not pass tool parameters through, which is why these weigh less than signature verification.
Structural Integrity
sse_eventsGateways that trim or incorrectly rebuild Anthropic SSE. Missing official events, invalid ordering, or absent input_tokens in message_start leave structural evidence. A relay that faithfully reproduces the full sequence can still pass, so this probe alone does not prove live generation.
Structural Integrity
sse_eventsGateways that trim or incorrectly rebuild Anthropic SSE. Missing official events, invalid ordering, or absent input_tokens in message_start leave structural evidence. A relay that faithfully reproduces the full sequence can still pass, so this probe alone does not prove live generation.
How it works
One request with stream: true. We extract the SSE event types in order and check that message_start → content_block_start → content_block_delta → content_block_stop → message_delta → message_stop all appear in sequence, and that input_tokens in message_start is greater than zero.
What it catches
Gateways that trim or incorrectly rebuild Anthropic SSE. Missing official events, invalid ordering, or absent input_tokens in message_start leave structural evidence. A relay that faithfully reproduces the full sequence can still pass, so this probe alone does not prove live generation.
Decision rule
Full ordered sequence with input_tokens > 0 → pass. A stream exists but the order is incomplete or input_tokens is 0 → warning. No native Anthropic SSE events at all → fail.
Basis & limits
Grounded in Anthropic's published streaming event sequence. Its limit is structural: a relay that faithfully reproduces the official sequence can still pass, so this does not prove live generation. TTFT and inter-chunk gaps are recorded for manual review but do not currently affect the verdict. Note: non-native endpoints also show an item called Structural Integrity, but that one checks core response JSON fields (choices / message), not the SSE sequence. The two never appear together.
Multimodal Capability
multimodalText-only substitute models, and gateways that transcode in the middle and drop attachments. The material is generated fresh each run, so memorizing the answer is not an option — the model has to actually see it.
Multimodal Capability
multimodalText-only substitute models, and gateways that transcode in the middle and drop attachments. The material is generated fresh each run, so memorizing the answer is not an option — the model has to actually see it.
How it works
Two sub-probes, both using canary material generated on the spot. Image: a PNG containing random characters, sent as base64 with a request to read it back. PDF: a document with an embedded random code, likewise sent as base64 with a request to echo the code.
What it catches
Text-only substitute models, and gateways that transcode in the middle and drop attachments. The material is generated fresh each run, so memorizing the answer is not an option — the model has to actually see it.
Decision rule
Both image and PDF read correctly → pass. Only one → warning. Neither → fail.
Basis & limits
Image and document input are official Anthropic capabilities, but treating a failed read as suspicious is an engineering judgement — models occasionally get it wrong, and a channel may disable attachments for cost reasons. Hence the mixed grade and a weight well below signature verification.
Gemini Native Protocol
Runs only against Gemini-native generateContent endpoints, checking native responses, safety feedback, and thought signatures for complete passthrough.
Gemini Native Structure
protocolGateways that reroute Gemini requests through an OpenAI-compatible layer, fabricate only plain text, or strip native candidates, content, parts, and usageMetadata fields.
Gemini Native Structure
protocolGateways that reroute Gemini requests through an OpenAI-compatible layer, fabricate only plain text, or strip native candidates, content, parts, and usageMetadata fields.
How it works
We send a minimal text request to models/{model}:generateContent with the key in the x-goog-api-key header and enough output budget for Gemini 2.5's internal thought tokens, then require both candidates[0].content.parts and usageMetadata while recording finishReason, modelVersion, and responseId.
What it catches
Gateways that reroute Gemini requests through an OpenAI-compatible layer, fabricate only plain text, or strip native candidates, content, parts, and usageMetadata fields.
Decision rule
2xx with a non-empty text Part plus non-negative integer promptTokenCount, candidatesTokenCount, and totalTokenCount, where total exactly equals prompt, candidate, and thought tokens → pass. 429 / 5xx → warning. Missing structure or inconsistent usage arithmetic → fail.
Basis & limits
The endpoint, auth header, response shape, and token fields all come from Google's official Gemini API documentation. A gateway can still imitate the schema, so passing proves protocol compatibility, not account provenance or model identity.
Safety Ratings Passthrough
gemini_safety_ratingsThin wrappers that do not preserve Gemini candidate safety feedback. Missing category or probability prevents clients from auditing filtering; the optional blocked state is retained whenever returned.
Safety Ratings Passthrough
gemini_safety_ratingsThin wrappers that do not preserve Gemini candidate safety feedback. Missing category or probability prevents clients from auditing filtering; the optional blocked state is retained whenever returned.
How it works
We send a benign safety-related prompt and explicitly set all four adjustable safety categories to BLOCK_NONE through safetySettings. Unlike Gemini 2.5's default OFF, this enables evaluation without blocking content. We then read category, probability, and the optional blocked state from candidates[0].safetyRatings.
What it catches
Thin wrappers that do not preserve Gemini candidate safety feedback. Missing category or probability prevents clients from auditing filtering; the optional blocked state is retained whenever returned.
Decision rule
At least one rating whose category and probability are both in Google's documented enums, plus valid usageMetadata arithmetic for this request → pass. A 2xx response with missing fields, unknown enums, incomplete ratings, or invalid usage → warning. A non-transient request failure → fail.
Basis & limits
safetyRatings, safetySettings, BLOCK_NONE, and usageMetadata are officially documented by Google. Gemini 2.5 and 3 default to OFF, which can omit safety metadata unless explicitly configured. The probe therefore enables evaluation while only testing complete passthrough; it never grades the model by a probability value or sends dangerous content. Usage is checked independently on every scored request so a relay cannot preserve billing fields only on the first response and strip them afterward.
Thought Signature Passthrough
gemini_thoughtGemini relays that reject thinkingConfig, flatten thought parts into plain text, or strip thoughtSignature. A missing signature is a compatibility warning, not standalone proof of model substitution.
Thought Signature Passthrough
gemini_thoughtGemini relays that reject thinkingConfig, flatten thought parts into plain text, or strip thoughtSignature. A missing signature is a compatibility warning, not standalone proof of model substitution.
How it works
We send a simple arithmetic problem with includeThoughts: true under generationConfig.thinkingConfig and force one record_total function call through toolConfig. Google documents that generateContent signatures may attach to a functionCall part or the final response part. We then scan candidate parts for thought: true, a non-empty thoughtSignature, the function call, and thoughtsTokenCount.
What it catches
Gemini relays that reject thinkingConfig, flatten thought parts into plain text, or strip thoughtSignature. A missing signature is a compatibility warning, not standalone proof of model substitution.
Decision rule
A thought part and thoughtSignature, plus valid usageMetadata arithmetic for this request → pass. A successful response with missing structure or invalid usage, or an explicit thinkingConfig unsupported error → warning. A non-transient request failure → fail.
Basis & limits
thinkingConfig, thought, thoughtSignature, function calling, thoughtsTokenCount, and usageMetadata are all documented by Google. Plain-text responses do not guarantee that every thought summary itself carries the signature, so a forced function call provides a more stable attachment point. Absence remains a compatibility warning, not standalone proof of model substitution. This request validates usage independently so thought tokens remain represented in its billing structure.
Gemini Behavioral Evidence
Six unscored signals supplement the native 100-point score with checks for model mismatch, content rewriting, obvious capability downgrades, dropped parameters, answer replay, and streaming-shape trimming.
Model Version Check
identityRouting mismatches or cheaper-model substitution where the requested model clearly differs from modelVersion. Gateways can rewrite this field, so it remains corroborating evidence only.
Model Version Check
identityRouting mismatches or cheaper-model substitution where the requested model clearly differs from modelVersion. Gateways can rewrite this field, so it remains corroborating evidence only.
How it works
We reuse modelVersion from the native-structure request, normalize models/, latest, and official version suffixes, then compare it case-insensitively with the requested model. No extra request is sent.
What it catches
Routing mismatches or cheaper-model substitution where the requested model clearly differs from modelVersion. Gateways can rewrite this field, so it remains corroborating evidence only.
Decision rule
Exact match or only an added official version suffix → pass. Missing or mismatched fields → warning for manual review, with no deduction from the native protocol score.
Basis & limits
modelVersion is an official Gemini response field, but a gateway can rewrite it. Reading the field is spec-grounded; inferring identity from it remains heuristic.
Canary Echo
canaryIntermediaries that rewrite, truncate, or filter ordinary text responses. A fresh randomized code on every run makes hard-coded handling of a fixed sample less practical.
Canary Echo
canaryIntermediaries that rewrite, truncate, or filter ordinary text responses. A fresh randomized code on every run makes hard-coded handling of a fixed sample less practical.
How it works
Each run generates a fresh uppercase code, asks generateContent to return only that code, then compares the complete trimmed response with a constant-time exact check. Gemini 2.5 Pro uses the official minimum thinkingBudget of 128, 2.5 Flash disables thinking, and 256 tokens are reserved for generation.
What it catches
Intermediaries that rewrite, truncate, or filter ordinary text responses. A fresh randomized code on every run makes hard-coded handling of a fixed sample less practical.
Decision rule
Complete response exactly equals the random code and ends normally → pass. MAX_TOKENS truncation or an unsupported thinkingBudget → warning. A normally completed response that rewrites, omits, or explains the code → fail.
Basis & limits
Random-token echo is an engineering heuristic, not Google identity proof. A model can occasionally miss the format instruction, so this remains unscored evidence; truncation is no longer mislabeled as relay rewriting.
Capability Floor Check
behaviorObvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
Capability Floor Check
behaviorObvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
How it works
We reuse the same generateContent response as the random canary and ask for a second line solving a combinatorial-geometry problem: for a 14 by 6 rectangle, choose any three vertices to form triangles, then return half the sum of all triangle areas. The unique integer answer is 84, with no extra upstream request.
What it catches
Obvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
Decision rule
The second line contains exactly one integer equal to 84 → pass. Multiple integers, a wrong integer, or no extractable answer → fail. MAX_TOKENS truncation or a thinkingBudget fallback → warning.
Basis & limits
This fixed problem is only a heuristic capability floor. It can expose some obvious downgrades or weak substitutions, but cannot distinguish models at the same capability level, and one wrong answer does not prove substitution. A relay that knows the problem can also hard-code the answer.
Gemini Parameter Passthrough
max_tokensGateways that silently discard or rewrite generationConfig.maxOutputTokens and stopSequences.
Gemini Parameter Passthrough
max_tokensGateways that silently discard or rewrite generationConfig.maxOutputTokens and stopSequences.
How it works
Two native requests. First we request 400 words with generationConfig.maxOutputTokens=192 and verify that candidate plus thought tokens stay within the cap with finishReason=MAX_TOKENS. Then a one-time marker is placed in stopSequences under a 512-token budget and we verify cutoff before it with finishReason=STOP. 2.5 Pro uses the minimum thinkingBudget of 128, while 2.5 Flash disables thinking.
What it catches
Gateways that silently discard or rewrite generationConfig.maxOutputTokens and stopSequences.
Decision rule
Both the length cap and stop sequence work → pass. Exceeding the cap or emitting the marker or suffix → fail. No observable prefix due to instruction variance, or a transient error → warning.
Basis & limits
maxOutputTokens, stopSequences, candidatesTokenCount, and finishReason are all official Gemini fields. Prompt compliance still varies, so the probe is not yet included in the native score.
Randomness / Replay Check
determinismCache layers that replay a complete answer verbatim even under a high-temperature open-ended request. Repetition is suspicious evidence, but templated copy can also repeat naturally.
Randomness / Replay Check
determinismCache layers that replay a complete answer verbatim even under a high-temperature open-ended request. Repetition is suspicious evidence, but templated copy can also repeat naturally.
How it works
We call the same open-ended copywriting prompt three times with temperature=1 and maxOutputTokens=256, then lowercase each complete response, remove whitespace, and compare exact equality. Only non-empty outputs ending with finishReason=STOP count as valid samples.
What it catches
Cache layers that replay a complete answer verbatim even under a high-temperature open-ended request. Repetition is suspicious evidence, but templated copy can also repeat naturally.
Decision rule
Three normally completed, distinct outputs → pass. Any two complete outputs matching → fail. MAX_TOKENS truncation or fewer than three valid samples → warning with no replay conclusion.
Basis & limits
temperature is an official sampling parameter, but the repetition threshold is an engineering judgement. It only detects verbatim replay, misses semantic caches, and cannot prove every duplicate came from caching.
Gemini Native Streaming
streamCompatibility layers that do not support streamGenerateContent?alt=sse or reshape the response into non-Gemini data events.
Gemini Native Streaming
streamCompatibility layers that do not support streamGenerateContent?alt=sse or reshape the response into non-Gemini data events.
How it works
We send a native 512-token request to models/{model}:streamGenerateContent?alt=sse, require a text/event-stream Content-Type, parse every data: JSON event, and check for candidates[].content.parts, a non-empty text part, and valid usageMetadata.
What it catches
Compatibility layers that do not support streamGenerateContent?alt=sse or reshape the response into non-Gemini data events.
Decision rule
2xx, SSE Content-Type, valid data events, non-empty text, and valid usage together → pass. Native structure without text, missing usage, MAX_TOKENS truncation, or a thinkingBudget fallback → warning. Plain JSON or a non-Gemini event shape → fail.
Basis & limits
The endpoint, alt=sse, and event payload are defined by Gemini's official streaming protocol. This implementation validates native SSE shape; it does not use a buffered client body to claim TTFT or live generation.
Protocol Foundation
The base layer: does the response have the right shape, are the billing fields complete. All three can be checked word-for-word against the official docs.
Schema Compliance
schemaCross-protocol wrapping between the three vendors, gateways stripping fields, and error payloads dressed up as successful responses.
Schema Compliance
schemaCross-protocol wrapping between the three vendors, gateways stripping fields, and error payloads dressed up as successful responses.
How it works
One plain request (temperature=0, asking which direction the sun rises) and a check of the returned JSON shape: the Anthropic protocol needs a content array, the OpenAI protocol needs choices with choices[0].message as an object.
What it catches
Cross-protocol wrapping between the three vendors, gateways stripping fields, and error payloads dressed up as successful responses.
Decision rule
2xx with the core structure present → pass. Transient 429 / 5xx → warning. Missing structure → fail.
Basis & limits
Response shape is explicitly defined in the OpenAI and Anthropic API docs, while Gemini uses its dedicated native-structure card. The rule can be checked against those docs directly. The limit: it reads structure, not substance. Passing only means the other end implements the protocol — not that the model behind it is genuine.
Usage Completeness
usageGateways that omit usage, or report numbers that contradict themselves. usage is your only basis for reconciliation — if it's missing or fabricated, the bill cannot be audited.
Usage Completeness
usageGateways that omit usage, or report numbers that contradict themselves. usage is your only basis for reconciliation — if it's missing or fabricated, the bill cannot be audited.
How it works
From the same response we read the usage block and check that at least one of prompt_tokens / completion_tokens is greater than zero, and that total_tokens is either 0 (not provided) or no less than their sum.
What it catches
Gateways that omit usage, or report numbers that contradict themselves. usage is your only basis for reconciliation — if it's missing or fabricated, the bill cannot be audited.
Decision rule
Usage present and the arithmetic holds → pass. 2xx but usage missing or inconsistent → warning. Nothing returned at all → fail.
Basis & limits
The semantics of usage are defined in the official docs. The limit: we verify internal consistency, not honesty. Determining whether the usage upstream reports matches what the relay charges you requires cross-request comparison, which is outside this probe.
Stop Reason Validity
stop_reasonRelays that assemble responses themselves often slip on the protocol-specific stop field by inventing an unknown value, omitting the field, or returning null.
Stop Reason Validity
stop_reasonRelays that assemble responses themselves often slip on the protocol-specific stop field by inventing an unknown value, omitting the field, or returning null.
How it works
We read the protocol-specific stop field: OpenAI-compatible responses must return a non-null choices[0].finish_reason, while non-streaming Anthropic responses must return a non-null top-level stop_reason. We then check the value against that protocol's official enum.
What it catches
Relays that assemble responses themselves often slip on the protocol-specific stop field by inventing an unknown value, omitting the field, or returning null.
Decision rule
2xx with the expected field present and a value inside the protocol's enum → pass. Transient 429 / 5xx → warning. Missing field, null value, or unknown enum value → fail.
Basis & limits
The field locations and enums come from the official OpenAI Chat Completions and Anthropic Messages schemas. Anthropic declares the type nullable for streaming message_start compatibility, but explicitly states that non-streaming responses are always non-null. The limit: copying a valid value from the docs is easy, so passing is only protocol-compliance evidence while failing is the stronger negative signal.
Runtime Behavior
Checks whether streaming is accepted and records timing, latency and stability evidence. Semantic caching is not currently evaluated.
Streaming Support
streamEndpoints that reject stream=true or return the entire response in one write. We also record time-to-first-byte and inter-chunk gaps for manual review, but those values do not currently affect pass/fail and cannot detect cached answers replayed as multiple network chunks.
Streaming Support
streamEndpoints that reject stream=true or return the entire response in one write. We also record time-to-first-byte and inter-chunk gaps for manual review, but those values do not currently affect pass/fail and cannot detect cached answers replayed as multiple network chunks.
How it works
One request with stream: true. Beyond checking that valid chunks arrive, we record time-to-first-byte as a share of total time, plus the median and maximum inter-chunk gaps.
What it catches
Endpoints that reject stream=true or return the entire response in one write. We also record time-to-first-byte and inter-chunk gaps for manual review, but those values do not currently affect pass/fail and cannot detect cached answers replayed as multiple network chunks.
Decision rule
2xx with multiple network chunks → pass. Transient error → warning. No multiple-chunk response → fail. TTFT and gap statistics are written to evidence only and do not affect the current status.
Basis & limits
stream=true and SSE transport are defined by the official protocols. The limit: multiple network chunks do not prove live model generation — a relay can fetch the full answer first and replay it in pieces. TTFT and gap measurements are also affected by network jitter, so they are currently for manual review only and cannot establish fraud or semantic caching.
Latency & Throughput
latencyNot a fraud check — a link-quality readout. For the same model, a relay with several hops in front of it usually shows visibly worse time-to-first-byte.
Latency & Throughput
latencyNot a fraud check — a link-quality readout. For the same model, a relay with several hops in front of it usually shows visibly worse time-to-first-byte.
How it works
We record time-to-first-token and total elapsed time. When authentication fails or no valid model response arrives, this item is not evaluated at all.
What it catches
Not a fraud check — a link-quality readout. For the same model, a relay with several hops in front of it usually shows visibly worse time-to-first-byte.
Decision rule
A valid model response → pass, with the measurements recorded. No valid response or auth failure → not evaluated, excluded from the denominator.
Basis & limits
Elapsed time is a direct measurement with no inference involved. The limit: a single sample depends heavily on network location and load at that moment, so when comparing relays side by side, check that the measurements came from the same origin.
Randomness / Replay Check
determinismGateways that replay cached answers verbatim. A normal model usually varies its wording across high-temperature samples of the same open-ended prompt; a full answer repeated after whitespace normalization may not have been regenerated.
Randomness / Replay Check
determinismGateways that replay cached answers verbatim. A normal model usually varies its wording across high-temperature samples of the same open-ended prompt; a full answer repeated after whitespace normalization may not have been regenerated.
How it works
We send the same open-ended creative prompt three times with temperature=1 and max_tokens=120. Each full output is lowercased with whitespace removed, then all three complete answers are compared for exact equality.
What it catches
Gateways that replay cached answers verbatim. A normal model usually varies its wording across high-temperature samples of the same open-ended prompt; a full answer repeated after whitespace normalization may not have been regenerated.
Decision rule
Any two normalized full outputs match → fail. If there is no duplicate but fewer than three valid outputs → warning, with no replay conclusion. Three valid outputs, all different → pass.
Basis & limits
Combining a documented sampling parameter with a full-answer repetition threshold makes this a spec-plus-field-data probe. It can expose verbatim replay, but cannot prove that every duplicate came from a cache, detect semantic caches that alter a few words, distinguish templated copy, or catch cached answers split into network chunks. Timing evidence remains manual-review context and does not affect this result.
Model Identity
Who the other end claims to be, and whether it respects the limits we set.
Model Self-Declaration
identityCheaper-model substitution and routing mismatches. A wrong model field is a direct clue — you asked for Opus and something else came back.
Model Self-Declaration
identityCheaper-model substitution and routing mismatches. A wrong model field is a direct clue — you asked for Opus and something else came back.
How it works
Primarily a comparison between the model field in the response and the model we requested: an exact match scores full, a containing match (e.g. with a date suffix) still passes, a mismatch raises a warning. We also ask the model which company built it and which model it is, and record that self-description verbatim.
What it catches
Cheaper-model substitution and routing mismatches. A wrong model field is a direct clue — you asked for Opus and something else came back.
Decision rule
model field identical to or containing the requested model → pass. Mismatch → warning. No model field but a valid body → graded down based on whether content exists.
Basis & limits
Graded as a heuristic because a model's self-description can be shaped freely by the system prompt, and no vendor has ever promised it works as an identity credential — making a model claim to be Claude is trivial. The model field is equally easy for a relay to rewrite. So this is corroboration only; the actual verdict comes from signature verification and channel fingerprints.
Capability Floor Check
behaviorObvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
Capability Floor Check
behaviorObvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
How it works
We send a combinatorial-geometry problem with one answer: for a 14 by 6 rectangle, choose any three vertices to form triangles, then return half the sum of all triangle areas. We prefer temperature=0 with max_tokens=128 and request exactly one decimal integer. If an Anthropic relay explicitly rejects temperature, we remove only that parameter and retry once.
What it catches
Obvious downgrades, low-capability substitutions, or routing to a smaller model. A fixed problem checks basic combinatorial and geometric reasoning, but cannot separate peers at the same capability level and can be hard-coded around.
Decision rule
The response contains exactly one integer equal to 84 → pass. Multiple integers, a wrong integer, or no integer → fail.
Basis & limits
This is a heuristic. A fixed problem establishes a basic capability floor and can expose obvious downgrades, weak substitutions, or routing mistakes, but it cannot separate peers at the same capability level. Models can occasionally answer incorrectly, and a relay that knows the prompt can hard-code the answer, so this evidence must be read with protocol, identity, and native signals.
Parameter Passthrough
max_tokensGateways that silently drop or rewrite max_tokens, stop, or stop_sequences. If a randomized stop marker appears in output past the intended cutoff, the stop parameter was not honored as specified.
Parameter Passthrough
max_tokensGateways that silently drop or rewrite max_tokens, stop, or stop_sequences. If a randomized stop marker appears in output past the intended cutoff, the stop parameter was not honored as specified.
How it works
First, we send a short-answer request with max_tokens=20 and compare completion_tokens with visible output length. Then we generate a one-time random stop marker, send stop for OpenAI or stop_sequences for Anthropic, and verify cutoff behavior plus the protocol-specific stop reason.
What it catches
Gateways that silently drop or rewrite max_tokens, stop, or stop_sequences. If a randomized stop marker appears in output past the intended cutoff, the stop parameter was not honored as specified.
Decision rule
Both the length cap and stop sequence work → pass. A 2xx response that still emits the marker or suffix → fail. A 400 explicitly saying the parameter is unsupported → skip only the stop sub-check without treating it as fraud. Transient errors or incomplete evidence → warning.
Basis & limits
max_tokens, stop, and stop_sequences are all official protocol parameters, so this is graded as official-spec evidence. The marker changes on every run to make hard-coded gateway handling less practical. If the model never emits the requested prefix, text alone cannot prove passthrough, so the check does not pass.
Channel Provenance
This group scores nothing. It answers a different question: what actually sits behind this key — a direct Console connection, AWS Bedrock, Google Vertex, or a credential pool.
System Override Check
system_overrideRelays that prepend content before our system prompt. The elegance here is that all three outcomes are informative: a model that faithfully adopts the sentinel persona tells us our system prompt really was first in line.
System Override Check
system_overrideRelays that prepend content before our system prompt. The elegance here is that all three outcomes are informative: a model that faithfully adopts the sentinel persona tells us our system prompt really was first in line.
How it works
We plant a sentinel persona in the system prompt (every reply must start with a random marker), ask the model to follow it, and additionally ask whether any other instructions came before ours — quoting their first few words if so.
What it catches
Relays that prepend content before our system prompt. The elegance here is that all three outcomes are informative: a model that faithfully adopts the sentinel persona tells us our system prompt really was first in line.
Decision rule
A Claude Code marker in the response → fail, confirmed wrapper. No marker but the sentinel persona adopted verbatim → pass, a strong clean signal. Upstream transiently unavailable → warning. Neither obeyed nor leaked → inconclusive, no deduction.
Basis & limits
Graded heuristic because obeyed equals clean is our inference, not a vendor guarantee — models sometimes just don't comply. This probe contributes nothing to the 100-point score; it feeds the channel verdict: a leaked marker pushes toward credential pool, while compliance plus a clean sweep elsewhere pushes toward direct Console.
Response ID Prefix
message_id_prefixCloud channels being resold as a direct Anthropic connection. This costs nothing extra — the prefix is already in every normal response, no additional request needed.
Response ID Prefix
message_id_prefixCloud channels being resold as a direct Anthropic connection. This costs nothing extra — the prefix is already in every normal response, no additional request needed.
How it works
We read the prefix of the message id in the response. Direct Console yields msg_, AWS Bedrock yields msg_bdrk_, Google Vertex yields msg_vrtx_.
What it catches
Cloud channels being resold as a direct Anthropic connection. This costs nothing extra — the prefix is already in every normal response, no additional request needed.
Decision rule
Never a verdict on its own; it feeds the channel conclusion. We let the structured id prefix take priority over fuzzy matching on response text, because words like bedrock in a body may come from user input or an error message, while the prefix will not.
Basis & limits
Graded heuristic: neither AWS nor Google ever promised this prefix works as an identity credential. It is a stable observation of current behavior that could change at any time. That said, it is a genuinely useful channel clue, and it is free.
Response Header Fingerprint
headersAgain separating Console, Bedrock and Vertex — plus spotting intermediate reverse proxies, which the via and server headers often give away.
Response Header Fingerprint
headersAgain separating Console, Bedrock and Vertex — plus spotting intermediate reverse proxies, which the via and server headers often give away.
How it works
From each response we retain the distinguishing headers: anything starting with anthropic- (including rate-limit quota), x-amzn / x-amz-, x-goog / x-vertex, plus server, via and request-id.
What it catches
Again separating Console, Bedrock and Vertex — plus spotting intermediate reverse proxies, which the via and server headers often give away.
Decision rule
Never a verdict on its own; read together with the response ID prefix as input to the channel conclusion.
Basis & limits
Graded heuristic for the same reason as the ID prefix: these headers are set by each vendor's infrastructure, not promised as an interface. Their value is costing zero extra tokens — they arrive with requests we were sending anyway.
AWS Image URL Fingerprint
multimodal.aws_image_url_checkNew-API conversion layers that wrap AWS Bedrock in an Anthropic Messages facade. They download URL images and convert them to base64 first, potentially leaking their adapter-specific error prefix on failure.
AWS Image URL Fingerprint
multimodal.aws_image_url_checkNew-API conversion layers that wrap AWS Bedrock in an Anthropic Messages facade. They download URL images and convert them to base64 first, potentially leaking their adapter-specific error prefix on failure.
How it works
We send Anthropic Messages an image URL under a reserved domain. Current Anthropic types support URLImageSource, while Bedrock Claude Messages still use base64 input; New-API's AWS adapter downloads the URL and converts it to base64 first.
What it catches
New-API conversion layers that wrap AWS Bedrock in an Anthropic Messages facade. They download URL images and convert them to base64 first, potentially leaking their adapter-specific error prefix on failure.
Decision rule
An exact get file base64 from url failed prefix from the New-API AWS adapter → record an AWS / New-API conversion-path clue. Any other 400, success, or transport error → no match, no fraud verdict, no score deduction.
Basis & limits
Graded spec plus field data: Anthropic URLImageSource and Bedrock base64 input come from current official types and documentation, but the fixed error prefix comes from New-API's AWS adapter implementation, not a stable AWS interface. An upstream update can make it disappear, so this remains channel provenance only.
What this report is, and is not
This is engineering forensics, not cryptographic proof of identity. What we can do: run 16 steps against an Anthropic-native endpoint or 7 steps against a Gemini-native endpoint — some steps make more than one upstream request — compare observable structure, fields, event ordering and behavior against the published official API definitions, and give a probabilistic reading of unexplained differences. What we cannot do: prove who owns the account behind a key, prove it will behave the same at another point in time, or reach a legal conclusion about whether a relay is compliant. Every heuristic probe can be defeated on purpose — a relay that knows what we check can make all of them pass. That is why we put the weight on the spec-grade probes, and why we publish the decision rules and limits in full: you can re-check every line rather than trust a number.
FAQ
Does a 100 score mean the relay is definitely genuine?
No. It means every scored probe passed. Unscored behavioral evidence can still show warnings or failures. Heuristic probes can be bypassed by a relay that knows what we ask, and a passing signature only proves a real Anthropic backend is behind it — not which channel, and not that it will stay that way tomorrow.
Why do some items say not evaluated instead of failing?
Because absence of evidence is not evidence of fraud. If the model never produced a thinking block, or every phrasing of a question was refused, we record it as inconclusive and drop it from the denominator rather than deduct points for something we did not observe.
Why does my report show fewer items than this page?
Anthropic-native runs produce 4 sections and 14 scored items, Gemini-native runs produce 2 sections with 3 scored items plus 6 unscored evidence items, and OpenAI-compatible runs produce 3 sections and 9. Channel provenance and Gemini behavioral evidence never change the 100-point score.
Can the relay tell it is being tested and behave differently?
Possibly, and we say so plainly. Canary codes and sentinel personas are randomized per run so they cannot be allow-listed, but a determined operator could still special-case our traffic. Nothing here defeats that; it only raises the cost.
Why is signature verification worth 25 points while model self-declaration is only 22?
Because a signature is validated by Anthropic's own servers, out of the relay's reach, while a model's self-description can be rewritten by a system prompt. The two look similar on a report, but only one of them is hard to fake.
Point our detector at a relay endpoint and read the report against this page.
Scan to join WeChat group