Anatomy of a receipt
A receipt is a typed payload composed with a content_hash and a proof. The signature covers the canonical JSON of the payload alone — hash and proof removed. Here is a real one (a FINRA-2210 marketing-claim check), abbreviated:
// composed receipt = payload ⊕ content_hash ⊕ proof { "v": 1, "typ": "flowdesk.marketing-claim-check", // selects how to read the payload "version": "2026-08-03.1", "rulepack": "finra-2210", "verdict": "fail", "summary": { "checked": 6, "violations": 4, "warnings": 2 }, "checks": [ /* … per-rule pass/fail + offending excerpt … */ ], "attested_at": 1754..., "content_hash": "65acd596…", // = sha256hex(canonical(payload)) "proof": { "alg": "ES256", "kid": "flowdesk-envelopes-2026", // pick the JWKS key by kid, not position "signature": "base64url(r‖s, 64 bytes)", "jwks": "/.well-known/flowdesk-signing-key.json" } }
Over the wire, endpoints return the payload nested under a type key with the proof as a sibling — { receipt, content_hash, proof } or { card, content_hash, proof } — and the /verify contract takes them apart again as { action:"verify", <payload>, proof }.
Verify it yourself — four steps, no EcoCloud required
This is the whole point: a receipt proves itself. You never have to trust (or call) EcoCloud to check one.
Canonicalize the payload
Remove content_hash and proof. Serialize the rest as JSON with every object key recursively sorted; arrays keep their order.
Check the content hash
Confirm content_hash equals sha256hex of those canonical UTF-8 bytes. This catches any edit before you even touch a key.
Select the signing key
Fetch the public JWKS and pick the key whose kid matches proof.kid. Selecting by position instead of kid is the classic verifier bug.
Verify the ES256 signature
ECDSA-verify proof.signature (raw r‖s, base64url) over the canonical bytes with that P-256 key. Valid → authentic and unaltered.
Type registry
One envelope, many payloads. Each typ is issued by a live kernel; every one is fetchable as a real signed sample with ?demo=1 and verifiable statelessly with { action:"verify" }.
| typ | What it records | Endpoint |
|---|---|---|
flowdesk.governance-matrix | Batch constitution verdicts + point-in-time replay | /api/v1/agent/matrix |
flowdesk.constitution.analysis | Coverage, conflicts & arbitrage-gap analysis of a constitution | /api/v1/agent/analyze |
flowdesk.audit-anomaly-report | Deterministic anomaly detection over a signed audit stream | /api/v1/agent/anomaly |
flowdesk.agent-card | A signed governance record for one agent — scopes, controls, posture | /api/v1/agent/agent-card |
flowdesk.marketing-claim-check | A rule-pack lint of marketing copy (FINRA 2210) → pass/fail | /api/v1/agent/marketing-claim |
flowdesk.markov-workflow-report | Absorbing/ergodic workflow analysis — completion, bottleneck | /api/v1/agent/markov |
flowdesk.constitution.export | A constitution compiled to Rego / Cedar for external engines | /api/v1/agent/interop |
The list is open and additive: a new typ extends the same envelope. A breaking change to the envelope bumps this spec's major version. schema · /schemas/eco-receipt.schema.json
What a valid signature does — and does not — mean
- It means: this payload was produced by EcoCloud and has not been altered by a single byte since. The verdict, the timestamp, the agent's scopes — exactly as issued.
- It does not mean the action is compliant, approved, or legal. A
marketing-claim"pass" means no flagged pattern was found — not that a regulator would clear the ad. A qualified human still decides. - It moves no value. EcoCloud measures and proves; it never holds, transfers, or settles money. A receipt is evidence, not an instrument.