cluster_state.md, rewritten. The state gains the cluster's identifier, its peers, its governance (delay, sequence number, pending changes), and the retired, exported and imported records. - Governance proposals change the registry and the peers. Each is sequence-numbered, needs approval from floor(2m/3)+1 of its electorate, and is time-locked by the cluster's delay. - A burn retires a byte for good. - A transition may name a destination cluster. The source's committing nodes certify the byte's state as it left. The destination checks the certificate and verifies the byte's full history itself. envelope_wire_format.md: the destination member, covered by the signature and the history hash. Frames rise within a cluster; a move to another cluster starts that cluster's count. Co-Authored-By: Claude Opus 5.5 <[email protected]>
9.7 KiB
Envelope Wire Format
Smart Byte Substrate — Spec, Section: Envelope Wire Format. v1.0-draft.
1. What this section fixes
Treatise §8 says what a smart byte carries and why. This section fixes how it is written: member names and types, the messages its signatures cover, how its history hash advances, and how it is verified. Everything is canonical CBOR as identity spec §2.3 defines it. Where the treatise's prose leaves a choice open, this section makes it.
2. The envelope
{
"d": SAID of this envelope as it stands (§4),
"provenance": the origin attestation (§3),
"ownership_chain": [transition, ...] (§5), oldest first,
"joule_cost": {"measured_microjoules": uint, "estimated_microjoules": uint}
}
An envelope has exactly these members, and so does every map inside it: a member this section does not define makes the envelope malformed. joule_cost is the two-part energy accounting of treatise §8.6.
An envelope carries two SAIDs. The provenance's d is the byte's identity (treatise §8.2): fixed when the byte is issued, never changed by anything that happens to it afterwards. The envelope's own d addresses this snapshot of the byte — its chain and joule cost as they stand now.
3. Provenance: the origin attestation
{
"d": SAID: the byte's identity,
"issuer": the issuer's controller AID (identity spec §3.1),
"recipient": the first owner's controller AID,
"nonce": 16 bytes,
"cargo": cargo (§6),
"signature": the issuer's controller signature (§7)
}
- The issuer signs the canonical encoding of the four attested fields (treatise §8.3): the map
{"cargo", "issuer", "nonce", "recipient"}holding the provenance's own values. dis the SAID of the provenance map (identity spec §2.1). It covers the signature, so the identity binds the issuer, the cargo, the recipient, the nonce, and the issuer's signature (treatise §8.2).- The nonce keeps two otherwise-identical issuances distinct.
Parties are named by controller AIDs, which identity spec §3.1 makes authoritative over the treatise's raw public keys. A signature is checked against the key state the signer's key-event log establishes (§7), so a byte stays verifiable across its owners' key rotations.
4. The snapshot SAID
The envelope's d is the SAID of the whole envelope map, computed per identity spec §2.1 with the provenance's d already in place. Each transfer and each joule-cost update produces a new snapshot, and so a new snapshot SAID; none changes the identity. The gossip overlay deduplicates and serves snapshots by this SAID (identity spec §2.6).
5. The ownership chain
{
"previous_history_hash": 32 bytes,
"from": the current owner's AID,
"to": the new owner's AID,
"frame": uint: the lockstep frame the transfer is applied in,
"destination": SAID: the cluster the byte moves to — only on a transfer between clusters,
"signature": from's controller signature (§7)
}
fromsigns the canonical encoding of{"byte", "previous_history_hash", "from", "to", "frame"}, wherebyteis the identity's SAID text (treatise §8.4). A transfer between clusters also has"destination"in the map it signs. The signature is therefore scoped to exactly one transition, of exactly one byte, at exactly one point in its history, and to where the byte goes.- The history hash (treatise §10.1) starts as the identity's 32 digest bytes. Each transition advances it:
next = BLAKE3(previous ‖ payload). Thepayloadis the canonical encoding of the transition's{"from", "to", "frame", "signature"}, with"destination"included when the transition has one. destinationis present only on a transition that moves the byte to another cluster (treatise §11.3.3; cluster state spec §5). A transition within a cluster has no such member.- Frames rise strictly within a cluster: a byte is transferred at most once per frame (treatise §8.4). Each cluster counts its own frames, so the transition after one with a
destinationis in another cluster's count and is not compared with it.
6. Cargo
{"type": uint, "bytes": bytes}
type is a small-integer tag from the registry below, and bytes is opaque to the substrate (treatise §8.5). A registered type fixes what its bytes hold; the substrate transports them unchanged.
| type | name | bytes |
|---|---|---|
| 0 | opaque |
application-defined |
| 1 | usd |
canonical CBOR {"micros": int}: dollars, six decimals |
| 2 | joules |
canonical CBOR {"microjoules": uint} |
| 16 | verifiable-credential |
the credential's JCS encoding (RFC 8785) |
| 17 | bbs-credential |
canonical CBOR of the BBS+ credential body |
| 18 | bbs-presentation |
canonical CBOR of the BBS+ presentation body |
| 19 | crdt-snapshot |
canonical CBOR of a CRDT document snapshot |
| 20 | crdt-delta |
canonical CBOR of a CRDT op batch |
| 21 | mdoc |
the mobile document's CBOR, as its issuer signed it |
Tags 3–15 are reserved for the substrate's own cargo classes. New types are added to this table.
7. Controller signatures
{
"establishment": uint,
"signatures": [{"index": uint, "signature": bytes}, ...]
}
establishmentis the sequence number of the signer's establishment event — inception or rotation — whose key set made the signatures.- Each signature is by key
indexof that set: the key's one-byte algorithm identifier, then the signature. For Ed25519 that is0x01followed by 64 bytes.
A controller signature is valid if and only if all of the following hold:
- The named establishment event is in the signer's verified key-event log (identity spec §4).
- Every listed signature verifies. One bad signature refuses the whole set, so a set means one thing however it is relayed.
- No index appears twice.
- At least the event's threshold
ktof keys signed.
Ed25519 is verified strictly. A key whose encoded y is not below the field prime is refused (RFC 8032 §5.1.3), as is a scalar S outside [0, L) (§5.1.7). So is any small-order key or commitment.
8. Verification
A verifier holding an envelope's bytes and its parties' key-event logs runs treatise §10.2's procedure:
-
Decode. The bytes are canonical, and exactly the encoding of an envelope: every member is defined by this section and written in the type and form it gives — a byte string, not an array of integers, where bytes belong. Both SAIDs check: the envelope's
dagainst the envelope, then the provenance'sdagainst the provenance. -
Provenance. The issuer's signature is valid over the attested fields (§3, §7).
-
Chain. For each transition in order:
- its
previous_history_hashequals the running history hash; - its
fromis the current owner (the recipient, for the first transition); - its frame exceeds the previous transition's, unless the previous transition named a
destination; - its signature is valid.
The running hash then advances, and
tobecomes the owner. - its
-
Result. The identity, the issuer, the current owner and the current history hash.
Each failure has an error class, shared with the conformance pack:
said_mismatch— a SAID does not check.not_an_envelope— an undefined member, or a member of the wrong type or form.unknown_key_state— the named establishment event is not in the verifier's logs.signature_invalid,below_threshold,duplicate_key_index,key_index_out_of_range,algorithm_mismatch,unsupported_algorithm,malformed_signature,malformed_verifying_key— a signature rule fails.broken_history_chain— a transition does not continue the history it follows.not_current_owner— a transition is not signed away by the current owner.frame_not_increasing— two transitions in one cluster share a frame, or frames go backwards.- A canonical-encoding class (identity spec §2.3) — the bytes are not canonical.
9. What the cluster decides
Four questions depend on cluster state rather than on the envelope:
- whether the issuer is registered for the cargo type (treatise §10.2 step 3, §15);
- whether a signer's establishment event was the one in force at a transition's frame;
- whether the byte may enter or leave the cluster — a destination it peers with, a source it trusts (treatise §11.3.3);
- how nodes account the joule cost (treatise §8.6).
The cluster answers these when it applies a mint, import or transfer (treatise §9.4 step 5). The cluster state section fixes how:
- the registry lists the issuers for each cargo type;
- a signature must name the establishment event in force when it is applied;
- a byte moves only between peer clusters, on a certificate from the source cluster's nodes;
- joule cost is reported beside the cluster state, not in it.
10. Implementation and vectors
smart-byte-rs/crates/smart-byte-core implements this section: envelope.rs, provenance.rs, ownership.rs and cargo.rs, with signatures in keys.rs. Key-event logs are replayed by smart-byte-rs/crates/smart-byte-keri-witness. The conformance vectors are ../conformance/v1/envelope_vectors.json, built on the key-event logs of ../conformance/v1/kel_vectors.json.