openpay/
compliance/ PCI-DSS readiness package (scope-map,
pci-zero-architecture, hsm-kms-guidance,
qsa-checklist), citing PCI-DSS v4.0.1
requirements. (#1)
examples/pci-zero/ Runnable example demonstrating a deployment
where merchant code never touches a PAN. (#1)
crates/op-core/ Tokenized<Card> typestate + NetworkToken
+ CardNetwork + Cryptogram. (#2)
crates/op-rails-card/network_token/
NetworkTokenProvider trait + VTS adapter
+ MDES adapter, with deterministic stubs
behind a 'live' feature flag. (#2)
crates/op-driver-sdk/conformance/network_token.rs
Conformance harness for third-party
NetworkTokenProvider implementations. (#2)
crates/op-cli/src/ledger/ 'openpay ledger as-of' subcommand for
bi-temporal queries. (#3)
crates/op-orchestrator/src/telemetry.rs
OpenTelemetry span emission per
Payment<S> transition with semantic-
conventions tags. (#3)
docs/observability/ Grafana dashboard JSON + wiring guide
for the bi-temporal ledger lens. (#3)
smart-byte/
smart-byte-rs/ Rust reference implementation: envelope
encode/decode (canonical CBOR + BLAKE3
content addressing), Ed25519 sign +
verify, lockstep BFT commit MVP, Iroh
transport scaffold, CLI. (#4)
spec/identity_and_key_rotation.md
Adopts KERI SAIDs + key-event log
format; explicitly does not adopt ACDC
chaining wholesale. (#5)
conformance/v1/ 21 test vectors covering canonicalisation,
SAID computation, signature verification,
lockstep commit, gossip propagation.
CI gate enforces spec/vector coupling. (#6)
eoc/
eoc-rs/ Rust + WASM reference implementation:
4-stage cascade (cache -> kv -> graph ->
neural), joule counters via RAPL / NVML /
powermetrics, CLI, wasm-bindgen surface.
18 tests pass. (#7)
eoc-bench/ Benchmark harness, MT-Bench and Router
case sets, 'joules per MT-Bench point'
metric, 'eoc-bench run | compare' CLI. (#8)
standards/sci-for-ai-submission.md
Submission proposal mapping EOC's
receipt schema to GSF SCI-for-AI. (#9)
cookbook/ HuggingFace-style cookbook (markdown +
runnable Python) demonstrating EOC's
cascade with HF AI Energy Score. (#9)
docs/bench-v0.md Reproducibility doc for eoc-bench.
Workspace state:
- openpay clippy -- -D warnings: clean across all touched crates
- smart-byte-rs cargo test --workspace + clippy: clean
- eoc-rs cargo test --workspace + clippy: clean (18 tests)
- eoc-bench cargo check --workspace + clippy + 15 tests: clean
Rust 1.95.0 edition 2024 pinned at the open-standards root via
rust-toolchain.toml; every workspace inherits.
Closes #1 #2 #3 #4 #5 #6 #7 #8 #9.
22 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.
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.
- Indefinite-length items are forbidden.
- The encoded form is the only form over which the digest is computed; serializations that round-trip through other formats (JSON, MessagePack) must re-encode to canonical CBOR before digest computation.
These are the deterministic encoding rules from RFC 8949 §4.2.1 with the tag-and-float 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 62 bytes.
BLAKE3-256 of those 62 bytes is (worked example; verifiers should reproduce against the test vector file referenced in §2.5):
b3: 3f a1 7b 9c 5d 2e 88 41 ... (32 bytes)
The 32-byte digest encoded in lowercase bytewise base32 (no padding) yields 52 characters, prefixed with E to produce the SAID:
E h7 q xt 4 ... (53 ASCII chars total — full string in conformance vectors)
The final object, with the placeholder replaced, is:
{
"d": "E<52-char base32 of the BLAKE3-256 digest>",
"n": 1
}
Any verifier in possession of the object replaces d with 53 # characters, re-encodes canonically, computes BLAKE3-256, base32-encodes 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/said_vectors.json (created in issue #6). Implementers MUST pass every vector. The suite covers the empty-map case, the one-field case, the worked example above, several envelope-shaped objects from treatise §8, and one adversarial case where a non-canonical CBOR encoding produces a different digest (to verify that implementations reject non-canonical input rather than silently re-encoding).
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. 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.
The controller AID replaces raw ed25519 public keys in envelope issuer_pubkey and ownership-chain from_pubkey / to_pubkey fields (treatise §8.3, §8.4). Each AID dereferences via §4 replay to the current signing key set and threshold; envelope signatures are verified against the replayed set, not a static key. A future revision of §8 will harmonize terminology; until then, this section is authoritative.
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 as malformed. The reservation exists so the event-type field is forward-compatible; the protocol is not yet specified.
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.- Each entry in
kis a public-key encoding prefixed with the one-byte algorithm identifier from treatise §8.3 (0x01for ed25519; remaining range reserved for post-quantum schemes such as ML-DSA). Order matters; the same key in a different position produces a differentncommitment and breaks the chain. 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. An emptynsignals the controller does not intend to rotate further; thereafter onlyixnevents are accepted. ais populated forixnand empty foricpandrot.
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 full prefixed public-key encoding the controller intends to use after the next rotation), the entry n[i] is:
SAID({"d": "<placeholder>", "k": "<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
Each event is signed by the key set established by the previous event (or, for the inception event, by the keys revealed in its own k field — the inception is self-signed). The signature(s) are carried outside the event map, in a wrapper structure that pairs the canonical CBOR-encoded event with an array of (key-index, signature) tuples. The wrapper is not part of the SAID computation; the event's d covers the event body only, and the signatures are evaluated against that body.
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.
-
SAID verification. Replace the event's
dfield with the 53-character placeholder, canonically re-encode per §2.3, compute BLAKE3-256, base32-encode with theEprefix, and confirm equality with the assertedd. Reject the event if the recomputed SAID does not match. -
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: confirmpis the empty string,ais the empty array,sis0. - For
rot: confirm the keys inkhave per-key SAIDs (per §3.4) matching the previous event'snarray, in order. Reject the event if any per-key SAID mismatches or iflen(k) != len(previous.n). - 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 meet the threshold of the current signing key set. The current signing key set is defined as:
- For the inception event: the keys revealed in the inception event's own
kfield. - For every subsequent event: the keys established by the most recent prior event whose type changes keys, which is the inception event or the most recent
rotevent. Anixnevent does not change the current key set.
Confirm that at least
ktof the signatures in the event's wrapper are valid signatures by distinct keys in the current key set over the canonical CBOR encoding of the event body. Reject the event if fewer thanktdistinct valid signatures are present. - For the inception event: the keys revealed in the inception event's own
-
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.
The current signing key set after full replay is the set against which envelope and ownership-chain signatures (treatise §8.3, §8.4) are validated. An envelope signed under a since-rotated key remains valid at the frame in which it was signed, because verification chains back through the log to the event that established the key set in force at that frame — the property that lets a byte's ownership chain span a rotation without a break.
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/said.rs— SAID computation, placeholder substitution, canonical CBOR encoding, per-key SAID derivation, and the conformance-vector harness referenced in §2.5.smart-byte-rs/crates/smart-byte-core/src/keri.rs— key-event log: event type definitions, the wrapper that pairs events with signatures, the verifier algorithm in §4, and helpers for emittingicp,rot, andixnevents from an in-memory controller state.
If these modules are not yet present in smart-byte-rs at the time this section lands, the implementer should treat the function signatures and field-name conventions in §§2–4 as the v1 interface contract and implement against them directly; the reference crate will conform to those signatures when it lands. The conformance test vectors in ../conformance/said_vectors.json (issue #6) are the authoritative reference, independent of any single language's implementation.
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/said_vectors.json); all paths are relative to this file.