Touchstone

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.