behalf · offline verifier

Drop your export here. It is verified in this tab.

This page runs the behalf-verify verification core, compiled to WebAssembly. The file you drop is read by JavaScript in your browser and handed to the wasm module in memory. It is never sent anywhere — this page has no upload code, no analytics, no fonts and no third-party requests of any kind.

Drop an export file
a run_*.jsonl in behalf.sh/export/v1 format — or pick one

The verifier could not load

Browsers block fetch() from file://. Either open the single-file build dist/verify.html, which has the wasm inlined and needs no server, or serve this directory: python3 -m http.server -d verifier/web/dist.

Try tampering with it

Load the demo export — 47 receipts from a support-agent run that issues a refund at step 31 — then break it. Each button below is one row of the tamper suite that gates every commit to this repository (docs/export-format-v1.md §5).

What a green tick here does and does not mean

✔ This page checks

  • Each receipt's payload bytes, taken as the exact byte span in the file, hash to the leaf_hash the file declares. Nothing is re-serialised first, so an edited byte cannot be normalised away.
  • Each receipt's Ed25519 signature verifies over those same bytes, framed by DSSE PAE.
  • Receipt indices are contiguous and ascending — no gaps, no reordering.
  • The hash chain recomputed over every leaf equals the chain value in the head, and the head's own signature verifies.
  • Signatures are checked against the public keys carried in the export's own header, whose RFC 7638 thumbprints must match the keys they name.

✖ This page does not check

  • That the signing key is the legitimate one. The export carries its own keys. An export forged end to end under an attacker's key verifies here exactly as a genuine one does. Binding a key to a real emitter is the published key log's job — that log does not exist yet. Until it does, a green tick means "internally consistent", not "authentic".
  • That the agent did what the receipt says. This is integrity, not truth. A receipt that was wrong when it was written verifies perfectly.
  • That this is the current export. An old but intact copy verifies. Catching a rolled-back or forked log needs the signed checkpoint and a witness, not a file.
  • Tile-directory (log) mode. behalf-verify log <dir> checks a Tessera tlog-tiles directory: checkpoint note signature, entry bundles, RFC 6962 root recomputation, the stale-restore rule. That needs a filesystem and is deliberately not in this build. Use the command-line verifier.

Why you can believe "nothing is uploaded"

The same result, from the command line

The wasm module here and the behalf-verify binary are built from one Rust crate — the same verify_export, the same classes, the same exit codes. What you see above is what the terminal prints:

$ behalf-verify run_c71e.jsonl
✔ 47/47 receipts intact   chain head 7136…8958
$ echo $?
0

$ sed 's/1200.00/12.00/' run_c71e.jsonl > covered.jsonl
$ behalf-verify covered.jsonl
✖ TAMPERED
receipt 31: content hash mismatch (expected 66f9… computed cb44…)
chain breaks at 31; receipts 32-46 unverifiable.
$ echo $?
1

Exit codes are stable and documented: 0 verified, 1 tampering detected, 2 unverifiable — not a readable export.