Motebit

Verify a Receipt

The fastest correct path to verify a motebit ExecutionReceipt — install the published verifier, do not reimplement it.

You have an ExecutionReceipt and you want to prove it's real. The whole point of the protocol is that you can do this yourself, offline, with no relay and no account. This page is the shortest correct path — for JavaScript/TypeScript, for the command line, and for any other language.

The one thing to internalize first: the verifier is already published. Do not write your own. Canonicalization (JCS / RFC 8785) and cryptosuite dispatch are exactly where hand-rolled verifiers silently pass on one receipt and fail on the next. The permissive-floor library handles both, is Apache-2.0 with a patent grant, has zero monorepo dependencies, and runs in the browser.

Try it now — real receipts to verify

Two real, signed fixtures, one per binding rung. Fetch either and run the steps below; both verify offline.

  • Sovereign (the strongest rung — motebit_id commits to the signing key, provable with no server): sovereign-receipt.json
  • Integrity-only (signature valid, identity not bound — the honest weaker rung): example-receipt.json

Fastest check, no install — the CLI against the live fixture:

curl -s https://raw.githubusercontent.com/motebit/motebit/main/examples/python-receipt-verifier/fixtures/sovereign-receipt.json -o receipt.json
npx @motebit/verify receipt.json    # binary is `motebit-verify`
VALID (receipt)
  task:    019dc500-0000-7000-a000-000000000001
  motebit: 65b60673-d6ed-884b-b01c-2c222d82ada0
  signer:  79b5562e8fe654f94078b112e8a98ba7901f853ae695bed7e0e3910bad049664
  binding: sovereign · motebit_id commits to the key (offline, no operator)

The binding: line is the whole point — sovereign means the motebit_id is itself the commitment to the signing key, provable offline with no operator. The integrity-only fixture verifies the same way but reports the honest weaker rung:

VALID (receipt)
  task:    019dc3f3-6035-7519-ad58-9dfce31f1f15
  motebit: 019dc3f3-6027-73ca-b877-0cf9141c0b72
  signer:  cd7f51b9c11761ebde6e99d7ab095a5c86ebd4c0787a24d2c5f6bc797d4924aa
  binding: integrity-only · signature valid; identity not bound

Flip one character of any field and the verdict becomes INVALID (receipt) with a signature error — that break-it-yourself round-trip is the proof. The two fixtures show the two rungs the result can report (see valid is not the same as identity proven below).

JavaScript / TypeScript

npm install @motebit/verifier
import { verifyArtifact } from "@motebit/verifier";

const receipt = await fetch("/receipt_9f4c.json").then((r) => r.json());
const result = await verifyArtifact(receipt);

if (result.type === "receipt" && result.valid) {
  // Integrity holds: the bytes are intact and the embedded key signed them.
  console.log("signer:", result.signer);

  // Identity binding — how much you can trust *who* signed it.
  if (result.sovereign) {
    // Strongest rung, checkable offline: the motebit_id is itself the
    // commitment to the signing key. No relay, no chain lookup required.
    console.log("sovereign — author proven from the receipt alone");
  }
}

verifyArtifact accepts the already-loaded artifact (object or JSON string); verifyFile(path) reads it off disk. Both return the same typed result, and formatHuman(result) renders the multi-line summary the CLI prints. That's the entire integration — three calls, no crypto code of your own.

valid is not the same as "identity proven"

A receipt answers two different questions, and conflating them is the most common integration bug:

  • Integrityresult.valid. The signature checks out over the canonical bytes; nothing was tampered with. This is verified against the key embedded in the receipt, so by itself it proves authorship-by-some-key, not that the key belongs to anyone in particular.
  • Identity binding — the ladder integrity-only → pinned → anchored → sovereign. This answers whose key it is. result.sovereign === true is the strongest rung and is checkable offline, because a sovereign motebit_id is the commitment to its own signing key. anchored means the key was pinned against an operator's transparency declaration at /.well-known/motebit-transparency.json. An on-chain anchor (Solana) is a second channel that hardens trust-on-first-use — it is not what makes a receipt sovereign, and you do not need a chain lookup to reach the top rung.

Never present valid: true as proof of identity on the strength of the embedded key alone. Render the rung you actually verified. If you want the highest assurance without any network call, check result.sovereign.

result.sovereign lives on @motebit/verifier's result, not on @motebit/crypto's. The binding rung is on this package's VerifyResultWithBinding — the bare @motebit/crypto VerifyResult does not carry it. If you type against crypto directly, .sovereign won't exist. Import verifyArtifact from @motebit/verifier to get the rung. And note the ladder is split across surfaces: @motebit/verifier computes integrity + sovereign offline; pinned/anchored need the relay second channel via @motebit/state-export-client. Which package for which rung → Choosing a Verify Surface.

Suites: never hardcode one

Every signed artifact carries an explicit suite field on the wire — for example motebit-jcs-ed25519-b64-v1 (base64url signature) or motebit-jcs-ed25519-hex-v1 (hex signature). Motebit is cryptosuite-agile by design; post-quantum suites are a registry addition, not a wire break. verifyArtifact dispatches on the suite field for you. A verifier that hardcodes one suite's signature encoding will pass the suite it was written against and silently fail every other — which is precisely why you let the library dispatch instead of decoding the signature yourself.

Delegation chains

If the receipt delegated sub-tasks, each nested receipt is independently signed and verified. Walk result.delegations to inspect every hop; the library recurses for you (the spec caps nesting depth at 10).

Command line — two binaries, split by audience

There are two CLI entry points. The difference is who you are, not what you verify:

You are…UseWhy
Verifying someone else's artifact — auditor, counterparty, a non-motebit CInpx @motebit/verify <path> (binary motebit-verify)The canonical standalone verifier: Apache-2.0, no runtime, smallest install, independently auditable and forkable. You verify the claim without installing the software that made it — the point of an independently-verifiable system.
Already running the motebit runtimemotebit verify <kind> <path>The same check as a subcommand of the CLI you already have (receipt / token / listing / identity). Convenience for operators and devs already in the runtime.
# Stranger / auditor — no install, no runtime:
npx @motebit/verify ./receipt_9f4c.json        # binary is `motebit-verify`

# Already a motebit user:
motebit verify receipt ./receipt_9f4c.json

motebit-verify is the fuller surface — it wires the canonical hardware-attestation leaves (App Attest, TPM, Android Keystore, WebAuthn) for credentials that carry them, and covers content-artifact. Both binaries bottom out in @motebit/crypto's canonical verify, so they report the same binding-ladder rung on the same receipt — no second implementation to drift. Why two namespaces at all: keeping motebit-verify standalone (and Apache-2.0) is what lets a verifier run without adopting the BSL runtime — verification independence is a trust property, not a CLI-ergonomics one. For the library axis (which package to import), see Choosing a Verify Surface.

No web page, no install — see it work

Paste any receipt into receipt.computer: a public, login-free verifier that runs the same verification client-side and reports the same rung. Because it uses @motebit/verifier, a receipt you verify in your own code and a receipt you verify there produce the byte-identical verdict — that agreement is the proof, not a claim about it.

Any other language

The verification recipe is fully specified, and a working non-TypeScript reference exists:

  • Specspec/execution-ledger-v1.md §11 fixes the wire format and the verification steps: strip signature, JCS-canonicalize the remaining body, decode the signature per the suite, Ed25519-verify against the embedded public_key.
  • Python reference verifierexamples/python-receipt-verifier: a single file, two PyPI dependencies, no motebit code consumed. It exists as a concrete proof that the spec alone is sufficient to verify receipts third-party-implementably, and it ships a committed fixture you can verify offline.

Building your own verifier (last resort)

Only if you cannot use the published library in your environment. If you do, you inherit two correctness burdens the floor otherwise carries for you: byte-exact JCS / RFC 8785 canonicalization (signer and verifier must produce identical bytes or signatures fail mysteriously), and suite-field dispatch across every encoding the network emits. Treat the Python reference verifier and the spec's §11 steps as conformance targets, and test against the committed fixtures. When you can, prefer @motebit/crypto (the primitive @motebit/verifier wraps) over raw Ed25519.

See also

On this page