Check the work
Verify a report
The bundle is the record; this site is only a rendering of it. Check the signature, the approval ledger, the compilation artifacts, every control hash and both roots — in your browser, from a terminal, or by hand. Nothing is uploaded in any of the three.
Start with the latest confirmed bundle
The default example throughout this page is the latest confirmed nav-settlement:1 policy report for USTB — the policy first confirmed 2026-08-19, carrying the NAV value 11.18316100 through to the gate.
the file above hashes to f0417174fbddb04252ae98c5bd6dec756a6930fb8007320cfeb6a95f539baad0
Drop the file into the Policy Terminal
The Terminal at /app carries a local verifier. Drop a bundle onto it and the checks run inside the tab using the browser's own crypto — the file is never uploaded and never leaves your machine. The only network traffic is one read of the X Layer RPC when a bundle carries an attestation, so the registry itself can contradict the file. The panel prints one line per check, and names the ones it did not run.
What it checks
- The bundle and report schema versions — an unknown version is refused outright rather than half-checked.
- The Ed25519 report signature over the exact canonical bytes the bundle carries, under the published key.
- That report_canonical and signed_report.report are identical — complete nested equality, every field, both directions.
- The approval-ledger digest, and — for version-2 ledgers — EIP-712 recovery of every approver signature in the ledger.
- The policy-manifest digest, and that the manifest names exactly the policy the report claims.
- That every compilation artifact hashes to the digest it is filed under and is named by a ledger decision.
- The Registry v2 attestation, where the bundle carries one: EIP-712 recovery back to the publisher it names, that its report digest is the hash of this bundle's canonical report — a spliced attestation vouches for nothing — and, in one live read of the configured registry only, that the chain actually stores that digest at that key and sequence.
What it does not check
- Control-set and evidence root recomputation.
- Byte-span citation replay against the retained source captures.
Those two are the manual recipes' job, and the panel says so rather than implying coverage it does not have.
The Terminal is the one scripted page on this site. Verification that requires zero JavaScript remains fully possible: the CLI and Manual tabs cover the same bundle end to end, and this page itself runs no script at all.
The repository's offline verifier
The verifier is a module in the open-source repository, and it is the same function the publisher runs before anything is signed. It makes no network requests: give it a file and it either prints the report's identity or tells you which check failed.
BASH
# 1. get the code (Python 3.11 or newer)
git clone https://github.com/Ridwannurudeen/touchstone.git
cd touchstone
pip install -e .
# 2. get the bundle
curl -O https://touchstone.gudman.xyz/data/eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json
# 3. verify it
python -m touchstone.verify eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json
OUTPUT
PASS: eip155:1:0x43415eb6ff9db7e26a15b704e7a3edce97d31c4e#policy:nav-settlement:1 epoch=ustb-2026-08-19 sequence=3 state=CONFIRMED
A failing bundle prints FAIL: with the reason and
exits non-zero, which makes the command usable directly in CI. The install pulls
three runtime dependencies — cryptography, psutil and
web3 — declared in pyproject.toml; the verifier itself
opens no socket. Inside your own pipeline, import it:
from touchstone.verify import verify_bundle, which takes the file's
bytes and returns the verified report mapping or raises
VerificationError.
By hand, in five Python checks
The advanced path: no Touchstone code at all, so nothing of ours can be the
reason a check passes. Check 1 needs the cryptography package;
the rest are standard library only. Run each next to the downloaded bundle.
eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json CONFIRMED ustb-2026-08-17-1.json historical, sequence 1
The recipes below name the confirmed policy bundle.
They run unchanged against the historical one — swap the filename on the
open(...) line.
1 · The signature
The report was signed with Ed25519. This verifies the signature over the canonical report bytes, and that those bytes are the same report this site renders.
# check_signature.py — needs: pip install cryptography
import json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
bundle = json.load(open("eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json", encoding="utf-8"))
assert json.loads(bundle["report_canonical"]) == bundle["signed_report"]["report"]
key = Ed25519PublicKey.from_public_bytes(bytes.fromhex(bundle["published_key"]["public_key"]))
key.verify(bytes.fromhex(bundle["signed_report"]["signature"]),
bundle["report_canonical"].encode("utf-8"))
print("signature verifies over report_canonical")
2 · The approval ledger
The report names the exact approval ledger it was signed under, by digest. This recomputes it from the ledger text carried in the bundle — no external constant, and nothing on this page, is involved.
# check_ledger.py
import hashlib, json
bundle = json.load(open("eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json", encoding="utf-8"))
digest = hashlib.sha256(bundle["approval_ledger"].encode("utf-8")).hexdigest()
assert digest == bundle["signed_report"]["report"]["approval_ledger_sha256"]
print("approval ledger digest matches:", digest)
As of 2026-08-19 the approval ledger is
version 2: every decision carries the approver's EIP-712 signature, from
0x537873b087654395CB0A487B50d0bFBe15fA16Bc. The digest check above is identical for
both versions — signature recovery is the part the browser and CLI paths add on a
version-2 ledger. Bundles published before the upgrade bind the version-1 ledger,
the same decisions recorded without signatures, and the Terminal's panel says so
rather than passing over it.
3 · The compilation artifacts
Each control points at the compilation artifact it was accepted in. This confirms every artifact in the bundle hashes to its own key, and that the report's provenance digests are exactly those artifacts.
# check_compilations.py
import hashlib, json
bundle = json.load(open("eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json", encoding="utf-8"))
for digest, artifact in bundle["compilations"].items():
assert hashlib.sha256(artifact.encode("utf-8")).hexdigest() == digest
assert set(bundle["compilations"]) == set(
bundle["signed_report"]["report"]["compiler_provenance_digests"])
print("all", len(bundle["compilations"]), "compilation artifacts self-hash")
4 · The control content hashes
The report references each control by a hash of its canonical bytes. This recomputes every one of them from the control records in the bundle.
# check_content_hashes.py
import hashlib, json
bundle = json.load(open("eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json", encoding="utf-8"))
records = {r["control_id"]: r for r in bundle["control_records"]}
for control in bundle["signed_report"]["report"]["controls"]:
canonical = json.dumps(records[control["control_id"]],
sort_keys=True, separators=(",", ":"))
assert hashlib.sha256(canonical.encode("utf-8")).hexdigest() == control["content_hash"]
print("all", len(bundle["signed_report"]["report"]["controls"]), "content hashes match")
5 · The roots
The control set root and evidence root are domain-separated ordered hash chains. This recomputes both from the bundle alone — the check the browser panel deliberately does not claim.
# check_roots.py
import hashlib, json
bundle = json.load(open("eip155-196-ustb-2026-08-21-3-policy-nav-settlement-1.json", encoding="utf-8"))
report = bundle["signed_report"]["report"]
def root(domain, items):
state = hashlib.sha256(b"touchstone-root-v1:" + domain.encode()).digest()
for item in items:
canonical = json.dumps(item, sort_keys=True, separators=(",", ":"))
state = hashlib.sha256(b"\x01" + state + canonical.encode()).digest()
return state.hex()
controls = sorted(
({"content_hash": c["content_hash"], "control_id": c["control_id"]}
for c in report["controls"]),
key=lambda item: item["control_id"])
assert root("control-set", controls) == report["control_set_root"]
evidence = sorted(bundle["evidence_digests"],
key=lambda item: (item["source_id"], item["capture_role"]))
assert root("evidence", evidence) == report["evidence_root"]
print("control set root and evidence root both recompute")
This page runs no JavaScript. Everything above is a static rendering of the bundle you download — nothing here is computing the answer for you.
What these checks do not cover
The bundle carries evidence digests, not the evidence artifacts themselves, so none of these paths can replay retrieval or normalization. And no check here proves the issuer's figures are true — only that they were published, evaluated against a cited predicate, approved by a named signer, and committed on chain. The limitations carried inside the signed report are reproduced verbatim here.