# 09 · Signed Envelopes — 7-Layer Verification

> Ed25519 signatures over canonical serialization.

## Overview

Anything that must be trusted across processes, machines, or time — policies,
capability grants, node identities, delegations — travels as a **signed
envelope**: an Ed25519 domain-separated signature over deterministic canonical
serialization of the payload.

Canonical serialization matters because signatures are computed over bytes.
If two implementations can serialize the same logical object differently,
signatures break or, worse, two different objects collide. Arcana's canonical
form is stricter than RFC 8785/JCS: no floats, strict UTC RFC 3339
millisecond timestamps, unpadded base64url.

## The seven verification stages

An envelope is trusted only after all seven pass, in order:

| # | Stage | Question answered | Example failure |
|---|---|---|---|
| 1 | PARSE | Is this decodable at all? | Truncated bytes |
| 2 | SCHEMA | Right shape and types? | Missing audience field |
| 3 | SIGNATURE | Does Ed25519 verify over the canonical bytes? | Tampered payload |
| 4 | TRUST | Is the issuer anchored in our trust store? | Self-signed stranger |
| 5 | AUDIENCE | Was this issued for us? | Node B reading node A's grant |
| 6 | FRESHNESS | Issued/expired windows sane? | Replay from last year |
| 7 | REVOCATION | Has the issuer withdrawn it? | Fired admin's policy |

**Any stage fails → rejected.** No partial trust, no skip path, no "well the
signature was fine".

## Conformance

Verification is cross-checked by two independent implementations:

- TypeScript production verifier: 100/100 golden crypto vectors.
- Rust verifier: 46 golden vectors (41 negative, 5 positive) generated by the
  TypeScript side and verified without shared code.
- Cross-language request-hash vector: identical hash in TS and Rust for the
  same fixture.

## Related

- Previous: [Scoped Approvals & Routing](/governance/approvals.md)
- Next: [RunProof — Portable Evidence](/governance/runproof.md)