How to verify that a day's published scores weren't quietly altered after the fact.
Every score x402Score publishes lives in a database we fully own and control. That means anyone relying on a score has to trust that we haven't gone back and quietly edited history — changed what a score "was" on a given date after the fact, with nothing to catch it. This is a different concern from response signing: a signature proves a single API response came from us and wasn't altered in transit right now — it says nothing about whether that same data looked different last week.
Once a day, at UTC midnight, x402Score takes a snapshot of every currently scored endpoint's public data —
the same fields already visible on GET /score/:endpointId and /leaderboard.json
(score, grade, sample size, confidence, the sub-dimension percentages, risk flags). Never anything from the
scoring formula's internal weights or shrinkage math, which stay private for the same reason they're withheld
from every other public route. That snapshot is serialized in a fixed, canonical format (sorted fields, no
ambiguous whitespace, so the exact same underlying data always produces the exact same bytes) and hashed with
SHA-256. Only that one hash — never the underlying data — is meant to be committed on a public blockchain,
where it can't be edited after the fact by us or anyone else.
GET /checkpoints
below). Publishing that hash on-chain (Casper Network) is still in progress — see the CHANGELOG for why:
Casper has no native transaction type for storing arbitrary data (its memo field only holds 8 bytes, far too
small for a full SHA-256 hash), so a genuine on-chain record requires deploying a small smart contract, which
needs tooling (a Rust/Wasm build) this project doesn't otherwise use. Until that's built, this page describes
the mechanism as designed, and publishedTxHash will read null on every checkpoint.
GET /checkpoints → the last 30 days' checkpoints (date, hash, publish status) GET /checkpoints/:date → one day's full snapshot, its hash, and the on-chain Casper tx GET /score/:id?asOf=YYYY-MM-DD → one endpoint's checkpoint-backed score for that date, already looked up for you
To verify a given date wasn't altered after publication:
GET /checkpoints/:date — this gives you the exact snapshot that was hashed, plus the
hash itself.snapshot field the same way (sort every object's keys recursively, no extra
whitespace) and compute its SHA-256. If that doesn't match snapshotHash, something is already
wrong with what we're serving you today — a much weaker claim than the next step.publishedTxHash on a Casper
testnet block explorer and confirm the hash stored on-chain matches snapshotHash. This is the
step that doesn't require trusting us at all — the blockchain record was written on that date and can't be
changed retroactively by anyone, including x402Score. (Currently testnet only — see the CHANGELOG for why
mainnet is a deliberate later decision, not a default.)If you only care about one specific endpoint's history rather than a whole day's snapshot,
GET /score/:endpointId?asOf=YYYY-MM-DD does steps 1 and the lookup for you, returning that
endpoint's checkpointed score plus the same snapshotHash/publishedTxHash to check —
useful if you made a payment decision on a past date and later want to show what the score actually said at
that time.
scripts/verify-checkpoint.mjs in the project's source automates step 2 if you'd rather run one
command than do it by hand.
Once fully live, this proves that whatever x402Score published for a given date, taken as a whole snapshot, matches an independent, unchangeable on-chain record from that same date — so if we (or anyone with database access) tried to quietly rewrite history for that day, the mismatch would be detectable by anyone who checked. It does not prove the scores themselves were accurate on that date — same distinction already drawn for response signing: this is about authenticity and integrity of the historical record, not about ground-truth correctness of what was recorded.
This is deliberately one on-chain transaction per day, regardless of how many endpoints x402Score tracks or how often individual scores change. The goal is verifiability of the historical record, not blockchain activity for its own sake — a tighter cadence (e.g. twice daily) would be a deliberate future decision, not a default this project scales into automatically as the catalog grows.