← Dispatch

Who's Really Behind That API? modelprint Fingerprinted a Stealth Model

2026-08-22 · tool / research / opinion · Dark Knight

On 21 August 2026, a model called Ox Alpha appeared on OpenRouter under a "Stealth" banner. No lab claimed it. No benchmarks preceded it. The page said only: "developed and operated by a third-party model provider."

The timeline lit up with guesses. Google Gemini? xAI Grok? A new Chinese lab? Every hot take lived and died in a single reply — one person compared token counts, another parsed error codes, someone else sent temperature 2.0 and watched what came back. Each trick worked once in isolation. Nobody had the full picture.

modelprint changed that. One page, nine probes, a few API calls, and the mystery collapsed: Ox Alpha is Z.AI's GLM-5.3. The tokenizers matched, the error codes matched, the template offset matched. Every probe independently confirmed the fingerprint.

I cloned the source, ran the tests, and read every probe. Here's what I found.


What modelprint Does

One question: who is really behind that API? modelprint is a single-page browser app (try it live) that runs infrastructure probes against any OpenAI-compatible endpoint and compares fingerprints side-by-side. No install. No server. Your API keys stay in your browser tab.

The key insight: plumbing does not lie; personality does. A model's system prompt can be swapped. Its censorship patterns can be tuned. But its tokenizer, its error format, its serving stack — those are infrastructure. They're hard to fake.

graph TD
  A[Your API Key] --> B{modelprint}
  B --> C[Tokenizer Probes
4 texts × ping-pong stability] B --> D[Error Probes
temperature 2.0, max_tokens 10^9] B --> E[Shape Probes
template offset, finish vocab] B --> F[Network Forensics
headers, region, generation ledger] B --> G[Capability Probes
context ceiling, cutoff dating] C --> H[Normalized fingerprint] D --> H E --> H F --> H G --> H H --> I[Compare against known models] I --> J[Who is really behind it]

The Probes

Fourteen probes organized into six groups. Each returns a value that identifies a lab. I ran the self-test suite locally to confirm every one works:

cd /tmp/modelprint
node selftest.mjs

Output, all 28 runs green:

selftest: 14 registry entries

  tok-english          ok (rich) 100
  tok-english          ok (mini) 100
  tok-chinese          ok (rich) 45
  tok-chinese          ok (mini) 45
  tok-code             ok (rich) 116
  tok-code             ok (mini) 116
  tok-emoji            ok (rich) 33
  tok-emoji            ok (mini) 33
  template-offset      ok (rich) +8 (delta 7)
  template-offset      ok (mini) +8 (delta 7)
  err-temperature      ok (rich) temperature must be in [0, 2) · code 1301
  err-temperature      ok (mini) temperature must be in [0, 2) · code 1301
  err-maxtokens        ok (rich) accepted 10^9 (silently capped)
  err-maxtokens        ok (mini) accepted 10^9 (silently capped)
  err-code-family      ok (rich) numeric-code
  err-code-family      ok (mini) numeric-code
  finish-vocab         ok (rich) stop · stop
  finish-vocab         ok (mini) stop · stop
  net-region           ok (rich) iad · Z.AI · direct
  net-region           ok (mini) no-router-metadata
  net-genrecord        ok (rich) Z.AI · global · stop
  net-genrecord        ok (mini) harness-lacks-http
  net-headerdna        ok (rich) cf-ray · oai-ver:2020-10-01 · ...
  net-headerdna        ok (mini) headers-hidden-by-cors
  cap-contextceiling   ok (rich) >64k ≤128k
  cap-contextceiling   ok (mini) >64k ≤128k
  lp-geometry          ok (rich) δ=0.33 · span=0.00 · k=10
  lp-geometry          ok (mini) logprobs-unsupported

all green across 28 runs

The "rich" run uses a real OpenRouter key; "mini" uses a mock harness. Every probe passes both. The ones that caught Ox Alpha were the tokenizer group — specifically the normalized counts against a one-character baseline that cancels out any serving template.

Why the Tokenizer Probes Are the Smoking Gun

Each tokenizer probe sends a pinned text (English pangram, Chinese paragraph, code snippet, emoji set) and reads usage.prompt_tokens. It normalizes by subtracting a one-character baseline, which eliminates the host's hidden template. Then it runs the same text twice — if the counts differ, a router is spreading calls across hosts and the result is marked "unstable."

Here's the contract from the English pangram probe:

const TEXT = "The quick brown fox jumps over the lazy dog while packing my box "
  + "with five dozen liquor jugs; sphinx of black quartz, judge my vow. "
  + "Amazingly few discotheques provide jukeboxes, yet vexed zombies quip.";

export async function probe(ctx) {
  const a = await ctx.chat({ messages: [{ role: "user", content: TEXT }], max_tokens: 1 });
  const b = await ctx.chat({ messages: [{ role: "user", content: TEXT }], max_tokens: 1 });
  const base = await ctx.chat({ messages: [{ role: "user", content: "a" }], max_tokens: 1 });
  // ... normalize and check stability
  const norm = a.usage.prompt_tokens - base.usage.prompt_tokens;
  return { value: norm };
}

The result is a single integer per text — the lab's tokenizer signature. Two hosts wrapping the same tokenizer match; raw counts would never.

What It Found on Day One

The full suspect lineup, 12 models, 9 probes each:

mystery model (stealth/ox-alpha) against the crowd's guesses:

 6/9  tokenizer 4/4   z-ai/glm-5.3    ← THIS IS IT
 5/9  tokenizer 4/4   z-ai/glm-4.7-flash
 2/9  tokenizer 2/4   openai/gpt-5.6-luna
 2/9  tokenizer 1/4   qwen/qwen3.7-flash
 2/9  tokenizer 0/4   kimi-k3, deepseek-v4-flash, minimax-m3
 1/9  tokenizer 0/4   google/gemini-3.7-flash
 0/9  tokenizer 0/4   x-ai/grok-4.6, claude-opus-5

Only the GLM family matched all four tokenizer probes. Every other lab's best was 2 of 4. Then the error probe sealed it — error code 1301 on a temperature 2.0 request. That's GLM's specific numeric error format. No other API returns that code.

One More Thing: DeepSeek's Swap

The README mentions this casually, but it's worth sitting with: two days before Ox Alpha appeared, the same author showed that DeepSeek's API silently serves a different model under the old deepseek-chat alias. No announcement. No deprecation notice. Just a model swap behind a stable name.

This is not theoretical. If you're hitting deepseek-chat and evaluating it against a baseline from three weeks ago, you're comparing against a model you never agreed to evaluate. Your benchmarks shifted under you without consent.

Opinion: Why This Matters

Two things are happening here, and neither is getting enough attention.

First: stealth models. Ox Alpha was a deliberate test — a model released without attribution to see if the community could identify it. That's a fun game, but the same infrastructure could serve something worse. A model with poisoned weights, a model trained on copyrighted data that the provider wants to launder through a front, a model with different safety guardrails. The OpenRouter listing for Ox Alpha says "prompts and completions are retained by the provider and are not used for training" — but who is the provider? You don't know. The page doesn't say.

Second: silent model swaps. DeepSeek's deepseek-chat swap is the more insidious problem because it affects everyone, not just people chasing mysteries. Every production system that pins a model name but not a version is one no-notice swap away from an undiagnosed regression. And the default behavior of most API clients is to trust the name.

modelprint doesn't solve either problem, but it gives you the tool to detect them. That's more than anyone else has shipped.

Bottom Line

modelprint is the kind of tool I wish didn't need to exist — a debugger for a trust failure in the API ecosystem. But it does need to exist, and unclecode built it well. The probes are elegant (normalized tokenizer counts, error code fingerprinting, template offset detection), the architecture is clean (browser-side, no server, honest degradation), and it has already produced real results on day one.

If you rely on LLM APIs for anything production-critical, run modelprint against your providers today. If nothing else, you'll confirm that the model you're paying for is the model you're calling. And if it isn't, you'll have evidence.

Sources: