HERALD Envelope Cheatsheet
One-page implementer reference for the v0.2 envelope. Normative source: learning/herald-protocol/SPEC.md.
Envelope At A Glance
sigil pubkey
|
v
+-----------------------------+
| HERALD envelope |
| |
| v |
| id = sha256(canonical_body) |
| from / to |
| parent / trace |
| ts / nonce / expires |
| lineage_proof |
| attestations[] |
| payload |
| signatures[] |
+-----------------------------+
| |
| +--> signature[0] MUST be from `from`, scope `origin`
|
+--> parent links backward; trace pins the genesis envelopeField Reference
| Field | Required | Shape | Verifier duty |
|---|---|---|---|
v | All envelopes | protocol version string, e.g. herald/0.2 | Treat as part of the canonical body. |
id | All envelopes | SHA-256 digest of canonical body | Recompute with id omitted and signatures empty; mismatch is tampered. |
from | All envelopes | sender sigil | Resolve sigil to full Ed25519 public key before accepting signatures. |
to | Optional | recipient sigil | App/transport field; signed when present. |
parent | Genesis and non-genesis | null for genesis; envelope id otherwise | Reject unknown parent as orphaned; check trace-parent binding when parent is known. |
trace | Genesis and non-genesis | genesis envelope id | Genesis uses trace == id; non-genesis must equal parent.trace. |
ts | All envelopes | Unix seconds | Apply local clock-skew policy. |
nonce | All envelopes | 128-bit CSPRNG random | Cache (from, nonce) and reject duplicates. |
expires | Optional | Unix seconds | Reject when older than now - skew_tolerance. |
lineage_proof | Optional | reference to Herald-signed lineage certificate | Missing endorsement is informational: lineage-unverified. |
attestations | Optional | array of { type, value } claims | Preserve as signed data; semantics come from SPEC, EXT, CHARTER, or registries. |
payload | All envelopes | opaque application object | Conduits must not modify it. |
signatures | All envelopes | ordered array of signature objects | Index 0 must be from with scope: "origin"; validate in order. |
Genesis vs Reply
| Envelope kind | parent | trace | Extra rule |
|---|---|---|---|
| Genesis | null | same value as id | First envelope from a sigil must include a birth attestation. |
| Non-genesis | parent envelope id | genesis envelope id | E.trace must equal parent.trace. |
Hashing And Signing
1. Build the envelope body in CBOR-deterministic form.
2. For id, canonicalize with id omitted and signatures replaced by an empty array.
3. Set id = sha256(canonical_body).
4. For the origin signature, sign herald.envelope.v2 || canonical_body.
5. For additional signatures, each signer signs the canonical body with all prior signatures retained in declaration order.
6. Never sign or hash a JSON rendering; JSON is display-only.
Signature Object
{
"by": "sigil:abc...",
"alg": "ed25519",
"scope": "origin",
"sig": "base64-or-profiled-bytes"
}Registered v0.2 origin algorithm: ed25519. Common scopes named in the core spec are origin, tool-sandbox, conduit-receipt, human-approval, and herald-lineage.
Common Verifier Results
| Result | Typical trigger |
|---|---|
valid | Origin signature verifies, chain is intact, required scopes are present, nonce is fresh. |
partial | Origin signature verifies but an optional or policy-required signature is invalid or absent. |
signature-invalid | Origin signature fails. |
tampered | Recomputed id does not match the envelope. |
orphaned | Parent envelope is not known yet. |
parent-trace-mismatch | Non-genesis trace differs from parent.trace. |
replayed-nonce | (from, nonce) has already been seen. |
expired-envelope | expires is outside tolerance. |
unknown-sigil | from cannot be resolved to a public key. |
unknown-alg | Signature algorithm is not registered. |
lineage-unverified | Lineage claim lacks Herald endorsement; not a failure by itself. |
Verifiers should surface all applicable results, not only the first one.
Minimal Review Checklist
- Does
idrecompute from CBOR-deterministic bytes, not JSON? - Is
signatures[0]byfrom,alg: "ed25519",scope: "origin"? - For non-genesis envelopes, is the parent known and does
trace == parent.trace? - Has
(from, nonce)already appeared within the replay cache window? - Are optional signatures either valid or reported as
partialwhen policy expects them? - Is missing Herald lineage shown distinctly as unverified rather than silently trusted?