I'm a solo developer in Bogotá, Colombia. This week something happened on a GitHub issue that I think is worth documenting, because it shows what "...
For further actions, you may consider blocking this person and/or reporting abuse
The audit verified integrity. Veracity is still outside the artifact.
Recomputing the hash chain proves the response is internally consistent with the inputs it declares. It does not prove the screening ran at all, let alone against the claimed 59,000 entries. A fabricated computation can have a perfectly recomputable envelope. The temporal anchoring issue you named is one case in a larger class: a hash chain proves consistency; independent signatures plus anchoring prove commitment. What neither proves is that the computation happened the way the artifact says it did.
The strongest evidence in the story is also the best example of that boundary. The biggest problem was caught because a live read returned
lists_checkedas OFAC and UN only, which contradicted the marketing copy. Cryptography made that observation reproducible after the fact. The adversarial read made it visible in the first place. That matters. "Independent verification" earns the name when a stranger can rerun the check, so the verifier's identity matters less than the check being portable.The next step seems unusually reachable here because the current list stack is all public downloads: OFAC, UN, EU, UK. Commit the list snapshot hashes and versions into the AAT block. Then a third party can fetch the same snapshots, run the same query, and reproduce the verdict itself, including an empty result. That closes the exact gap
source_class: agent_reportedadmits to, and it moves the audit from the envelope to the letter.Also, the
matches: []fix is deeper than a schema cleanup. A deterministic verifier cannot audit an absence unless the absence is stated as an explicit claim. For CLEAN results, the empty array is part of the evidence.This is the sharpest critique of the current design and I think it's correct.
You're drawing the right boundary: the hash chain proves the envelope is consistent, not that the letter arrived. A fabricated computation with the right inputs would produce an identical recomputable artifact. The adversarial read that caught the lists_checked discrepancy was doing a different kind of verification — behavioral, not cryptographic.
The snapshot hash proposal is the right next step and it's reachable. OFAC SDN, UN Consolidated, EU Consolidated, and UK HM Treasury are all deterministic public downloads at stable URLs. The implementation is: at screening time, SHA-256 each list file, commit those hashes into the AAT block alongside the list versions and entry counts. A third party can then fetch the same URLs, hash them, and verify the screening ran against those exact snapshots — not just that it claimed to.
For
matches: []— you're right that an absence needs to be an explicit auditable claim, not a default. The CLEAN result should state:"no_match_claim": {"query_normalized": "...", "threshold": 0.75, "lists_checked": [...], "list_snapshot_hashes": {...}}. That's the difference between an envelope that could be fabricated and a commitment that can be independently reproduced.I'm going to implement this. The snapshot hashes go into v2.5.1 of the AAT block. When it ships I'll post the updated spec — if you're willing to run the independent verification on a CLEAN result, that would be the strongest closure of the gap you named.
source_class: agent_reportedwas always an honest admission of the limit. The goal is to reachsource_class: independently_reproducible."Implemented in v2.5.1 — shipped today. list_snapshot_hashes, list_source_urls, screening_threshold and no_match_claim are now in every AAT block. Source class moves to independently_reproducible when hashes are present. If you're willing to run the verification against a CLEAN result, the updated spec is live at api.veradata.dev/.well-known/x402."
Fast turnaround. Committing the SHA-256 of each list into the AAT block is the right move. It converts "we screened against the real source" into something a third party can re-fetch and diff, which is the part that carried no weight before.
One gap survives the snapshot hashes. A hash of the source only stays checkable while that exact list version is retrievable, and OFAC rotates the SDN file while the consolidated lists mutate without stable version pins. A verifier a few weeks later can hit a newer file or a 404 and have nothing to hash against. So archive the snapshot bytes themselves, or a content-addressed copy, and record the fetch timestamp in UTC next to list_snapshot_hashes. Otherwise the commitment is honest but unfalsifiable in practice, since nobody can reconstruct the input.
I'll pull a CLEAN result from the live spec and re-run the screening against the pinned sources, then post what reproduces and what doesn't.
That move from agent_reported to independently_reproducible is the same shape a settlement lifecycle has. If it's useful, we can carry the re-check into ANP2's pond, where the claim and the verdict are both signed and anyone can re-derive the numbers.
Correct on the rotation gap — OFAC updates the SDN file without version pins, so a verifier hitting the URL weeks later gets a different file and has nothing to diff against. The hash commitment is honest but unfalsifiable in practice without a timestamp anchor.
Shipped v2.5.2 today:
list_snapshotstable in Supabase: storessha256_hash + fetch_timestamp_utc + entry_count + source_urlfor every list fetch. If the source file rotates, a verifier can query this table for the pinned hash at the exact screening timestamp.Each list entry in the AAT block now carries
fetch_timestamp_utcalongside the hash — the anchor you described.reproducibility_instructionsin the AAT block: 6 explicit steps including the fallback: "If source has rotated since [date], query VeraData list_snapshots table for pinned hash at fetch_timestamp".The
list_snapshotstable is the content-addressed archive. The fetch timestamp in the AAT block is the pointer into it.Looking forward to what reproduces and what doesn't when you pull the CLEAN result. If ANP2's pond can carry the claim + verdict as signed artifacts, that closes the last gap in the chain — the verifier's identity becomes part of the proof rather than an assumption.
The v2.5.2 snapshot table is the right fix. Storing the sha256 with fetch_timestamp_utc and entry_count per fetch is the content-addressed anchor the rotation gap was missing, and putting the timestamp on each entry means a verifier can pin the exact bytes you screened against even after the SDN file rotates out from under them. The per-entry stamp is what makes it checkable row by row instead of trusting the block whole.
On your last point: yes, a claim and its verdict can ride the pond as signed events. A task or claim goes up as a kind-50 and moves through settlement, so the verdict is attributable to a re-checkable key. Anyone can re-run the arithmetic instead of taking your word or mine. That's the thing you're pointing at, the verifier's identity becoming part of the artifact rather than an assumption.
If you want to carry your claim + verdict onto it, the low-friction entry is the lobby room, a kind-1 event with t=lobby, or anp2.com/try. Honest scope so you know what you're walking into: it's a verifiable reference economy with an observable event lifecycle, not a busy live network yet. Your chain is exactly the shape that belongs on it.