# Feature Specification: MailioReceipt v1 Library

**Feature Branch**: `001-mailio-receipt-v1`
**Created**: 2026-02-04
**Status**: Draft
**Input**: Build a "MailioReceipt v1" library + verifier for cryptographically verifiable,
append-only event records for human-agent and agent-agent workflows.

## Overview

MailioReceipt is a cryptographically verifiable, append-only event record system that enables:

- Agents to decide execution paths dynamically based on approvals and policies
- Users to define rules and guardrails (approval requirements, notifications, limits)
- Verifiable audit trails ("who approved what, when")
- Revocation at any point before execution

MailioReceipt does NOT define workflows. It defines events, rules, and proofs from which
workflows emerge. The library provides generic receipt creation; domain-specific intents
(approvals, revocations, outcomes, notifications, etc.) are modeled via `intent.kind` and
`intent.scope` conventions rather than specialized receipt types.

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Create a Receipt for a Planned Action (Priority: P1)

An agent planning to execute an action (e.g., send an invoice, execute a trade) creates a
MailioReceipt to record the planned action. The receipt contains the event details, actor
information, intent, and optional bindings to artifacts. The agent signs the receipt and
chains it to any previous receipt in the workflow flow.

**Why this priority**: Receipt creation is the foundational capability. Without recording
events, no audit trail or approval workflow is possible.

**Independent Test**: Create a receipt with sample event data, verify the structure contains
all required fields (type, version, event, eventDigest, signatures) and the signature is valid.

**Acceptance Scenarios**:

1. **Given** event data with actors, intent, and optional bindings, **When** an agent creates
   a receipt, **Then** the receipt contains a valid eventDigest (SHA-256 over RFC8785-
   canonicalized event) and a detached JWS signature.

2. **Given** an existing receipt in a flow, **When** creating a new receipt, **Then** the
   new receipt's chain field references the previous receipt's event digest.

3. **Given** event data with artifacts to bind, **When** creating a receipt, **Then** the
   binding field contains cryptographic digests of all referenced artifacts.

---

### User Story 2 - Request and Grant Approval (Priority: P1)

A user wants to approve an agent's planned action before it executes. The agent creates
a receipt with intent kind `mailio.workflow.approval.requested`. The user reviews and
creates a separate approval receipt with intent kind `mailio.workflow.approval.granted`
that binds to the request's digest.

**Why this priority**: Approval is the core user control mechanism. Users must be able to
authorize agent actions before execution.

**Independent Test**: Create an approval request receipt, then create a grant receipt that
references it, verify the chain linkage and approval binding are correct.

**Acceptance Scenarios**:

1. **Given** a planned action requiring approval, **When** the agent requests approval,
   **Then** a receipt is created with `intent.kind = mailio.workflow.approval.requested`.

2. **Given** an approval request, **When** a user grants approval, **Then** an approval
   receipt is created with `intent.kind = mailio.workflow.approval.granted` binding to
   the request's digest.

3. **Given** an approval with a TTL, **When** the TTL expires, **Then** the approval is
   no longer valid for execution decisions.

---

### User Story 3 - Revoke an Approval (Priority: P2)

A user wants to revoke a previously granted approval before the action executes. They
create a revocation receipt referencing the approval. Any execution check after the
revocation timestamp must block the action.

**Why this priority**: Revocation is critical for security and user control. Users must be
able to stop workflows before irreversible actions occur.

**Independent Test**: Create an approval, then revoke it, verify that execution checks
correctly block based on timestamp comparison.

**Acceptance Scenarios**:

1. **Given** a granted approval, **When** a user revokes it, **Then** a receipt is created
   with `intent.kind = mailio.workflow.approval.revoked` referencing the approval digest.

2. **Given** a revocation with `ts = T1` and an execution decision at `T2 > T1`, **When**
   checking execution status, **Then** the action is blocked.

3. **Given** a revocation with `ts = T2` and an execution decision already made at `T1 < T2`,
   **When** the revocation is recorded, **Then** the prior execution remains valid but the
   revocation is recorded for audit.

---

### User Story 4 - Verify a Receipt and Chain (Priority: P2)

A verifier wants to validate that a receipt is authentic and the chain is intact. The
library fetches the receipt, canonicalizes the event, verifies all signatures by resolving
signer DIDs, and traverses the chain to validate integrity.

**Why this priority**: Verification establishes trust. Without it, receipts have no
cryptographic value.

**Independent Test**: Create a valid receipt chain, verify it succeeds. Tamper with a
receipt and verify it fails.

**Acceptance Scenarios**:

1. **Given** a valid receipt, **When** verifying, **Then** the library canonicalizes the
   event using RFC8785, verifies all signatures, and returns success with signer details.

2. **Given** a tampered event, **When** verifying, **Then** the signature verification fails.

3. **Given** a receipt chain, **When** traversing via `chain.prevReceiptDigest`, **Then**
   each link is verified and chain integrity is confirmed.

---

### User Story 5 - Check Execution Eligibility (Priority: P2)

An agent about to execute an action needs to verify that all required approvals exist
and no valid revocations block execution. The library provides an execution check that
traverses the chain, collects approvals, checks for revocations, and validates bindings.

**Why this priority**: This is the decision point for agents. Execution must only proceed
when policy is satisfied.

**Independent Test**: Create a flow with approvals and optionally revocations, verify the
execution check returns the correct allow/block decision.

**Acceptance Scenarios**:

1. **Given** a planned action with a valid approval, **When** checking execution, **Then**
   the check returns allowed.

2. **Given** a planned action with no approval, **When** checking execution, **Then** the
   check returns blocked (missing approval).

3. **Given** a planned action with approval and revocation where `revocation.ts <= decisionTs`,
   **When** checking execution, **Then** the check returns blocked with revocation details.

4. **Given** artifact bindings in the approval, **When** checking execution, **Then** the
   check validates that execution artifacts match approved digests.

---

### User Story 6 - Resolve DID:web Identifiers (Priority: P3)

A verifier needs to resolve actor DID:web identifiers to obtain public keys for signature
verification. The library fetches the DID document and extracts the verification method.

**Why this priority**: DID resolution is required for verification but is a supporting
capability.

**Independent Test**: Resolve a known DID:web and verify the returned key matches expectations.

**Acceptance Scenarios**:

1. **Given** a valid DID:web (e.g., `did:web:example.com:user:alice`), **When** resolving,
   **Then** the DID document is fetched and the public key is extracted.

2. **Given** an unreachable DID:web endpoint, **When** resolving, **Then** a retryable error
   is returned.

3. **Given** a DID document with multiple verification methods, **When** resolving, **Then**
   the correct key is selected based on the JWS key ID.

---

### User Story 7 - Record Execution Outcome (Priority: P3)

After executing an action, an agent records the outcome (success or failure) as a receipt.
This completes the audit trail for the workflow.

**Why this priority**: Recording outcomes ensures complete audit trails but depends on
prior stories.

**Independent Test**: Create a flow through planned → approved → executed, verify the
chain is complete and all events are linked.

**Acceptance Scenarios**:

1. **Given** an approved action, **When** execution succeeds, **Then** a receipt is created
   with `intent.kind = mailio.workflow.action.executed`.

2. **Given** an approved action, **When** execution fails, **Then** a receipt is created
   with `intent.kind = mailio.workflow.action.failed` including failure details.

---

### Edge Cases

- **Chain branching**: Chains MAY branch when parallel actions are allowed. The library
  MUST support multiple receipts referencing the same predecessor.

- **DID:web temporarily unavailable**: Return a retryable error. Caching of resolved DID
  documents MAY be implemented with appropriate TTL.

- **Clock skew**: Timestamps are informational. Chain ordering is determined by digest
  references, not timestamps. However, revocation decisions use timestamp comparison.

- **Expired approval**: If an approval has a TTL and it expires before execution decision,
  the approval is invalid.

- **Revocation after execution decision**: The revocation is recorded but does not
  invalidate the prior execution decision.

- **Missing artifacts at execution**: If bound artifacts cannot be verified (digests don't
  match), execution MUST be blocked.

## Requirements *(mandatory)*

### Functional Requirements

**Receipt Structure**

- **FR-001**: Receipt MUST contain top-level fields: `type` ("MailioReceipt"), `v` ("v1"),
  `event`, `eventDigest`, `signatures`.

- **FR-002**: Library MUST compute `eventDigest` as SHA-256 over RFC8785-canonicalized
  `/event` object.

- **FR-003**: Library MUST produce detached JWS signatures over the canonicalized event.

- **FR-004**: Library MUST support ES256 (ECDSA with P-256 and SHA-256) and EdDSA
  (Ed25519) for signing and verification.

**Event Object**

- **FR-005**: Event MUST contain required fields: `type` ("MailioAuditEvent"), `v`, `id`
  (globally unique), `flowId`, `ts` (RFC3339), `actors`, `intent`.

- **FR-006**: Event MAY contain optional fields: `ttl`, `chain`, `binding`, `meta`.

- **FR-007**: `actors` MUST contain at minimum `issuer` (entity asserting the event) and
  `subject` (user or principal affected). Optional: `authority`, `others`.

- **FR-008**: `intent` MUST contain `kind` (lifecycle event type), `action` (domain action),
  `category` (high-level classification), `scope` (structured action data).

**Intent Kinds (v1)**

- **FR-009**: Library MUST support core lifecycle intent kinds:
  - `mailio.workflow.action.planned`
  - `mailio.workflow.approval.requested`
  - `mailio.workflow.approval.granted`
  - `mailio.workflow.approval.revoked`
  - `mailio.workflow.action.executed`
  - `mailio.workflow.action.failed`
  - `mailio.workflow.notify`

**Chaining**

- **FR-010**: Chain field MUST contain: `prevReceiptDigest` (event digest of previous receipt),
  optional `prevReceiptRef` (retrievable reference), optional `reason`.

- **FR-011**: Events are immutable. New information MUST be recorded as a new receipt.

- **FR-012**: Chains MAY branch (multiple receipts referencing the same predecessor).

**Approvals**

- **FR-013**: Approvals MUST bind to immutable digests (the approved event's digest).

- **FR-014**: Approvals MAY include additional scope fields (e.g., purpose or policy metadata).

- **FR-015**: Execution MUST reference the approval event digest.

- **FR-016**: Approvals MAY include a TTL. Expired approvals are invalid.

**Revocations**

- **FR-017**: Revocations use `intent.kind = mailio.workflow.approval.revoked` and reference
  the approval being revoked.

- **FR-018**: An action MUST NOT execute if a valid revocation exists where
  `revocation.ts <= executionDecisionTs`.

- **FR-019**: If revocation arrives after execution decision, execution remains valid;
  revocation is recorded but non-blocking.

**Bindings**

- **FR-020**: Bindings link artifacts (files, documents, JSON payloads) and messages
  (email, message identifiers) to events.

- **FR-021**: Bindings MUST use cryptographic digests.

- **FR-022**: Execution MUST validate that artifacts match digests approved by the user.

**Signatures**

- **FR-023**: Each signature MUST contain: `role` (user/agent/authority), `signer` (DID),
  `alg` (algorithm), `signed` (canonicalization metadata), `jws` (detached JWS).

- **FR-024**: All signatures MUST sign the RFC8785-canonicalized `/event`.

- **FR-025**: Any change to event invalidates all signatures.

**Verification**

- **FR-026**: Verification algorithm MUST:
  1. Fetch the receipt by digest
  2. Canonicalize the event using RFC8785
  3. Verify all signatures by resolving signer DIDs
  4. Traverse chain via `prevReceiptDigest`
  5. Collect approvals bound to the action
  6. Ensure approval exists, not expired, no valid revocation
  7. Validate bindings match approved digests

**DID:web Resolution**

- **FR-027**: Library MUST resolve DID:web identifiers according to the DID:web
  specification (fetching `/.well-known/did.json` or path-based resolution).

- **FR-028**: Library MUST extract verification methods from DID documents and match
  them to JWS key IDs.

**Error Handling**

- **FR-029**: Library MUST return clear, actionable errors for: invalid signatures,
  chain integrity failures, DID resolution failures, revocation blocks, expired approvals,
  binding mismatches.

### Key Entities

- **MailioReceipt**: The top-level signed container around a single event. Contains type,
  version, event, eventDigest, and signatures array.

- **Event (MailioAuditEvent)**: The semantic content of a receipt. Contains id, flowId,
  timestamp, actors, intent, and optional chain/binding/meta.

- **Flow**: A logical grouping of receipts belonging to the same workflow instance,
  identified by `flowId`.

- **Actors**: Structured identification of participants: issuer (who asserts), subject
  (who is affected), authority (optional governing entity), others (additional participants).

- **Intent**: Describes what the event represents: kind (lifecycle type), action (domain
  action), category, scope (structured data).

- **Chain**: Links events immutably via `prevReceiptDigest` (event digest). Enables audit trail traversal.

- **Binding**: Cryptographic links to external artifacts and messages via digests.

- **Signature**: Explicit statement of what was signed: role, signer DID, algorithm,
  canonicalization metadata, detached JWS.

## Assumptions

- Signers are responsible for securing their private keys; the library does not provide
  key storage.
- DID:web endpoints are publicly accessible over HTTPS.
- Event data is provided as valid JSON-serializable structures.
- The consuming application maintains receipt storage; the library operates on individual
  receipts or provided chains.
- Receipts MAY be announced over untrusted event buses (EventBridge, Kafka, Webhooks);
  agents MUST fetch full receipts and verify signatures locally.
- Transport and storage layers are NOT trusted; cryptographic verification is mandatory.

## Out of Scope

- Workflow definition or state machine logic (MailioReceipt records events; workflows emerge)
- Total ordering enforcement beyond timestamps
- Key management or storage
- Event bus implementation
- Policy definition language (policies are enforced by consuming applications)

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: Developers can create a signed receipt from event data in a single function
  call with a clear, typed API.

- **SC-002**: Receipt verification completes in under 2 seconds for a chain of up to 100
  receipts (excluding network latency for DID resolution).

- **SC-003**: 100% of tampered receipts (modified event, signature, or chain reference)
  are detected during verification.

- **SC-004**: The library correctly blocks execution when any valid revocation exists
  (where `revocation.ts <= decisionTs`), with zero false negatives.

- **SC-005**: Expired approvals are correctly identified and treated as invalid.

- **SC-006**: Binding verification catches 100% of artifact digest mismatches.

- **SC-007**: All cryptographic operations produce deterministic outputs (RFC8785
  canonicalization ensures this).

- **SC-008**: Error messages clearly indicate the failure type (signature invalid, chain
  broken, revoked, expired, binding mismatch, DID unreachable).

- **SC-009**: The library handles chain branching correctly, supporting multiple receipts
  with the same predecessor.

## Clarifications

### Session 2026-02-04

- Q: Authorization model for approvals/revocations? → A: Any valid signer can create
  receipts; policy enforcement is done by consuming application based on actors, intent,
  and binding fields. Revocations have time-based rules: `revocation.ts <= executionDecisionTs`
  to block.
