Assurance — what we verify, and what we cannot

The claim

Statbook never claims to be “100% accurate.” The claim is: traceable, corroborated, re-verified, versioned, signed — and when wrong, detectably and briefly wrong, with a published correction trail. No indemnity is offered at any tier.

How a value earns “corroborated”

Every figure declares which legal instrument governs it (Source A, legislation.gov.uk) and which official guidance page states it (Source B, gov.uk). The two are fetched and extracted by isolated passes that share no state; only when both extractions match can a value carry the corroborated status — the build system is physically unable to emit it otherwise. Mismatches ship as disputed or not at all.

The pipeline, in order

  1. Fetch — both official sources per figure; raw snapshots stored with content hashes.
  2. Extract — two isolated passes that share no state, one per source.
  3. Gate 1 · corroboration — A must equal B; mismatches ship as disputed or not at all.
  4. Gate 2 · invariants — cross-figure identities and date-range checks; any failure fails the whole build.
  5. Gate 3 · human diff review — every value change reviewed against source excerpts before release.
  6. Sign & publish — manifest with per-record hashes, minisign-signed, committed to the public releases repository.

Source independence — the honest statement

Source B is HMG's own interpretation of the legislation, produced downstream of it. Corroboration therefore catches our extraction errors, staleness, and drift — it does not catch an error both sources share, nor resolve genuine ambiguity in the instrument itself. A-vs-B mismatch most commonly means gov.uk hasn't updated yet (early April) — exactly what the gate must catch.

In plain terms: corroboration is a strong net for our mistakes, not a guarantee the government's own documents are right. An error shared by both sources, or genuine ambiguity in the instrument itself, passes through by design and is caught only by the corrections protocol when surfaced.

Assurance by source type

The promise “every figure traceable to primary legislation” is scoped to act/si source types. Other types are published with their assurance level explicit — honesty as a feature, not a broken promise.

source_typeExamplesTwo-source gate Assurance level published
actAMAP rates (ITEPA 2003 s.230(2))YesFull
siIncrease of Limits Orders, NMW regs, uprating ordersYesFull
presidential_guidanceVento bands (no SI exists; judiciary.uk PDF, URL rotates yearly — each snapshot archived; Crown/OGL via site-wide notice)Degraded (sole publisher + reproductions)Labelled
resolutionScottish and Welsh income tax rates (a rate resolution of the Scottish Parliament or the Senedd — primary law for the rates, published as an agreed motion and never on legislation.gov.uk)Yes — the motion as agreed, against the devolved government's rates pageFull
administrativeHMRC Advisory Fuel Rates and National Insurance category letters (HMRC's own creation; no instrument prints them, and the page is overwritten quarterly)Where a second official page prints the same table; otherwise no — single source, honestly labelledLabelled
statisticalThe RPI All Items Index (ONS series CHAW) that SI 2009/470 reg 3(1) names by title and the student-loan repayment thresholds are computed from — the statute states the formula and can never state the indexNo — one publisher, named in the instrument; a second page printing the same index is a copy of it, not an independent readingLabelled
computedLate-payment interest (SI formula × live BoE base rate)Formula yes; value computedLabelled + formula shown
derivedMax redundancy payment (20 × 1.5 × cap)Derivation from corroborated inputsLabelled + derivation shown

April and Budget weeks — “guidance confirmation pending” is the system working

On the day a new instrument is made, gov.uk typically hasn't updated yet. The headline new figures therefore cannot pass corroboration on day one — and the gate refuses to claim corroboration it doesn't have. Promoted figures publish as Primary source only with guidance confirmation pending, and nightly re-verification auto-promotes each one to Corroborated the day gov.uk catches up — leaving a public, dated promotion trail. If you see that badge in early April, you are watching the gate work in public.

Re-verification and corrections

Verify a record yourself — no trust in us required

  1. Take any record from the API, e.g. /v1/figures/employment.redundancy.weekly_pay_cap.json — the receipt.record_hash field states its SHA-256.
  2. Recompute it: serialize the figure object as UTF-8 JSON (2-space indent, non-ASCII preserved, trailing newline) and hash it.
  3. Check the same hash appears in the release manifest under record_hashes.
  4. Verify the manifest's minisign signature against the published public key, and compare the manifest with the copy in the public statbook-releases git repository — history rewrites there are detectable by any cloner.
  5. Follow the per-source citations: every uri is the document its role names — the official publisher's, except a secondary source, which is an independent reproduction used only to corroborate a figure whose publisher is the sole one (see the source-type table below) and is labelled as such on the record. content_hash pins the exact bytes we extracted or verified from. Where those bytes are a different representation than the cited page — legislation citations point at the human-readable page while extraction reads the as-made data.xml — content_hash_of names the exact URL the hash pins. Sources whose documents we have not yet archived carry content_hash: null rather than a hash we couldn't back.

A self-contained reference implementation is published at /tools/verify_receipt.py — a single stdlib-only Python file you can download, read and vendor.

The signing key

Every release manifest is signed with this minisign public key (key ID 12D5059844B2CD8F). The same key is published in every API response under meta.signing and in the statbook-releases repository README — cross-check all three so no single source can substitute a key of its own.

Save it as minisign.pub and verify any manifest offline:

untrusted comment: minisign public key 12D5059844B2CD8F
RWSPzbJEmAXVEvTqYZ8O9iHuOcOUii5lCNZM5t9bdoQh4ZnwKsxk/+bd
minisign -Vm manifest.json -p minisign.pub