envelope_wire_format.md is new. It fixes what the treatise leaves open: the envelope's members, what the issuer and each owner sign, the snapshot SAID and the byte's identity, the history hash, the cargo registry, controller signatures, and verification with an error class for each failure. It also says which questions the cluster answers instead. The identity spec now says: - an inception placeholders `i` with `d`; - an event has exactly its §3.3 members; - thresholds run from 1 to the key count, with no key or digest listed twice; - nothing rotates after an empty `n`; - `dip` is not a v1 type. Rotations follow KERI's rule: signed by the keys they reveal, under both the committed threshold and their own. The previous rule, "signed by the previous keys", let anyone holding the current keys rotate the controller away. Co-Authored-By: Claude Opus 5.5 <[email protected]>
27 KiB
Identity and Key Rotation
Smart Byte Substrate — Spec, Section: Identity and Key Rotation. v1.0-draft, May 2026.
1. Why this section exists
Smart Byte envelopes (treatise §8) bind a byte's lifetime to ed25519 public keys held by issuers and owners, but the treatise does not specify how a controller rotates a signing key, how a verifier follows a controller across a rotation, or how the system recovers from key compromise without unilaterally revoking byte history. Those questions have a mature, well-reviewed answer in the Key Event Receipt Infrastructure (KERI) family of specifications. Smart Byte adopts KERI's cryptographic spine — Self-Addressing IDentifiers (SAIDs) and the key-event log format — without taking the wider Authenticated Chained Data Container (ACDC) layering, which the envelope already subsumes. The spine gives Smart Byte what the envelope is missing: a self-certifying identifier scheme and a pre-rotation-protected key history.
2. Self-Addressing IDentifiers (SAIDs)
2.1 Definition
A Self-Addressing IDentifier is the cryptographic digest of a canonically-encoded object that includes a placeholder for the SAID itself, computed in a way that lets any verifier reproduce the digest and confirm that the object's stated SAID matches its content. The procedure is:
- Construct the object with the SAID field (
din this spec) set to a fixed-width placeholder equal in length to the final SAID encoding. The placeholder is the canonical "all-zeros" string of the same byte length as the final identifier (KERI uses the literal ASCII character#repeated, which keeps the canonical encoding length-stable; Smart Byte follows that convention). - Serialize the object using the canonical encoding rules in §2.3.
- Compute the digest of the canonical bytes using the algorithm named by the SAID's derivation prefix (§2.2).
- Encode the digest using the prefix-plus-bytewise-base32 encoding in §2.2.
- Replace the placeholder in the object with the resulting SAID.
After step 5, the object is self-certifying: any party in possession of the object can run steps 1–4 against a copy with the SAID field reset to the placeholder, compute the digest, and confirm that the object's d field matches. Any modification of any other field — by even a single bit — produces a different digest, and the verification fails. A party that holds the object as bytes it received first confirms they are the object's canonical encoding (§2.3) and refuses them otherwise; it never re-encodes received bytes into canonical form and verifies the result (§2.5, §2.6).
This is the same content-addressing pattern used for envelope identity in treatise §8.2; the SAID construction generalizes it to any object that needs a content-derived identifier including those that must reference themselves (key-event log entries, anchors, attestations).
2.2 Algorithm and encoding
Smart Byte's SAID derivation uses:
- Hash function: BLAKE3 with 256-bit output. BLAKE3 is the same hash family the treatise selects for envelope identity (§8.2, SECURITY.md §1); reusing it here keeps a single cryptographic dependency across the substrate.
- Encoding: a single ASCII prefix byte identifying the digest algorithm, followed by the 32-byte digest encoded in lowercase bytewise base32 (RFC 4648 §6) without padding. Bytewise base32 produces 52 ASCII characters for a 32-byte input; with the prefix the SAID is 53 ASCII characters total.
- Prefix: the character
Edenotes BLAKE3-256. This extends KERI's derivation-code table — which originally allocatedEto BLAKE3-256 in the family of self-addressing prefixes — and reserves additional single-byte prefixes for future digest algorithms (Fis reserved for SHA3-256;Gis reserved for a post-quantum hash, unallocated in v1).
A SAID is therefore a 53-character ASCII string of the form E + 52 base32 characters, fitting cleanly inside JSON, CBOR text, and URL components without escaping. The placeholder used during digest computation is the ASCII character # repeated 53 times.
2.3 Canonical encoding
The canonical encoding for SAID computation is CBOR (RFC 8949) with deterministic encoding rules:
- Map keys are sorted lexicographically by their canonical CBOR byte representation, not by the corresponding string.
- Integers use the shortest CBOR head that fits the value (a value of 0 is encoded as
0x00, never as0x18 0x00). - Strings use the shortest length-prefix.
- Floats are forbidden. Any value that would be a float in another encoding must be represented as a fixed-point integer pair.
- CBOR tags are forbidden. Tagged values change parser behavior and undermine determinism.
- Simple values other than
false,trueandnullare forbidden. The data model has nothing else to decode them to. - Indefinite-length items are forbidden.
- The encoded form is the only form over which the digest is computed. An object held in another format (JSON, MessagePack) is encoded to canonical CBOR before its digest is computed. CBOR bytes received from another party must already be canonical: a verifier refuses any other encoding and never re-encodes it (§2.5, §2.6).
These are the deterministic encoding rules from RFC 8949 §4.2.1 with the tag, float and simple-value prohibitions made explicit. They are also the rules used by envelope serialization in treatise §8 — the choice is intentional, so that a SAID computed over an envelope-shaped object yields the same identifier whether the SAID is being computed by Smart Byte's envelope-identity machinery or its key-event log machinery.
2.4 Worked example
Consider a minimal envelope-shaped object with two fields and a SAID placeholder. In canonical CBOR diagnostic notation, before SAID insertion, the object is:
{
"d": "#####################################################",
"n": 1
}
(The map is shown in sorted-key order; d precedes n lexicographically.)
The canonical CBOR encoding of this object, in hex, is:
A2 # map(2)
61 64 # text(1) "d"
78 35 # text(53)
23 23 23 23 23 23 23 23 23 23
23 23 23 23 23 23 23 23 23 23
23 23 23 23 23 23 23 23 23 23
23 23 23 23 23 23 23 23 23 23
23 23 23 23 23 23 23 23 23 23
23 23 23 # 53 '#' bytes
61 6E # text(1) "n"
01 # unsigned(1)
Concatenated: A2 61 64 78 35 + 53×23 + 61 6E 01, total 61 bytes:
a2616478352323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323616e01
BLAKE3-256 of those 61 bytes is:
56813033774b7fb781e0f8547eae6ec3ca24728c8af6ae9dd771d1e0258f6d39
The 32-byte digest encoded in lowercase bytewise base32 (no padding) yields 52 characters, prefixed with E to produce the 53-character SAID:
Ek2atam3xjn73papa7bkh5ltoypfci4umrl3k5hoxohi6ajmpnu4q
The final object, with the placeholder replaced, is:
{
"d": "Ek2atam3xjn73papa7bkh5ltoypfci4umrl3k5hoxohi6ajmpnu4q",
"n": 1
}
These are the values of vector said-008-spec-worked-example in ../conformance/v1/said_vectors.json, generated by the reference implementation and reproduced by an independent second implementation (../conformance/crosscheck/verify_v1.py).
A verifier holding the object's bytes confirms they are canonical, replaces d with 53 # characters, encodes the result canonically, computes BLAKE3-256, base32-encodes it with the E prefix, and confirms equality with the asserted d.
2.5 Test-vector hook
The full set of canonical worked examples — placeholder bytes, canonical CBOR bytes, BLAKE3 digest, final SAID — lives in ../conformance/v1/said_vectors.json. Implementers MUST pass every vector. The suite covers the empty-map case (said-006), the one-field case (said-007, a §3.4 per-key object), the worked example above (said-008), five objects the substrate addresses by SAID (said-001 to said-005: an issued envelope, its provenance — the byte's identity — the envelope after transfers, and a rotation and an interaction event), and two adversarial cases where a non-canonical CBOR encoding produces a different digest (said-009, said-010), to verify that implementations reject non-canonical input rather than silently re-encoding it or hashing it as received. said-011 is a canonical object that states a SAID its content does not hash to. The canonical-encoding rules of §2.3 are exercised one by one in ../conformance/v1/canonicalisation_vectors.json.
2.6 Receiving SAID-addressed objects
An object that arrives from another party — gossiped (treatise §11.4) or as the answer to a request by SAID (treatise §21.4) — is accepted in four steps, in this order:
- Its bytes are the canonical encoding of the object (§2.3). If they are not, the object is refused; it is never re-encoded.
- It hashes to the SAID it states in
d. - That SAID is the one it was announced or requested under.
- Only then: if the SAID is already held, the object is a duplicate and changes nothing.
Verification precedes deduplication, so a forged object announced under a SAID the receiver already holds is reported as a SAID mismatch — the substitution attempt is visible — instead of being absorbed as a duplicate. No first-come rule is needed: two different canonical objects cannot share a SAID short of a BLAKE3 collision. ../conformance/v1/gossip_vectors.json exercises each step.
3. Key-event log format
The Smart Byte key-event log is a sequence of self-addressing events that record the lifecycle of a controller — the cryptographic identity that signs envelopes' provenance and ownership transitions. The log is adopted from KERI §11 with simplifications appropriate to Smart Byte's envelope model.
3.1 Controller identifiers
A controller AID (Autonomic IDentifier) is the SAID of the controller's inception event with the prefix B substituted for E: B followed by the same 52 lowercase base32 characters. The substitution distinguishes a controller identifier from any other SAID, and tells a verifier that the referenced object is an inception event whose key history is recoverable by replaying the log.
An inception event carries its own AID in i, which is derived from the very SAID being computed. While the inception event is hashed, i therefore holds the 53-# placeholder exactly as d does (§2.1), and both are then stamped from the result.
The controller AID replaces raw ed25519 public keys in envelopes: the provenance's issuer and recipient and each transition's from and to are AIDs (envelope wire format §3, §5). An envelope signature names the establishment event whose key set made it, and is verified against that event's key set as §4 replay establishes it — so a byte stays verifiable across its owners' rotations.
3.2 Event types
There are four event types in v1. Each is a CBOR map with the field set described in §3.3.
-
Inception (
icp) — the first event in a controller's log. Binds the controller AID (the SAID of the inception event, prefixed withB) to:- a set of signing keys (
k), - a signing-key threshold (
kt) — the number of valid signatures required for an event signed by this key set to be accepted, - a set of pre-rotated keys committed by digest (
n), - a next-key threshold (
nt) — the threshold that will apply to the next rotation.
The pre-rotated commitment is the cryptographic core of KERI. The next key set is committed by hash at inception (or at the most recent rotation), so an adversary who compromises the current signing keys still cannot mint a new chain of events for the controller: minting requires the pre-images of the committed next-key digests, which were chosen by the controller and never exposed.
- a set of signing keys (
-
Rotation (
rot) — replaces the current signing keys with the keys committed by the previous event'snfield. A rotation event:- reveals a set of signing keys (
k) whose per-key digests under the §3.4 derivation must match the previous event'snfield, in order, exactly, - establishes a new signing-key threshold (
kt), - commits a new set of pre-rotated keys (
n), - establishes a new next-key threshold (
nt).
After a
rotevent commits, the keys revealed in itskfield are the controller's current signing keys. - reveals a set of signing keys (
-
Interaction (
ixn) — a signed anchor to other content. Carries an anchors array (a) referencing one or more external objects by SAID — envelopes, ownership transitions, external commitments, third-party attestations. Does not change the controller's keys; the signing-key set after anixnevent is identical to the set before. Interaction events are the substrate's mechanism for binding off-log content to the controller's verifiable history without spending a rotation. -
Recovery (
rec) — reserved. A future revision of this section will specify key-loss recovery semantics — the rules under which a controller who has lost the pre-images for anncommitment can re-establish control without a full chain reissue. v1 implementations MUST rejectrecevents. The reservation exists so the event-type field is forward-compatible; the protocol is not yet specified.
No other event type exists in v1. Delegated inception, which KERI defines, is not part of this specification.
3.3 Event field layout
Every event is a CBOR map with the following fields, in canonical-sorted order:
{
"a": [<anchors>], # array; SAIDs of anchored objects; empty for icp and rot
"d": "<SAID of this event>", # SAID, computed per §2 over the event with d as placeholder
"i": "<controller AID>", # the inception event's SAID with the 'B' prefix substitution
"k": [<signing keys>], # array of signing-key encodings; ed25519 keys carry the
# one-byte algorithm identifier from treatise §8.3
"kt": <signing threshold>, # unsigned integer; minimum number of valid signatures
# against keys in k for an event signed by this key set
# to be accepted
"n": [<next-key digests>], # array of SAIDs of next-key public keys, in the same
# order as the future rot event's k field
"nt": <next threshold>, # unsigned integer; the kt that will apply at next rotation
"p": "<SAID of prior event>",# the d of the immediately preceding event; empty string
# for icp (the first event has no predecessor)
"s": <sequence number>, # unsigned integer; 0 for icp, incremented by exactly 1
# for each subsequent event in the log
"t": "icp" | "rot" | "ixn" | "rec",
"v": "SBYTE10JSON" # version string; SBYTE + major-version digit +
# minor-version digit + serialization tag
}
Notes on individual fields:
visSBYTE10JSON: Smart Byte v1.0 with canonical CBOR encoding. TheJSONtag is held over from KERI for forward-compatibility with a future JSON-serialization variant.tis one of the four lowercase tokens above.sstarts at0foricpand increments by exactly 1 per event — no gaps, no reuse.pis the empty string foricpand the previous event'sdfor every other event.ktis an unweighted integer threshold; KERI's weighted-threshold extension is reserved for a later revision. It is at least 1 and at most the number of keys ink.- Each entry in
kis a byte string: the one-byte algorithm identifier from treatise §8.3 (0x01for ed25519; the remaining range is reserved for post-quantum schemes such as ML-DSA) followed by the public key. Order matters; the same key in a different position produces a differentncommitment and breaks the chain. No key appears twice: one holder of a key listed twice could meet a two-key threshold alone. ntis committed in advance so an adversary cannot lower the threshold without revealing pre-rotated keys.- Each entry in
nis the SAID of the corresponding next signing key per §3.4, and no entry appears twice. An emptynsignals the controller does not intend to rotate further; thereafter onlyixnevents are accepted. ntis 0 whennis empty, and otherwise at least 1 and at most the number of entries inn.ais populated forixnand empty foricpandrot.- An event has exactly these members, each of the type given. A member this section does not define — or a member written in another form, such as a key as an array of integers rather than a byte string — makes the event malformed: its bytes must be the event's own canonical encoding, so that its SAID covers exactly what it says.
3.4 Per-key SAID derivation
Each entry in n is the SAID of a tiny CBOR map containing the corresponding next signing key, computed per §2 with the placeholder rules. Specifically, for each next-key K_i (the algorithm-prefixed key byte string of §3.3 the controller intends to use after the next rotation), the entry n[i] is:
SAID({"d": "<placeholder>", "k": h'<K_i>'})
with d initialized to the 53-# placeholder, the map canonically encoded per §2.3, the digest computed per §2.2, and d replaced with the resulting SAID. This per-key SAID is what appears in the previous event's n array; at rotation, the verifier recomputes the per-key SAID for each k[i] in the new event and confirms it matches n[i] from the previous event, position-wise.
A per-key SAID is used rather than a raw key digest so that adding fields to the per-key envelope in a later revision does not change the v1 derivation rule.
3.5 Signatures on events
Who signs depends on the event:
- Inception. Signed by the keys it reveals in its own
k: the inception is self-signed. - Rotation. Signed by the keys it reveals. Those keys must match the previous establishment event's
ncommitment, and the signatures must meet both that event'sntand the rotation's ownkt. Only the holder of the pre-rotated keys can rotate, so an adversary who has compromised the current keys cannot. Nor can the committed threshold be lowered without the pre-rotated keys. - Interaction. Signed by the current key set.
The signatures are carried outside the event map, in a wrapper that pairs the canonical CBOR-encoded event with an array of (key-index, signature) pairs. Each signature is the key's one-byte algorithm identifier followed by the signature (0x01 and 64 bytes for Ed25519) over the event's canonical encoding. The wrapper is not part of the SAID computation; the event's d covers the event body only.
This separation lets a verifier confirm the event's SAID independently of signature verification, and lets multiple signing parties append signatures incrementally without changing the event's identifier.
4. Verifier algorithm
Given a sequence of events and a current controller AID, a verifier accepts the log if and only if every check below passes for every event in order from s: 0 to the latest event seen.
-
Decoding and SAID verification. Confirm the event's bytes are canonical (§2.3) and reject them otherwise (§2.6). Confirm
tisicp,rotorixn—recis reserved (§3.2), and no other type exists — and that the event has exactly the §3.3 members, each of its type, andvisSBYTE10JSON. Then replace the event'sdfield — and, for an inception, itsifield (§3.1) — with the 53-character placeholder, encode canonically, compute BLAKE3-256, base32-encode with theEprefix, and confirm equality with the assertedd. Reject the event if the recomputed SAID does not match. For an inception, also confirmiis the AID itsdnames. -
Sequence verification. Confirm the event's
sis exactly the previous event'ss+ 1. For the inception event, confirmsis0. Reject the event on any gap, repeat, or out-of-order value. -
Prior-event verification. Confirm the event's
pequals the previous event'sd. For the inception event, confirmpis the empty string. Reject the event on mismatch. -
Controller AID verification. Confirm the event's
iequals the inception event'sdwith the digest-algorithm prefix replaced byB. Reject the event on mismatch (an event purporting to be in this controller's log but bearing a different AID is malformed and may indicate a confused or hostile log assembler). -
Type-specific structural check.
- For
icp: confirm it is the first event,ais the empty array, and its key fields are well-formed:kholds at least one key and no key twice,1 <= kt <= len(k),nholds no digest twice, andntis0ifnis empty and otherwise1 <= nt <= len(n)(§3.3). - For
rot: confirm the previous establishment event'snis not empty — a controller that committed to no next keys cannot rotate. Confirm the keys inkhave per-key SAIDs (per §3.4) matching thatnarray, in order; reject the event if any per-key SAID mismatches or iflen(k) != len(n). Confirmais the empty array, and that its key fields are well-formed as foricp. - For
ixn: confirmkequals the previous event'sk,ktequals the previous event'skt,nequals the previous event'sn,ntequals the previous event'snt. An interaction event does not change keys, so the unchanged fields must be carried forward verbatim. Reject the event on any divergence. - For
rec: reject. v1 does not define recovery semantics, and anyrecevent in a v1 log is malformed.
- For
-
Signature verification. Verify the event's signatures over its canonical encoding, against the key set and threshold §3.5 assigns it:
icp: the keys in its ownk, thresholdkt.rot: the keys in its ownk, threshold the greater of the previous establishment event'sntand its ownkt.ixn: the current key set — the inception's or the most recentrot's — and itskt.
Every listed signature must be valid, no key index may appear twice, and at least the threshold of keys must have signed. Reject the event otherwise.
-
Replay determinism. After all checks pass, update the verifier's notion of the controller's current signing key set, signing threshold, next-key commitment, and next threshold according to the event's
k,kt,n,ntfields. Forixnevents, these fields are unchanged from the previous event (per §3.5 above) and the update is a no-op.
Replay also records the key state each establishment event put in force. An envelope's signatures name the establishment event whose keys made them and are verified against that event's key state (envelope wire format §7), so a byte signed under keys since rotated away stays verifiable — the property that lets a byte's ownership chain span a rotation without a break. Whether the named event was the one in force at a transfer's frame is for the cluster to decide (envelope wire format §9).
A verifier MUST treat any failed check as terminal: events with later sequence numbers are not considered part of the log until the rejected event is supplied in a valid form (a different event with a different SAID). v1 does not specify a protocol for replacing a rejected event — that is the recovery problem reserved for rec.
5. Explicit non-adoption: ACDC chaining
The Authenticated Chained Data Containers (ACDC) specification extends KERI's cryptographic spine with a property-graph model over chained containers — each carrying issuer, schema, edges, and rules — and is the right answer for verifiable-credential applications like the GLEIF vLEI deployments (Provenant, Veridian). Smart Byte envelopes already supply the structural primitives ACDC contributes: provenance is the issuer attestation (§8.3), cargo is the payload (§8.5), and the ownership chain is the cryptographically-bound transition chain (§8.4). An ACDC layer on top of the envelope would be either redundant or competing, and either would erode the envelope's role as the substrate's only object (§8.1). The KERI cryptographic spine — SAIDs and the key-event log — is adopted because it solves what the envelope does not. The ACDC container model is not adopted. A future revision may revisit this decision if a substrate-layer use case for property-graph attestations emerges that the envelope cannot accommodate.
6. Implementation reference
The Rust reference implementation of this section lives in the smart-byte-rs workspace:
smart-byte-rs/crates/smart-byte-core/src/canonical.rs— the canonical CBOR encoding of §2.3, and a strict decoder that refuses non-canonical input instead of re-encoding it.smart-byte-rs/crates/smart-byte-core/src/said.rs— SAID derivation and verification (§2.1, §2.2), placeholder substitution, and per-key SAID derivation (§3.4).smart-byte-rs/crates/smart-byte-core/src/inbox.rs— receiving SAID-addressed objects (§2.6).smart-byte-rs/crates/smart-byte-conformance— the conformance-vector harness referenced in §2.5. It generates../conformance/v1from the reference modules and checks the published pack undercargo test.smart-byte-rs/crates/smart-byte-keri-witness— the key-event log: event type definitions, the wrapper that pairs events with signatures, the verifier algorithm in §4, and helpers for emittingicp,rot, andixnevents. Its verifier reports every establishment event's key state, which is what envelope signatures are checked against. Outside spec v1 it also carries extensions — a witness pool with receipts and watchers, delegated inception, and recovery that counts only with its witnesses' receipts — which its default, spec-v1 verifier refuses.
Where the reference crate and this section disagree, this section governs. The conformance vectors are the authoritative reference, independent of any single language's implementation: ../conformance/v1/said_vectors.json for §2, and ../conformance/v1/kel_vectors.json for §3–§4.
7. Citations
- Smith, Samuel M. Key Event Receipt Infrastructure (KERI). Whitepaper, v2.54, 2021–2024. https://github.com/SmithSamuelM/Papers
- Smith, Samuel M., et al. Authentic Chained Data Containers (ACDC) Specification. IETF Internet-Draft,
draft-ssmith-acdc. Current revision: 2024–2025. - Trust over IP Foundation, KERI/ACDC Working Group. KSWG-ACDC Specification. https://trustoverip.github.io/tswg-acdc-specification/
- Bormann, Carsten, and Paul Hoffman. Concise Binary Object Representation (CBOR). RFC 8949, IETF, December 2020.
- Josefsson, Simon, and Ilari Liusvaara. Edwards-Curve Digital Signature Algorithm (EdDSA). RFC 8032, IETF, January 2017.
- Josefsson, Simon. The Base16, Base32, and Base64 Data Encodings. RFC 4648, IETF, October 2006.
- O'Connor, Jack, et al. BLAKE3: One Function, Fast Everywhere. 2020. https://github.com/BLAKE3-team/BLAKE3-specs
- Global Legal Entity Identifier Foundation (GLEIF). vLEI Ecosystem Governance Framework. Production deployments via Provenant and Veridian, 2024–2026.
Cross-references within this spec are to other files in ./ (e.g. treatise_v1_parts_I_II_III_combined.md, ../conformance/v1/said_vectors.json); all paths are relative to this file.