The two steps
A pack is a zip. The kernel reads the bundle inside it, so extract first.--logs when the pack has no logs/ directory. A pack of hand-off
records has none, and --logs naming a directory that does not exist exits 2.
From 0.17.0, a --logs folder is read by one rule. The command reads each
regular file named <event_id>.json as that record’s disclosed content. It never
follows a link, never enters a subfolder, and names every other entry in a note:
line without reading it. It exits 2 for a name that is not valid UTF-8, for more than
50,000 entries, and for more than 1 GiB of <event_id>.json files. Both
@sanning/proof and sanning-proof apply the same rule, so one folder no longer reads
differently in the two.
Exit codes
This is the one table every verb of@sanning/proof and sanning-proof
uses, verify, export and case alike:
A pack whose declaration says a record’s content is withheld on purpose still
reads
verified: a hand-off record commits to a message’s hash and the pack
carries no file for it, and the declaration says so, so its content binding
is undetermined rather than missing. A pack with no declaration (built
with no signing seed) and a record that discloses nothing reads incomplete
instead, because nothing says the absence was on purpose. Either way the
signatures, the inclusion proofs and the declaration, where the pack has one,
are still checked, and the output says so on its own lines.
This command reads one pack. To read several parties’ packs as one case, see
Hand over and verify a case.
From 0.14.0,
--files matches a file beside the pack to the record that commits to
it. Each offered file is hashed and checked against event.content_hash, or a file
part’s content_hash inside a hand-off message. A file is never matched by name, and
case takes the same flag. See Command line.From 0.17.0,
--files accepts a folder. The folder stands for the regular files
inside it, found recursively with hidden files included, and each is matched by
SHA-256 as a named file is. Links are never followed. A pipe is not read, and a file that cannot be read is
attempted and then named as skipped (unreadable); the report names each in a
skipped list with the reason. A folder
nested more than 32 levels deep, holding more than 50,000 names, or holding a name
that is not valid UTF-8 exits 2. Files found inside folders share 1 GiB per run, and
one that would pass it is skipped as too_large. A file you name is read at any size.
You can mix files and folders, and the verdict and exit code never move.From 0.17.0, a reading fails closed on a tree size it cannot trust. An anchor-trace
event whose
inclusion.leaf_count differs from the leaf_count its signed checkpoint
carries reads failed, and so does a checkpoint record with no integer leaf_count.
A stamp inclusion whose stated count is a boolean reads failed in both kernels.
Integers on the anchor-trace, stamp, and agent-proof paths are read by value, so 3.0
is 3 and an honest bundle written with 3.0 verifies.From 0.14.0, parsing is stricter. A repeated key anywhere in the JSON reads
malformed, exit 2. event_id, payload_hash, payload_ref, subject, and every
time field a verdict carries, are checked against their exact wire shapes rather than
accepted as any string. Nothing on the public record is affected.Two errors you might hit first
Both exit 2, before any check runs. You pointed it at the zip. The kernel reads JSON, not an archive:bundle.json inside it.
You pointed it at the directory.
bundle.json, not the folder that holds it. Only --logs and --files take a
folder.
Check the tamper-evidence yourself
Do not take the verdict on trust. Break the pack and watch it fail.SOME_EVENT_ID with any filename
from logs/.
Confirm the file actually changed, with
sha256sum before and after. An edit that
writes the same byte back produces an identical file, which verifies clean and looks
exactly like a passing tamper test.Confirm the witness on the public record
The verdict above is offline. It names a witness without contacting it. To check that the witness holds those bytes, pass a gateway:Only an
arweave witness with a 43-character transaction id is re-fetched.
That is the one witness kind and id shape the kernel knows how to look up. A
witness of any other kind, or an arweave witness whose id is not exactly 43
characters, is reported as not checked rather than skipped silently, and the
run answers partial and exits 3.From 0.16.0, a pack can carry
witness-cache.json beside bundle.json. It names
a witnessed time the pack’s own builder already looked up. This offline check prints
it labelled not checked; pass --gateways to check it yourself.A stamp reaches the chain before an index can serve it. It is submitted,
then bundled, then indexed, so a gateway can answer
404 for a few minutes
on a stamp that exists. That is exit code 3 as well, and it is a statement
about reachability rather than about your evidence. The offline check does
not wait on any of it. Retry the online one.Python
sanning-proof on PyPI is the same kernel and ships the same command, so a
Python shop needs no Node to check a pack:
npx @sanning/proof exactly.
Both kernels are graded against one corpus, and a disagreement between them on
the same input is release-blocking.
Use it as a library instead when you are checking a pack inside your own code: