Every verdict this register publishes carries a SHA-256 and an invitation to recompute it without us. That invitation rests on one thing almost nobody tests: that the bytes we hash are the bytes you would produce from the same value. If canonicalization drifts, nothing crashes. The verdict simply becomes a number no one else can reach, and it keeps looking like proof. On 2026-08-23 we measured our own, against an independent implementation, and we did not pass.
We did not find this on our own. Federico Blanco Sanchez-Llanos published that his own signing path claimed RFC 8785 JCS and matched only 6 of 10 vectors from a real conformance suite, with Python's exponent formatting and code-point key ordering both diverging on edge cases. He swapped in a real JCS library and pinned the vectors as a permanent regression test.
A register that measures other people's conduct does not get to read that and move on. We run the same class of code, for a stronger claim, on a public promise. So we borrowed the question and measured ourselves. The finding below is ours, not his.
Being written in JavaScript did most of the work for us, and that is luck, not virtue. RFC 8785 orders object keys by UTF-16 code unit, which is exactly what JavaScript's default sort already does, and it specifies ES6 number formatting, which is exactly what JSON.stringify already does. The same routine hand-written in Python misses on both. That is the divergence Federico hit, and we were never exposed to it.
The vector we failed was the one where luck runs out.
input {"huge": 1e400} ours {"huge":null} and it returned a hash over that, without comment RFC 8785 refuses no JSON form exists for a non-finite number
1e400 parses to Infinity. Our canonicalizer handed it to JSON.stringify, which turned it into null, and then we hashed the altered value and published the result as something you could reproduce. You could not. Two different inputs could reach that same hash, and an independent verifier doing it correctly would refuse where we produced a confident number.
Nothing failed loudly. That is the whole problem. A measuring instrument that breaks silently is worse than one that breaks.
The canonicalizer now declines a non-finite number rather than coercing it, and the refusal is reported as a statement about us, not about the server being measured. A withheld hash appears as canonicalization: "refused" with the reason attached. We do not blame a server for a shape we chose not to canonicalize.
$ node tools/jcs_conformance.mjs pass V01 numbers precision and exponent pass V02 key order UTF-16 vs codepoint pass V03 string escapes ... pass V13 non-finite === 13/13 conformant ===
Every record ends with a note telling you how to recompute its hash. It used to say: JSON.stringify the remainder in this key order. That is a JavaScript idiom, not an instruction. So we took one real production record and tried the four ways a competent stranger would actually reach for it.
| attempt | result |
|---|---|
json.dumps(obj), straight | different hash |
| compact separators only | different hash |
compact separators and ensure_ascii=False | reproduces |
the above plus sort_keys=True | different hash |
One in four. The hash was always honest; the instructions were not sufficient to reach it. Anyone outside JavaScript who tried, failed, and reasonably concluded we were bluffing. The note now names the exact serialization in both languages, because a promise of reproducibility that only one runtime can act on is not a promise.
JavaScript JSON.stringify(record) Python json.dumps(record, separators=(',',':'), ensure_ascii=False)
What this page said: that in JavaScript an integer past 2^53 is rounded inside JSON.parse before our code can see it, that nothing downstream can tell it happened, and that the case was therefore undetectable for us. We wrote it into this page, into the gate's machine-readable spec, and into a comment we were about to publish. We called it the missing sixty-fourth.
It was wrong. Someone read it and asked the obvious question: is it really impossible, or does it just look that way? It looks that way. JSON.parse hands the original source text of every literal to a reviver, so the rounding is visible after all. The value is lost; the evidence that it was lost is not.
JSON.parse(text, function (k, v, ctx) {
// ctx.source is the literal exactly as it arrived
if (typeof v === "number" && /^-?\d+$/.test(ctx.source)) {
if (BigInt(ctx.source) !== BigInt(v)) /* it was rounded */
}
return v;
});
Note which direction the error ran. It did not make us look worse. It excused us from work, and it did it in the vocabulary of humility, which is the hardest kind of false claim to catch, because it sounds like the opposite of a boast.
Responses are read as text and parsed with a source-aware reviver. An integer literal outside the IEEE-754 safe range is detected from what arrived on the wire rather than from the rounded number, and the surface carrying it has its fingerprint withheld instead of published. RFC 7493, the profile RFC 8785 builds on, excludes such integers for exactly this reason: a runtime with arbitrary-precision integers reads them as a different value, so no hash over them is reproducible across languages.
Every verdict now reports unsafe_integer_scan as one of three things, and the third is not the same as the second:
| value | meaning |
|---|---|
clean | we looked, and there were none |
found | we looked, found one, and withheld the fingerprint |
unavailable in this runtime | we could not look. Not the same as none, and never reported as none. |
Thirteen vectors, each an input and the exact bytes a conforming implementation must produce, or a refusal where no JSON form exists. Two of them (V02 and V11) are the ones that catch code-point key ordering while every other vector still passes. If you publish hashes over JSON, these are worth ten minutes of your time, whatever you think of us.
Public domain, no attribution required, no signup, no tracking on this page.
# no dependencies, exits non-zero on any divergence
curl -sO https://shield.the-horizons-innovation.com/verify-directory/conformance/jcs_vectors.json
node jcs_conformance.mjs
Vectors test a library. They do not test the path your code actually takes over the wire, which is where our own failure lived. So there is a permanent endpoint in production whose tool manifest violates RFC 7493 deliberately: it declares the integer 9007199254740993, which no IEEE-754 double can hold. Python reads it exactly. JavaScript reads 9007199254740992. Any hash taken over it disagrees across languages, which is precisely why a fingerprint must be withheld rather than published.
# point our gate at it and watch it refuse to publish a hash curl -s -X POST https://gate.horizonshield.dev/check \ -H 'content-type: application/json' \ -d '{"endpoint":"https://gate.horizonshield.dev/testbed/i-json/mcp","allow_tool_call":true}' # or point your own canonicalizer at it, and canonicalize the bytes, not the number curl -s -X POST https://gate.horizonshield.dev/testbed/i-json/mcp \ -H 'content-type: application/json' -d '{"method":"tools/list"}'
It is free, stores nothing, and is happy to be used against us. A retraction nobody can test is just a second claim.
Condition 07 is live. Every verdict now carries a top-level canonicalization block saying whether the surface that server declares can be canonicalized at all, which is the same as asking whether anyone other than us could ever catch a silent change to it.
It is a disclosed measurement, not a pass or a fail, and it never turns a row red. We published that promise on this page before the condition existed, and the implementation keeps it: the value is computed after the verdict is decided and cannot reach it. Almost nothing in the field does this correctly today, so a threshold would only condemn.
// in every verdict, alongside the checks "canonicalization": { "condition": "07", "measured": true, "canonicalizable": true, "scheme": "rfc8785-jcs", "verdict": null, "gate_self_conformance": { "vectors": 13, "passing": 13, ... }, "known_limitation": "integers past 2^53 are rounded inside JSON.parse before we see them" }
The register measured itself first, published the failure before the fix, promised the condition would not punish anyone, and then implemented it that way. Kept in that order, it means something. In any other order it is marketing.