Verify receipts offline #
Verification checks signatures and signer identity without contacting a provider. It also explains what the signed records can tell you.
Export a bundle #
Replace ./instance-data with the source instance's data directory. The demo
prints its directory when it starts. Quote paths with spaces, for example
--data-dir "./my instance". Export and key export refuse existing output
files; choose a new filename or move the earlier file first.
tabslip export --data-dir ./instance-data --out calls.tabslip.json
tabslip key --data-dir ./instance-data
tabslip key export --data-dir ./instance-data --out tabslip.pubExport previews which calls it will include and which content it leaves out. Add
--dry-run to preview without writing. Filters include --tag, --model,
--upstream, --since and --until; dates accept RFC 3339 or YYYY-MM-DD at
UTC midnight. Since is inclusive; until is exclusive. No bounds selects all
retained receipts. Reads work while running and after shutdown and create no
state.
Bundles contain original DSSE envelopes (signed payloads and signatures), signed supplements and a convenient copy of the public key. The embedded key alone does not establish who signed. Confirm the public key, fingerprint or key id with the sender through a separate channel.
Check the signer #
After confirming tabslip.pub independently:
tabslip verify calls.tabslip.json --key tabslip.pubAlternatively use --key-id with the independently confirmed ts_… id.
Replace the illustrative id rather than copying it literally. The key id is
derived from the actual public key, not trusted from a label. For a bundle,
the embedded public key supplies the bytes when only its id is confirmed.
Without flags, an existing matching local key can confirm the signer.
Explicit --key or --key-id takes precedence over the local key, and the
two flags are alternatives. Select --data-dir when comparing a particular local key.
A single receipt download is a DSSE envelope, not a bundle. It needs the
public key bytes through --key, even when you know the key id:
tabslip verify receipt.json --key tabslip.pub --jsonFor example, download receipt.json from a receipt's local page, or save
tabslip show <receipt-id> --data-dir ./instance-data --json to that file,
replacing <receipt-id> with a listed id. Single envelopes do not include supplements.
The three answers #
The signature result counts valid receipts and supplements or identifies invalid records. Supplement digests must bind to a receipt in the bundle; changed payloads, wrong keys and inconsistent signer labels are invalid. The signer result says whether an independently selected or existing matching local key confirms the signer. The claims answer explains that model names and usage come from the provider. Provider cost is also reported; Tabslip did not check the model itself.
| Exit | Meaning |
|---|---|
| 0 | Valid signatures and confirmed signer |
| 10 | At least one invalid signature or record |
| 11 | Valid signatures, signer unconfirmed or mismatched |
| 1 | Usage error |
| 2 | Runtime error |
| 5 | File not found |
JSON provides the same three answers and individual envelope results. A signature shows the record has not changed since your Tabslip signed it. It does not show the provider told the truth or stop the key owner inventing a record.
Check a local terminal receipt #
tabslip show <receipt-id> --data-dir ./instance-data checks the receipt and
all attached supplements against the existing local public key. SIGNED means
their signatures and record types are valid. Their signer labels must also match,
and supplements must link to the receipt.
A failed check displays INVALID and returns exit 10 without changing records.
show --json returns only the original base envelope, even on exit 10.
Diagnostics on stderr identify the invalid receipt or supplement. A damaged supplement
can therefore make show --json fail while its base envelope still verifies
on its own. Undecodable storage returns exit 2; a missing receipt returns 5.
These reads never create or repair instance state.
Other export formats #
--format csv and --format jsonl are unsigned exports. JSONL contains one
decoded {receipt, supplements} object per line. HTML is a printable statement
with totals, unknown-cost coverage and a compact call table, without individual
receipt SVGs. These formats combine supplement cost once; they cannot be
verified as signed bundles. No export includes local content.
See the command reference.