# Action Receipt Specification (AR-1) — Draft **Status:** Draft. Profile tag `ar/1-draft`. Field names are settled against the existing canonical projections; the tag becomes `ar/1` when the vector pack has been reviewed. **Conformance pack:** `jcp/conformance/v1/action_receipt.json`, `action_receipt_meta.json` — 7 positive and 3 negative vectors, signed with a published fixture key. --- ## 0. Why this exists Three receipt families already ship in this tree. Each records one quarter of what an audit actually asks for, and **none of them binds to the others**: | Family | Records | Carries | |---|---|---| | `jcp-receipt` | authority | grant chain, verdict, budget drawn | | `sandbox-proof` | environment | attestation, isolation tier, substrate | | JCR-1 | work | input hash, joules, and how they were obtained (energy provenance) | | *(nothing)* | **effect** | what changed, and whether it can be undone | So no single artifact answers: *under what authority did this agent do that, where did it run, what did it cost, and what did it change?* The receipt encoding is implemented six independent times across the tree. That is evidence the **encoding** is portable, and evidence the **composition** was never specified. AR-1 specifies it. Two of the three families already gesture at the chain — the sandbox proof cell carries a `receipt_id`, the authority receipt carries a `payload_hash`. The intent was there. The binding was not. ## 1. Design invariants 1. **Envelope, not replacement.** Each family keeps its own canonical encoding and its own signer. AR-1 reaches into them by content-id; they never reach back. Every existing verifier continues to work unchanged. 2. **No new cryptography.** Canonical det-CBOR (RFC 8949 §4.2.1) and the tagged COSE_Sign1 envelope are reused verbatim from JCR-1. If AR-1 ever appears to need a novel primitive, the design has gone wrong. 3. **Refusal over record-keeping.** A format that merely records claims is a log. AR-1 defines three shapes that MUST be refused at verification, so a violation is *detectable* rather than a matter of trust. 4. **Never claim beyond the evidence.** Every field that asserts a property must be falsifiable from the receipt itself or from an artifact it binds. ## 2. The signed core Canonical projection, det-CBOR encoded, signed as COSE_Sign1 with EdDSA (COSE `alg` −8). Absent optional fields are **omitted, not nulled** — JCR-1 §3.1 drops nulls from the signed core, so an emitted null would not round-trip. ``` ActionReceipt { ar_receipt: "ar/1-draft" // profile tag closed_at: Timestamp // RFC 3339 // binding 1 — authority grant_id: ContentId chain_ids: [ContentId]? // root first; omitted when empty authority_receipt: ContentId verdict: "allow" | "deny" // binding 2 — environment attestation_id: ContentId proof_cell_id: ContentId? tier: "S0".."S7" grounding: "hardware" | "software" // binding 3 — work work_receipt: ContentId input_hash: Hash output_hash: Hash? // a commitment to what the work returned (§2.3) joules_uj: u64 // the work's cost, graded by energy_provenance energy_provenance: "HwShunt" | "ModelBased" | "Estimator" // binding 4 — effect (new; nothing else records this) effect: { class: "none" | "reversible" | "compensable" | "irreversible" target: ContentId before: Hash? after: Hash? compensator: ContentId? plan_receipt: ContentId? } // binding 5 — egress (the router's per-request privacy contract) egress: { class: "none" | "attested_enclave" | "third_party" bytes: u64 peer: ContentId? } steward: String // REQUIRED, as in JCR-1 §2.1 } ``` **Receipt id** = BLAKE3 over the canonical signed core. ### 2.1 `grounding` is carried, never upgraded `grounding` reports whether the bound attestation is rooted in silicon or in software. It is copied verbatim from that attestation. An implementation MUST NOT promote `software` to `hardware` under any circumstance — the field exists precisely so that a software-rooted claim stays legible as one. ### 2.2 `energy_provenance` never collapses `HwShunt`, `ModelBased` and `Estimator` are three different claims about how a joule figure was obtained. An implementation MUST NOT sum or average across them into a single undifferentiated number. A measured figure and an estimated one are not interchangeable, and a receipt that treats them as such is lying by arithmetic. `joules_uj` is the work's cost however it was obtained. It is a hardware reading only when `energy_provenance` is `HwShunt`, and a verifier or display MUST NOT present it as a measurement otherwise. ### 2.3 `output_hash` commits to what came back Without it a receipt shows what went in (`input_hash`) and what changed in the world (`effect`), and not what the work returned. It therefore cannot show which answer it was for. Where the producer binds the output, it seals `output_hash`. It is omitted when absent, so a receipt sealed before the field existed keeps its bytes. A producer SHOULD seal a **commitment** rather than a plain hash wherever the output can be short. A plain hash of a one-token answer can be reversed by trying every answer, and a receipt is shown to parties who were not owed the output. `mesh/1`'s `output_commitment` (mesh SPEC §6.4), which commits under an opening its steward discloses per receipt, is such a commitment and can be carried here as it is. ## 3. The three rules These MUST be enforced at verification, re-derived from the wire projection — **not** from a caller-supplied struct. A receipt hand-forged to violate them signs correctly and must be rejected anyway. The negative vectors in the pack exist to prove an implementation does this. | # | Rule | Rationale | |---|---|---| | 1 | `effect.class == "irreversible"` REQUIRES `effect.plan_receipt` | The cascade's body-tier design already separates plan from commit for actions that touch the world. AR-1 binds that discipline into the evidence: an irreversible commit with no preceding plan is a protocol violation, not a missing nicety. | | 2 | `egress.class == "none"` REQUIRES `egress.bytes == 0` | A claim that nothing left the machine is falsified by its own byte count. | | 3 | `egress.class == "attested_enclave"` REQUIRES `egress.peer` | The router may never claim a stronger class than it can bind evidence for. An unattested destination is `third_party` even when the operator believes it trustworthy. **Trust is not an egress class.** | ## 4. Effect classes | Class | Contract | Enforcement | |---|---|---| | `none` | Read-only. Nothing changed. | Trivially safe to replay | | `reversible` | `before` recorded; prior state restorable. | Undo is a state transition | | `compensable` | Cannot be undone, but a compensating action exists and **its grant is named at the time of acting** — not discovered afterwards. | `compensator` bound in the receipt | | `irreversible` | Once committed, permanent. | Rule 1: plan/commit separation | AR-1 does not solve effect revocation — **nothing does, in general**. Revoking authority does not un-send an email or un-move funds. What AR-1 does is make the distinction explicit and machine-readable *at the moment of action*, which is the precondition for any system that wants to reason about it afterwards. ## 5. Egress classes | Class | Means | Verifiable by | |---|---|---| | `none` | Resolved on-device. | Rule 2, plus tier and attestation | | `attested_enclave` | Left the device to somewhere that proved what it was running. | Rule 3 — the peer attestation id | | `third_party` | Left the device to an unattested destination. | Named and counted, honestly marked unverifiable | ## 6. Verification flow 1. Parse the tagged COSE_Sign1 envelope; reject any `alg` other than EdDSA (−8). 2. Check `kid` against the expected steward. 3. Verify the signature over the canonical signed core. 4. Decode the core as canonical det-CBOR. Reject trailing bytes — a signed core is exactly one item, and anything appended is corruption or an attempt to smuggle data past a verifier that stops at the first complete value. 5. Check the profile tag. 6. **Apply the three rules to the decoded projection.** 7. Optionally resolve each bound content-id and verify the underlying receipt in its own family's verifier. AR-1 binds; it does not re-verify on their behalf. Steps 1–6 require only the envelope and a public key. That is what makes a standalone verifier possible, and shipping that verifier is what turns an internal convention into something a third party can adopt. ## 7. Conformance An implementation conforms at **AR-1/basic** when, given the published pack: - every positive vector verifies, and the re-derived receipt id and canonical core match the published `id_hex` and `core_hex` byte-for-byte; - every negative vector is **rejected**, despite carrying a genuine signature. The negatives are the substance. An implementation that checks signatures and skips the structural rules passes the positives and fails the pack — which is exactly the failure mode the pack is designed to catch. ## 8. What AR-1 deliberately does not do - **Key custody and recovery.** Unsolved everywhere; AR-1 must not be built in a way that presumes a solution. - **Re-verifying bound receipts.** Each family owns its own verification. AR-1 binds content-ids and stops there. - **Proving the energy figure is honest.** It carries provenance so a reader can judge; it does not adjudicate. - **Undoing effects.** See §4. ## 9. Security considerations **A valid signature is not a valid receipt.** The three rules exist because signature verification alone accepts well-formed lies. The negative vectors demonstrate this concretely: each is signed with the real fixture key and each must be refused. **Content-id binding is only as strong as the bound artifacts.** AR-1 proves *which* authority receipt, attestation and work receipt were referenced. It does not prove those artifacts are themselves sound — that is their own verifiers' job, and a complete audit runs all of them. **The energy cross-check is a consistency check, not a proof.** An attestation asserts; joules are metered, and where `energy_provenance` is `HwShunt` they are read off an independently sampled counter. Binding both means a forged attestation must also maintain a plausible energy profile across that counter, and that the failure is visible rather than silent. An `Estimator` figure offers no such cross-check, which is why it is labelled. An adversary with physical access who can forge a quote can also influence measurement. Claiming more than this would be exactly the overreach the design exists to avoid.