Skip to main content
Two commands, owned by two packages, and the split matters. An auditor never needs the write SDK. Verification lives in its own package with no account and no key, and no network unless you choose to pass a gateway URL for the optional online check (see below).

bundle

Assemble the hand-over pack for one agent over one period. This section is linked as docs.sanning.io/reference/cli#bundle from the console’s Evidence page.
Same flags, same exit codes, same output in both languages. One shared fixture grades them, and neither passes by agreeing with the other.
--logs is optional to the parser and required in practice. The command reads your record objects to work out which events are this agent’s, because a minimum-disclosure envelope does not carry that name.
Authentication comes from SANNING_API_KEY in the environment. There is no flag for it. The key needs anchor:read. A declaration needs SANNING_SIGNING_SEED. Set it to the seed of the agent whose records you are bundling, 64 lowercase hex characters, and bundle signs a declaration, the pack’s own statement of what it holds, and seals the pack with that key. Without it, bundle seals the pack with a one-time key it mints and discards, and builds no declaration. With it set, bundle refuses a window holding a record that names no case, a window holding more than one case, and an s3:// log store. See Hand over and verify a case. --agent is checked twice. The records route the command reads from filters by this name on the server, and the command also refuses if a record it gets back names a different agent. A misspelled --agent prints bundle: no stamped records for agent "…" … (the control plane does not recognize this agent) and exits 1, rather than building an empty or mismatched pack. It verifies its own output before writing. If the assembled pack does not verify, nothing is written and the command exits non-zero. A pack that only fails at the auditor is a pack shipped broken. From 0.16.0, bundle writes witness-cache.json beside bundle.json when a stamp’s witness is already in a block. It records a witnessed time your side already looked up, so a reader is not required to; verify and case print it labelled not checked unless you pass --gateways, and a missing or partial cache never blocks the pack.

Pack reporting

After bundle writes the pack, it reports that it did so to Sanning, using the same API key, so the pack appears under Evidence in the console. The report carries your agent’s name, the environment, the window, the record count, the SHA-256 of bundle.json, the SDK name and version, and the time the pack was built. It never carries the pack, a log, a record id, or a file name, and Sanning never receives the pack itself. Every run of bundle builds a new bundle.json with its own build time, so every run is reported, even one covering a period you have already handed over. If the report fails, bundle prints one line naming the pack’s digest and still exits 0: the pack was assembled and verified before the report was attempted, so a reporting failure does not undo it. The credential needs anchor:read, the scope it already holds to read your records back.

verify

Check a pack. No account, no key, no relationship with Sanning.
verify, export and case share one exit table: A pack whose declaration says a record’s content is withheld on purpose reads verified; a pack with no declaration and an undisclosed record reads incomplete instead, because nothing says the absence was on purpose. Only an arweave witness with a 43-character transaction id is re-fetched from a gateway. A witness of any other kind, or an id that is not exactly 43 characters, is reported as not checked, and the run answers partial and exits 3. From 0.14.0, parsing is stricter. A bundle, an envelope or a log file whose JSON repeats a key reads malformed, exit 2, rather than silently keeping the last value. event_id, payload_hash, payload_ref and subject, and every time field a verdict carries, are checked against their exact wire shapes; a value that fails names the field and reads malformed or failed where it used to read verified with nothing to compare against. No item on the public record is affected. sanning-proof on PyPI is the same kernel and ships the same verify command. See Verify at the command line.

case

Read one or more packs, or a case file, as one case, and print one verdict with every exception named. Each argument is a pack folder, a folder holding several packs, or a case file named <name>.zip:
case shares verify’s exit table, above. --gateways takes a comma-separated list and re-fetches each pack’s witnesses, reported on their own line; the lookup never moves the verdict. --files, from 0.14.0, works the same way verify uses it: files handed in beside the case are matched against every party’s records, never by name, and a path can be a folder from 0.17.0. See Hand over and verify a case. From 0.17.0, case also matches the files that sit outside every pack, with no flag. Each regular file in the .zip or in the folder unzip makes of it is hashed with SHA-256 and listed as vouched for by a record in a named pack, as matching no record, or as skipped (a link, a pipe, a refused zip entry, or an unreadable file). The section is information only: the verdict and exit code never move. The loose files share 1 GiB, and a file past it is skipped as too_large. A .zip larger than 1.25 GiB on disk exits 2 before it is read. If a file the case index lists passes its 1 GiB, the comparison reports case_index_file_too_large; if contents.json would pass the run’s whole-read budget, or cannot be read, the index reads as case_index_unreadable and the verdict still prints.

Getting help

--help works on the top-level command in both languages. On a subcommand it exits 2 with unknown argument: --help, which is a rough edge rather than a failure.