Agent GuardProtocolMCPGovGuardFinGuardDemoPricingDocsRequest Pilot
Specification · EP-RECEIPT-v1

The Trust Receipt format

A Trust Receipt is a signed, offline-verifiable record that a specific action was authorized — by whom, under what policy, with what outcome. Anyone with the signer’s public key can verify one with no account, no API, no network. This page specifies it precisely enough to implement a verifier in any language.

1. Document

anchor is optional; @version, payload, and signature are required.

{
  "@version": "EP-RECEIPT-v1",
  "payload":   { ... the signed claim ... },
  "signature": { "algorithm": "ed25519", "value": "<base64url>" },
  "anchor": {                                  // OPTIONAL
    "leaf_hash":    "<hex sha-256>",
    "merkle_proof": [ { "hash": "<hex>", "position": "left|right" } ],
    "merkle_root":  "<hex>"
  }
}

2. Payload

The payload is application-defined; the signature covers it whole. EP’s convention:

{
  "receipt_id": "ep_...",
  "issued_at":  "2026-06-04T00:00:00Z",
  "claim": {
    "action":   "payment.release",
    "outcome":  "allow | allow_with_signoff | deny",
    "approver": "operator:<named human>",
    "context":  { "amount": 50000, "destination": "acct_9f12", "currency": "USD" }
  }
}

3. Canonicalization

The exact bytes that get signed. Recursive, depth-first key sort at every level — byte-identical on signer and verifier for any nesting depth. A shallow sort is not sufficient; nested keys must be ordered too.

object  -> "{" + keys.sort().map(k => json(k) ":" canon(v[k])).join(",") + "}"
array   -> "[" + elements.map(canon).join(",") + "]"
scalar  -> JSON encoding (UTF-8; non-ASCII NOT escaped)

4. Signature

Algorithm Ed25519, over canonicalize(payload) as UTF-8 bytes. The public key is the base64url of its SPKI DER encoding; signature.value is the base64url of the 64-byte signature.

5. Merkle anchor (optional)

leaf_hash is a hex SHA-256. Each proof step folds the running hash with a sibling: sorted([a, b]) then SHA-256(lo ‖ hi) (hex); position: "left" means the sibling is on the left. The reconstructed value must equal merkle_root. Proof length is bounded (≤ 20).

6. Verification algorithm

  1. @version{EP-RECEIPT-v1}, else invalid.
  2. Ed25519-verify signature.value over canonicalize(payload) with the signer’s key.
  3. If anchor is present, reconstruct the root from leaf_hash + merkle_proof; it must equal merkle_root.
  4. Valid iff version holds, signature verifies, and (anchor absent OR anchor reconstructs). A malformed receipt verifies as invalid — never raises.

7. Reference implementations

Interop is tested: a receipt signed on the JS side verifies under the Python implementation, and vice versa.

JavaScript / Node
@emilia-protocol/verify
Python
pip install emilia-verify
Get the MCP server →Full protocol spec
Trust Receipt Format (EP-RECEIPT-v1) — EMILIA Protocol