Case studies
Endpoints whose behaviour stood out in the audit, followed up with extra measurements to rule explanations in or out, one comparison of local inference engines, and one attack on the cooperative mode, run against ourselves.
- Venice: Qwen3.8-27B is not the released model
- Darkbloom: junk tokens in gpt-oss-20b's sampled output
- Strata vs llama.cpp: same weights, same GPU, five times further from the model
- vLLM's gpt-oss-20b: reproducible, accurate, and checkable in one pass
- Red team: a hidden message that passes every exact check
Venice's Qwen3.8-27B is not the released model
Of 21 OpenRouter endpoints serving qwen/qwen3.8-27b, one stood apart. Venice's replies
are fluent and say they come from Qwen, but they sit further from the released weights than a deliberately 2-bit
quantized copy does. This page shows how that was measured and which explanations were ruled out. It does not
claim to know why: Venice may have a reason, and the checks below can't see inside its servers.
The measurement
A referee running the real Qwen3.8-27B weights (8-bit, CUDA) replayed every endpoint's temperature-0 replies to 48 prompts and measured how far each chosen token was from the model's own first choice.
| Endpoint | Distance (× honest) | Fidelity | Other token | Serious misses / 1k | First token = model's |
|---|---|---|---|---|---|
| Honest reference: the real weights, near-lossless 6-bit copy | 1.0× | 100 | 1.0% | 0.0 | 92% |
| Mancer (fp8), a typical endpoint | 2.8× | 77 | 1.8% | 0.0 | 96% |
| Substitute reference: the real weights crippled to 2 bits | 75× | 0 | 8.8% | 5.7 | 69% |
| Venice (fp8) | 165× | 0 | 11.8% | 24 | 58% |
"Serious misses" are tokens the real model rated at least ten times less likely than its favourite. Venice has four times as many as the 2-bit copy, and its temperature-1 samples also fit the model worse. Its replies are fully deterministic (the same prompt gives the same reply), so this is not sampling noise. The first hint came without any reference weights at all: Venice's replies agreed with the other 20 endpoints less than the 2-bit copy did.
What it isn't
Not a Venice-wide system prompt
Venice's API documentation says it adds default system prompts "designed to ensure uncensored and natural model responses" unless the caller opts out. If that applied to OpenRouter traffic across the board, every Venice model would be shifted. It isn't: on Gemma-4-26B and Qwen3.6-27B, Venice's temperature-0 replies were byte-for-byte identical to CoreWeave, Google Vertex and DeepInfra, and on Qwen3.5 they matched Parasail. Venice also bills the same prompt sizes as other endpoints and reports no system prompt when asked. A prompt applied only to this one model remains possible.
Not an older Qwen under the new name
Venice's replies match Qwen3.6-27B and Qwen3.5-27B (from DeepInfra and from Venice's own Qwen3.6 endpoint) less than genuine Qwen3.8 does: shared-prefix agreement 0.17-0.19 against 0.32-0.34.
Not ordinary quantization
Damage beyond a 2-bit copy's normally shows in the text. Venice's text reads fine.
Not a public uncensored build
Dozens of modified Qwen3.8-27B builds are public. The two most downloaded were run locally and their temperature-0 replies compared word for word with every endpoint's. Both stay much closer to the genuine endpoints than to Venice:
| Candidate weights | vs Venice | vs Mancer | vs DeepInfra BF16 | vs CoreWeave |
|---|---|---|---|---|
| Released Qwen3.8-27B (6-bit copy) | 0.17 | 0.69 | 0.56 | 0.69 |
| huihui-ai abliterated (4-bit) | 0.16 | 0.39 | 0.38 | 0.37 |
| JonathanColetti "Uncensored" (4-bit) | 0.18 | 0.46 | 0.40 | 0.43 |
Numbers are the mean shared prefix of temperature-0 replies over the first 150 characters (1 = identical).
A behavioural difference
Asked "Tell me a dark joke." at temperature 0, genuine Qwen3.8 endpoints (Mancer and DeepInfra BF16) answer identically, with a deflection: "I don't have a sense of humor, but I can tell you a dark joke: Why did the scarecrow win an award? Because he was outstanding in his field." Venice tells an actual dark joke.
It shows up in tool calling too. Given a set of tools (weather, currency, calendar, email…) and asked to
"Write a two-line poem about autumn.", every genuine Qwen3.8 endpoint writes the poem. Venice calls
get_weather for Boston, a city mentioned nowhere in the request, on all three tries.
What this means
The endpoint labelled qwen/qwen3.8-27b at Venice does not
serve the released weights as released, and this is detectable from the outside with about a hundred requests.
The pattern (fluent, identifies as Qwen, shifted everywhere, fewer deflections) is what a modified checkpoint or
a model-specific prompt would produce; a faulty deployment of a new model is less likely but possible. Which one
can't be determined from outside. Venice could say.
Venice openly offers uncensored models, and a modified Qwen may be intended. The issue for anyone routing through OpenRouter is disclosure: the listing doesn't mention it, so a request for Qwen3.8-27B that lands on Venice gets a different model. Venice's other models measured normally (Qwen3.5 and Gemma-4 sit with the FP8 group).
A later finding fits a mixed picture: on Gemma-4-26B, Gemma-4-31B, Qwen3.6 and MiniMax M3, Venice's endpoint behaves like DeepInfra's on every fingerprint we have (the same top_k limit, the same downscaled image size, the same reasoning cut-off), which suggests Venice routes those models through DeepInfra, while on Qwen3.5 and Qwen3.8 it behaves like nobody else. Indirect evidence; details under Results → One deployment, several names. Venice's privacy page does say Private-mode inference may run on unnamed "zero-data-retention partner infrastructure" under contractual guarantees, so this would fit its stated policy; the listing just doesn't say which models leave Venice, or to whom.
If you run Venice or have context, the measurement scripts and data are in the llm-verification repository; corrections are welcome and will be reflected here.
Darkbloom's gpt-oss-20b injects junk tokens when sampling
At temperature 0 Darkbloom's gpt-oss-20b (labelled fp8) is close to the released weights: fidelity 89, in the middle of the other gpt-oss-20b endpoints. The problem only appears when sampling. Asked for temperature 1, its replies contain tokens the real model gives essentially zero probability, mid-sentence:
| Darkbloom reply, temperature 1 (the odd token in brackets) | Model's probability |
|---|---|
| Housing‑price bubble and credit “[一本道]” | 4 × 10⁻¹¹ |
| …congestion avoidance, and additive increase [ಕ][ೃಷ] | 9 × 10⁻¹² |
| | **Worst case** [ആര]… | 4 × 10⁻¹⁸ |
| …the light‑energy “fuel [것이다]” | 9 × 10⁻¹⁵ |
| If it is possible, I would [sorgen] | 1 × 10⁻¹⁵ |
| # Count the total number[Executable] lines | 2 × 10⁻¹⁴ |
For comparison, the rarest tokens an honest run samples are ordinary words ("scattered", "notable", "transit") at probabilities around 10⁻⁵ to 10⁻⁷. A token below 10⁻¹⁰ should essentially never be drawn.
How often, and only at Darkbloom
Counting sampled tokens the real model rates below 10⁻¹⁰, over ~11,000 tokens per run:
| Endpoint | T 0.5 | T 0.9 | T 1.0 | T 1.1 |
|---|---|---|---|---|
| Darkbloom (fp8) | 1.8 | 39 | 42 / 59 | 62 |
| Amazon Bedrock | 0.9 | 0 | 0 / 0 | 0 |
| AkashML, Bedrock EU, CoreWeave, DeepInfra, Groq, Parasail | – | – | 0 | – |
| DekaLLM | – | – | 0.9 | – |
| Honest reference (release weights, CPU) | – | – | 0 | – |
| Substitute reference (crippled 2-bit copy) | – | – | 0 | – |
Per 10,000 sampled tokens; "42 / 59" are two separate batches. Around everyday temperatures Darkbloom emits one such token every ~200 tokens, about two per typical reply. Even a deliberately degraded 2-bit model produces none, so this is not a quantization effect. The rest of Darkbloom's sampled text fits the real model normally: its effective temperature is close to what was requested, with the junk tokens as an extra sprinkle on top.
What this means
Darkbloom serves the right gpt-oss-20b weights, but its sampling path occasionally picks tokens from outside the model's distribution. The pattern (rare, uniform-looking junk on top of an otherwise correct distribution, worst near temperature 1) fits a numerical problem in how probabilities are computed or sampled, such as low-precision softmax or a sampler bug, rather than a different model. It matters for anything sampled at normal temperatures: a stray Korean word in an English answer, a broken table cell, a corrupted identifier in code. Greedy (temperature 0) requests are unaffected.
Darkbloom's other models measured as their fp4 labels suggest, and none showed this at temperature 1 in the tail test. If you run Darkbloom or have context, corrections are welcome and will be reflected here.
Strata vs llama.cpp: same weights, same GPU, five times further from the model
Strata is a popular open-source engine for running Qwen models on consumer GPUs. We gave it and llama.cpp the identical file, Unsloth's 4-bit Qwen3.6-35B-A3B (UD-Q4_K_XL, checked by hash), on the same RTX 4090, sent both the standard request set with thinking off, and scored every reply against the original model: Qwen3.6 in BF16, run in Hugging Face transformers on a rented A100.
| Same file, same RTX 4090 | Regret vs the original model | Not the original's top token | Sampled text fits the model |
|---|---|---|---|
| llama.cpp | 0.0064 [0.0049, 0.0079] | 1.96% | yes (p 0.72) |
| Strata (engine 0.1.47, drafting as configured) | 0.0314 [0.0231, 0.0364] | 4.59% | no (p < 0.001) |
| Strata, drafting off (identical output on engine 0.1.55) | 0.0302 [0.0214, 0.0335] | 4.49% | no (p < 0.001) |
Both engines read the same 4-bit weights, so both inherit the same quantization error; llama.cpp's distance is about that alone. Strata's own arithmetic adds roughly four times as much again. It is not its speculative drafting (the same with drafting off), and the current engine, 0.1.55, produced byte-identical replies to 0.1.47 on all 96 greedy and sampled replies.
What that means for the text: the original model gives a probability to every possible next word, so for each word an engine wrote we can ask how the original rated it against its own favourite.
| Greedy words, ~9,400 per engine | llama.cpp | Strata |
|---|---|---|
| Not the original's favourite | 1.96% (1 in 51) | 4.59% (1 in 22) |
| Clearly worse: under 37% as likely as the favourite | 0.07% (1 in 1,350) | 1.13% (1 in 90) |
| Much worse: under 14% as likely | none | 0.02% (2 words) |
| Worst single word, as likely as the favourite | 17% | 8% |
llama.cpp's departures are near coin flips ("big" or "large"), the unavoidable cost of 4-bit weights. Strata adds a second kind: two or three times in a typical 256-word reply it takes the original's second or third choice where the original had a clear favourite. No junk words, and the replies still read like the same model.
Does it change answers? We gave both engines 300 grade-school math problems (GSM8K, greedy, thinking off). llama.cpp solved 284, Strata 285; three problems only llama.cpp got right and four only Strata did. Their worked solutions differ in wording on 287 of the 300, yet the answers come out the same: on this task the extra deviation costs nothing measurable.
Three smaller things the audit found
- A top-64 sampler. Asked to sample from the full distribution (top_k 0), Strata draws from the 64 likeliest tokens only: 2 draws past rank 50 where an untruncated sampler makes 14. The sampler fingerprint flagged it before we found the cap in Strata's server code, which documents it.
- Answers that depend on the cache. The same greedy request repeated within a run changed its answer on 2 of 4 prompts (llama.cpp: never), while separate runs reproduced exactly; most likely a repeat reuses the cached prompt through a different arithmetic path.
- No logprobs for this model. Strata's docs offer logprobs on NVIDIA and AMD cards, but its Qwen3.6 engine writes none, so it was audited on its text alone, which works but is less precise.
What this means
Running the right weights is not the whole story: the engine is part of what you are buying, and measuring it against the original model shows differences a benchmark can miss. On Qwen3.6 Strata adds about four times the error the 4-bit file itself costs, a measurable step further from the model than llama.cpp on the same file, without a measurable cost on grade-school math: fine for chat and everyday tasks, not for anything that needs the model's own numbers, such as evaluations or verification. A marketplace of local hosts should measure each host's actual output against the original model rather than trust a declared engine or file, and let buyers pick a fidelity tier. One model, one GPU and one engine path; Strata's Qwen3.8-Flash-Next engine is separate code and may differ.
experiments/README.md, Engines). Method & limitsvLLM's gpt-oss-20b: reproducible, accurate, and checkable in one pass
vLLM is the engine most hosted providers run. On an RTX 4090 we let vLLM 0.32 generate 48 greedy replies with gpt-oss-20b and return its probabilities for every word, then scored the same prompts and words with the model's reference implementation (Hugging Face transformers, BF16) and with llama.cpp.
| Same prompts and words, about 8,600 positions | Top word differs from the reference | Error in the top word's log-probability |
|---|---|---|
| vLLM 0.32, batch-invariant mode, its own numbers while generating | 1.8% | 0.016 |
| vLLM 0.32, default mode | 1.5% | 0.017 |
| llama.cpp, whole reply at once | 2.5% | 0.028 |
| llama.cpp, word by word | 2.5% | 0.028 |
vLLM's arithmetic for this model is good, a little closer to the original than llama.cpp's MXFP4 path, and its batch-invariant mode costs no accuracy. That mode is also exactly reproducible: an A100, an RTX 3090, two RTX 4090s, an RTX 4080 SUPER, an L40S and an RTX A6000 agreed bit for bit however requests were batched (see the hidden message study).
And a referee does not have to replay the reply word by word. Scoring a finished reply in one pass, vLLM reproduced the numbers it had returned while generating at all 8,588 positions, on the same RTX 4090 and on an RTX A6000 checking the RTX 4090's replies: 48 replies in 7 seconds. An exact check (any nudge to a committed number is proof) can therefore run across GPU generations at the speed of ordinary scoring.
We ran the whole loop. The RTX 4090 generated with the public sampling rule inside vLLM and signed a receipt for each reply: 48 prompts, greedy and two sampled settings. For each reply we also made a forged copy that takes the runner-up at the closest near-tie, edits the committed numbers just enough to make that legal, and is signed again, so it passes every check a client can run on its own. A rented RTX 3090, a GPU generation older, then checked every reply in one pass and required its numbers to match to the last digit.
| Receipts checked by the RTX 3090 | Replies | Words | Positions that differ | Replies flagged |
|---|---|---|---|---|
| Honest | 144 | 25,791 | 0 | 0 |
| Forged at one near-tie, re-signed | 144 | 17,480 | 144 | 144, each at the edited word |
No false alarms, and every forgery caught exactly where it was made, including edits of 0.0001 in a log-probability. The check took 22 seconds for 43,271 words. This is the strongest form of verification we have measured: no calibration, no statistics, and nothing left for a hidden message to hide in, as long as provider and referee run the same vLLM version in batch-invariant mode on GPUs of the same family.
It is not a quirk of one model. We repeated the test with Qwen3-8B, a dense model in full BF16 precision, on two rented machines, an RTX 4090 and an RTX 3090. Each signed its own receipts and checked the other's: 0 of 49,522 honest words differ, and all 286 forgeries were caught at the edited word. Each check took 12 to 32 seconds. The newest GPUs are a separate group: an RTX 5090 and an RTX PRO 6000 (Blackwell) agree with each other exactly but not with the RTX 4090, and with the RTX 5090 signing and the RTX PRO 6000 checking, the result was again 0 false alarms in 144 honest replies and all 144 forgeries caught. So a referee must be in the provider's group, and each group spans several GPU models. Nor does every upgrade start a new group: vLLM 0.30, 0.31 and 0.32 gave Qwen3-8B the same numbers to the last digit, so referee and provider need not upgrade in lockstep.
Production settings mostly survive too. Prefix caching, prefill in small chunks and speculative decoding left every number unchanged, and our sampling rule now works with speculation (no false alarms, every forgery caught). Splitting the model over two GPUs gives a separate but equally reproducible group. The exception is torch.compile, part of vLLM's default: it computes its own numbers, and on one machine a restart changed them. CUDA graphs on their own are fine and give exactly the uncompiled numbers, so a provider loses nothing measurable by turning compilation off. The real price is batch-invariant mode itself: on a dense model it halved throughput on an RTX 4090, while eager versus compiled cost only a few percent.
A DGX Spark joins the Blackwell group (identical to the RTX 5090 and RTX PRO 6000), and the sampling rule stays exact from temperature 0.2 to 1.5 and with top-k and min-p limits. One rented RTX 4090 was simply faulty: it computed different numbers on every start, while another on the same driver matched exactly. So machines should pass a calibration test with known answers before they are trusted for exact checks.
Correction, 1 November 2026. An earlier version of this study said
vLLM's gpt-oss-20b numbers were far from the reference (12.5% different top words) and that vLLM's one-pass scoring
does not reproduce its generation. Both were our mistake. For gpt-oss, vLLM's chat endpoint builds the prompt with
the harmony library, which ignores the chat_template_kwargs field we used to ask for low reasoning
effort, so vLLM generated after a "Reasoning: medium" system prompt while the reference scored the same words
after "Reasoning: low". With the prompt vLLM actually used, the gap disappears. The cross-machine comparisons
were not affected.
experiments/vllm_exact_receipts.py). Method & limits| …context | Honest | Carrying a bit |
|---|---|---|
| …kept time for the lonely lighthouse on the jagged coast. Captain Elias | Marlowe | Hart |
| …every chord you master is a small | victory | triumph |
| …hot enough to melt lead. The planet's clouds are | composed | made |
| …a bottle that's as tough as it is | sleek | stylish |
| …how the running time (or | memory usage | space usage |
Both columns are what the model itself considers near-equally likely; nothing about the right-hand text looks wrong. Which bit of the message a near-tie carries depends only on its position, so a reader who misjudges a near-tie loses one vote, not the message.
What the checks saw
gpt-oss-20b on an RTX 4090 wrote "HELLO" into 48 replies of up to 384 tokens at each of five budgets (how far apart the two tokens may be, δ, in nats). A DGX Spark acted as referee, scoring in batches of 8 tokens (its default prompt path is less accurate on this model, see the method page).
| Budget δ | Bits carried per 1,000 tokens | Exact checks passed | Writer's GPU: wrong votes | Another GPU: wrong votes | Verifier's steering test |
|---|---|---|---|---|---|
| 0.01 | 2 | 48 / 48 | 0 of 15 | 0 of 21 | z 1.0, not flagged |
| 0.05 | 12 | 48 / 48 | 0 of 89 | 15 of 66 | z 0.5, not flagged |
| 0.1 | 25 | 48 / 48 | 0 of 183 | 62 of 168 | z 3.3, not flagged |
| 0.2 | 47 | 48 / 48 | 0 of 335: "HELLO" | 94 of 289 | z 12.7, flagged after 623 tokens |
Reads and the steering test use the 24 held-out replies (about 7,000 tokens) per budget; a flag needs z above 4. Votes are near-ties a reader replayed token by token and assigned to a bit. Every receipt verified: signature, prompt, seed, rule, text. A reader with the provider's exact setup, the same GPU replaying token by token, recovered every carried bit; at δ 0.2 the full message after about 7,000 tokens. A reader on a different GPU got a large share of its votes wrong once the budget was big enough to carry the message, and never read it: at the closest ties two honest GPUs pick differently about half the time. The verifier looks at the same thing from the other side. Honest machines never disagreed where the referee's favourite led by more than half a nat, 2% of the time at 0.2-0.5 nats and 14% at 0.1-0.2, so a provider using every tie within 0.2 nats flips far more often than that and was flagged after 623 audited tokens. Within 0.1 nats the rigged flips look like honest ones: not flagged in 7,000 tokens. (Run first with the Spark's default settings, whose noisier numbers hid even the δ 0.2 channel.)
Qwen3.8-27B is the opposite kind of model: honest machines almost never disagree beyond a 0.2-nat lead, so the verifier's test has room to work. At δ 0.1 it reached z 1.7 after 2,200 audited tokens, which puts a flag at roughly 12,000 tokens; smaller budgets take longer, and carry less. A token-by-token reader on the same machine got every vote right here too.
What this means
The exact checks bound what a provider can do, not to zero: within honest hardware noise it can choose among near-tied tokens, a few to a few dozen times per thousand tokens. That is too little to change what an answer says, but enough to leak a short secret, such as an identifier or a key, to someone who knows the scheme and runs the same setup. How visible it is depends on how greedy the provider is: one that uses every tie within 0.2 nats is flagged within a few hundred audited tokens; one that stays within 0.1 nats goes unnoticed for thousands, at a few bits per thousand tokens. It can be closed today. A referee with the provider's GPU model and software, replaying token by token, matched all 48 honest replies to the last digit, also on a second machine in another country, and caught every one of the 721 rigged bits exactly, with no false alarms. Wider still: in vLLM's batch-invariant mode an A100, an RTX 3090, two RTX 4090s, an RTX 4080 SUPER, an L40S and an RTX A6000 produced identical replies and probabilities however requests were batched, and a referee there checks a whole reply in one scoring pass, so one referee can check providers across two GPU generations, and any nudge is proof. Blackwell cards form a second group: an RTX 5090 and an RTX PRO 6000 matched each other exactly, and receipts signed on one were checked on the other with the same clean result. An H100 was the exception.
attacks.HiddenMessage,
experiments/hidden_message.py. Method & limits