Canonical encoding contract
This document turns the semantic contracts in the specification and its state-machine, client API, and settlement companions into the selected wire contract. Tables marked Proposed identify engineering selections that have not yet passed executable conformance; they are not pending product decisions. Tables marked Fixed restate an accepted specification or pinned-source rule. Section 15 lists the remaining qualification inputs that prevent a byte-complete claim.
This document is the normative wire contract. Canonical Rust schema and release types are its executable authority. The format manifest records the selected revision, tags, domains, dependency pins, and artifact digests; it is not a separate schema language and does not generate Rust. Transport docs, bindings, and metadata are generated from the Rust registry and checked against reviewed vectors. Source-language declaration order, RPC JSON, protobuf field numbers, and a dependency's working tree never define canonical bytes.
1. Primitive registry
Unless a row says otherwise, integers use fixed-width unsigned big-endian bytes. Arithmetic rejects overflow and the semantic maximum where the specification reserves it.
| Type | Bytes | Encoding and use | Status |
|---|---|---|---|
U8, U16, U32, U64, U128 |
1, 2, 4, 8, 16 | Unsigned big-endian | Proposed |
Amount256 |
32 | Unsigned big-endian; credits, fees, deposits, prices, max_charge, and ledger values |
Fixed by specification §16.3 |
Bool |
1 | 00 false, 01 true; every other byte invalid |
Fixed by pin |
Option<T> |
variable | 00; or 01 || T; every other tag invalid |
Fixed by pin |
Digest32 |
32 | Full BLAKE3, Keccak256, or registered external digest according to the containing field; Keccak256 exactly on the proof path of specification section 6.2 | Fixed |
AccountId |
20 | Raw address bytes | Fixed |
Height, Epoch, View, Sequence, Revision, Generation, Counter, TimestampMs |
8 | U64; reject u64::MAX where revision 4 requires a reset/end |
Proposed |
CommandIndex, CommitmentIndex, SameCommandIndex |
4 | U32 |
Proposed |
NativeMmrLocation |
variable | Exact commonware_storage::merkle::Location<mmr::Family> codec: shortest-form UInt<u64> with the MMR range check |
Fixed by pin |
RulesetId |
8 | U64; genesis chooses the first ID, and every non-parameter continuing release increments it by one |
Fixed by specification §18.3 |
SchemaId, ProfileId, PriceScheduleId, WorkTableId, LayoutId, CodecId, SchemeId, ReleaseDigest, KeyId, ProjectId |
32 | Full Digest32; no truncation |
Proposed |
EnvelopeVersion, SchemaRevision |
2 | U16 |
Proposed |
| enum tag | 1 | Manifest-owned U8; unassigned and retired values are invalid |
Proposed |
| fixed bytes | stated size | Bytes without a length prefix | Fixed by pin |
BoundedBytes<B> |
variable | shortest-form Commonware usize count followed by bytes; count must be 0..=B |
Proposed |
NonEmptyBytes<B> |
variable | same framing; count must be 1..=B |
Proposed |
BoundedUtf8<B> |
variable | BoundedBytes<B> plus strict UTF-8 and field-specific character rules |
Proposed |
BoundedVec<T,C,B> |
variable | shortest-form Commonware usize item count followed by items; at most C items and B aggregate encoded bytes |
Proposed |
SortedVec<T,C,B> |
variable | BoundedVec; canonical encodings strictly increase and contain no duplicate |
Proposed |
Amount256 is a 32-byte scalar, not a Commonware usize and not a varint. Implementations use
checked 256-bit arithmetic for addition and subtraction. Multiplication either widens before the
range check or rejects an overflowing intermediate. The settlement contract's slot-zero uint256
cumulative therefore maps without narrowing to AccountLedger.credited.
At the pin, commonware-codec supplies the selected mechanics in codec/src/types/primitives.rs,
codec/src/types/vec.rs, and codec/src/varint.rs. The collection count is the pinned usize
codec, which writes a shortest-form unsigned u32 varint. It is only collection framing. No
semantic protocol integer uses it. Decoders apply both C and B before allocation and consume the
complete enclosing slice.
2. Tag registry
Tags are explicit values in this contract and the canonical Rust registry. Adding a variant consumes a new value. Removing one reserves its
old value forever. Values 0x80..=0xff are reserved for a future format revision and are invalid in
revision 1.
2.1 Command and envelope tags
| Value | CommandPayload |
|---|---|
0x00 |
INIT_ACCOUNT |
0x01 |
ROOT_CONFIGURE |
0x02 |
KEY_AUTHORIZE |
0x03 |
KEY_REVOKE |
0x04 |
PROJECT_CREATE |
0x05 |
PROJECT_SET_METADATA |
0x06 |
PROJECT_SET_STATUS |
0x07 |
ROLE_SET |
0x08 |
TRANSFER_OFFER |
0x09 |
TRANSFER_ACCEPT |
0x0a |
PUSH |
0x0b |
RECLAIM |
0x0c |
USERNAME_CLAIM |
0x0d |
USERNAME_RELEASE |
0x0e |
IMPORT_CREDITS |
0x0f |
SCHEDULE_RELEASE |
0x10 |
VALIDATOR_SET_SCHEDULE |
| Registry | Allocations |
|---|---|
IntentAuthority |
00 Account, 01 PermissionlessSystem, 02 GovernanceSystem |
EnvelopeAuthorization |
00 Genesis, 01 Root, 02 Delegated, 03 None, 04 Governance |
UnsignedEvidence |
00 None, 01 PushWitnesses, 02 TempoCredit, 03 TempoRefresh |
ConsensusProfile |
00 FixedEpochZeroV1, 01 DynamicReshareV1 |
KeyFamily |
00 Secp256k1, 01 Ed25519Sshsig, 02 P256Direct, 03 WebAuthnP256 |
GenesisAuthorization |
00 SingleKey, 01 Configured |
RootAuthorizationNode |
00 SingleKey, 01 Configured |
OwnerApproval |
00 Key, 01 Account |
VerificationPolicy |
00 None, 01 WebAuthn |
Capability |
00 Social, 01 Write, 02 Project, 03 Account |
ProjectStatus |
00 Active, 01 Frozen |
ProjectRoleKind |
00 Writer, 01 Maintainer |
Expected<T> |
00 Absent, 01 Present(T) |
KeyAuthorize |
00 Authorize, 01 Renew |
KeyRevoke |
00 One, 01 All |
RoleSet |
00 Grant, 01 Revoke |
TransferOfferAction |
00 Set, 01 Clear |
RefOp |
00 Create, 01 Set, 02 Delete |
MembershipWitness |
00 SameCommand, 01 Location, 02 Proof |
ReclaimTarget |
00 DelegatedKey, 01 TransferOffer, 02 ProjectRole, 03 UsernameLease |
UsernameClaim |
00 Claim, 01 Extend, 02 Restore, 03 Rename |
ImportCredits |
00 Credit, 01 Refresh |
ScheduleRelease |
00 Activate, 01 Defer, 02 Cancel, 03 EmergencyActivate |
ValidatorSetSchedule |
00 Append, 01 Cancel |
ReleaseClass |
00 Parameter, 01 FormatPreserving, 02 VersionedState, 03 Reset |
ReleaseScheduleEntry |
00 Activate, 01 Defer, 02 Cancel, 03 EmergencyActivate |
ActivationInstaller |
00 WriteSettlementAnchor, 01 RotateGovernanceSet, 02 ReserveValidatorKey, 03 QualifiedHandler |
ValidatorScheduleRecord |
00 Schedule, 01 Cancel, 02 KeyReservation, 03 ScheduleState |
ProfileEpochInfo |
00 Fixed, 01 Dynamic |
FinalityEvidence |
00 Direct, 01 DescendantAncestry |
StateFamilyTag |
00 AccountLedger, 01 AccountRoot, 02 DelegatedKey, 03 Project, 04 ProjectMetadata, 05 SlugClaim, 06 ProjectRole, 07 TransferOffer, 08 Ref, 09 UsernameLease, 0a AccountUsername, 0b SettlementAnchor, 0c ProtocolReleaseEntry, 0d ProtocolScheduleState, 0e ValidatorScheduleEntry |
HashAlgorithmTag |
00 Blake3Flat, 01 MkitBmtV1, 02 GitSha1, 03 GitSha256 |
CanonicalDecodingTag |
00 MkitObjectV1, 01 GitLooseObjectV1 |
ReleaseRecordKind |
00 Descriptor, 01 ScheduleEntry, 02 ActivationMarker |
ValidatorEntryKind |
00 Schedule, 01 Cancel, 02 KeyReservation, 03 ScheduleState |
PublicKeyV1 |
00 Secp256k1, 01 Ed25519Sshsig, 02 P256Direct, 03 WebAuthnP256 |
SignatureV1 |
00 Secp256k1, 01 Ed25519Sshsig, 02 P256Direct, 03 WebAuthnP256 |
ProtocolReleaseRecordV1 |
00 Descriptor, 01 ScheduleEntry, 02 ActivationMarker |
ProofPayloadV1 |
00 CurrentState, 01 Membership, 02 Command, 03 Finality, 04 ReleaseLineage, 05 PublicationReceipt |
DkgRevealModeV1 |
01 ContributorAware; 00 is retired and invalid |
SettlementConfigurationV1 |
00 DisabledDiagnostic, 01 Enabled |
3. Common wrappers
3.1 Command intent and complete envelope
Field order is the order shown.
CommandIntentV1 {
chain_id: Digest32,
envelope_version: U16 = 1,
ruleset_id: RulesetId,
price_schedule_id: PriceScheduleId,
authority: IntentAuthority,
payload: CommandPayload,
}
IntentAuthority::Account {
actor: AccountId,
key_id: KeyId,
authorization_generation: Generation,
sequence: Sequence,
max_charge: Amount256,
valid_before_timestamp_ms: Option<TimestampMs>,
}
IntentAuthority::PermissionlessSystem {}
IntentAuthority::GovernanceSystem { governance_revision: Revision, governance_nonce: U64 }
SignedCommandV1 {
intent: CommandIntentV1,
authorization: EnvelopeAuthorization,
evidence: UnsignedEvidence,
}
EnvelopeAuthorization::Genesis { witness: GenesisAuthorizationV1 }
EnvelopeAuthorization::Root { witness: RootAuthorizationNodeV1 }
EnvelopeAuthorization::Delegated { witness: DelegatedAuthorizationV1 }
EnvelopeAuthorization::None {}
EnvelopeAuthorization::Governance { witness: GovernanceAuthorizationV1 }
UnsignedEvidence::None {}
UnsignedEvidence::PushWitnesses { evidence: PushEvidenceV1 }
UnsignedEvidence::TempoCredit { evidence: TempoCreditEvidenceV1 }
UnsignedEvidence::TempoRefresh { evidence: TempoRefreshEvidenceV1 }
This union keeps account replay and charging fields out of permissionless and governance commands.
It does not use zero-address or zero-sequence sentinels. The command tag is the first byte of
payload; it is not duplicated beside the payload.
The allowed cross-product is closed:
| Command | Authority | Authorization | Evidence |
|---|---|---|---|
INIT_ACCOUNT |
Account |
Genesis |
None |
account commands other than INIT_ACCOUNT |
Account |
Root or Delegated, subject to semantic capability |
PushWitnesses only for PUSH, otherwise None |
IMPORT_CREDITS.Credit |
PermissionlessSystem |
None |
TempoCredit |
IMPORT_CREDITS.Refresh |
PermissionlessSystem |
None |
TempoRefresh |
| both governance commands | GovernanceSystem |
Governance |
None |
Every other combination is invalid before cryptography. intent_digest covers the complete
CommandIntentV1. command_id covers the complete SignedCommandV1, including replaceable PUSH
and Tempo evidence.
For root or genesis authorization, key_id is the account's permanent root slot:
BLAKE3("makechain.root-slot" || actor). Its authorization generation is the exact current
AccountRoot.version, including zero for INIT_ACCOUNT; a root command remains bound to the version
read before execution even when ROOT_CONFIGURE advances it. sequence advances the permanent
AccountRoot.root_sequence mark and is never reset by reconfiguration.
3.2 Key and signature wrappers
PublicKeyV1::Secp256k1 { compressed_sec1: FixedBytes33 }
PublicKeyV1::Ed25519Sshsig { key: FixedBytes32 }
PublicKeyV1::P256Direct { compressed_sec1: FixedBytes33 }
PublicKeyV1::WebAuthnP256 {
compressed_sec1: FixedBytes33,
credential_id: NonEmptyBytes<max_webauthn_credential_id_bytes>,
}
AccountScopedKeyV1 { account: AccountId, public_key: PublicKeyV1 }
KeyIdV1 = BLAKE3(registered key-id domain || canonical(AccountScopedKeyV1))
SignatureV1::Secp256k1 { r: FixedBytes32, s: FixedBytes32, recovery_id: U8 }
SignatureV1::Ed25519Sshsig { signature: FixedBytes64 }
SignatureV1::P256Direct { r: FixedBytes32, s: FixedBytes32 }
SignatureV1::WebAuthnP256 {
authenticator_data: FixedBytes37,
client_data_json: BoundedBytes<max_webauthn_client_data_json_bytes>,
r: FixedBytes32,
s: FixedBytes32,
}
VerificationPolicyV1::None {}
VerificationPolicyV1::WebAuthn {
rp_id: NonEmptyBytes<max_webauthn_rp_id_bytes>,
origins: SortedVec<BoundedUtf8<max_webauthn_origin_bytes>,max_webauthn_origins,max_webauthn_origins_bytes>,
}
VerificationPolicyV1::None is required for secp256k1, SSHSIG, and P256 direct. The WebAuthn family
requires the WebAuthn policy. SEC1 points must be compressed, canonical, on-curve, and non-identity.
ECDSA signatures require nonzero scalars and low S. The recovery ID is 0 or 1.
GenesisAuthorization::SingleKey {
compressed_secp256k1_key: FixedBytes33,
signature: SignatureV1::Secp256k1,
}
GenesisAuthorization::Configured {
genesis_configuration: OwnerConfigurationV1,
node: RootAuthorizationNodeV1::Configured,
}
DelegatedAuthorizationV1 { signature: SignatureV1 }
RootAuthorizationNodeV1::SingleKey {
account: AccountId,
compressed_secp256k1_key: FixedBytes33,
signature: SignatureV1::Secp256k1,
}
RootAuthorizationNodeV1::Configured {
account: AccountId,
configuration: OwnerConfigurationV1,
approvals: BoundedVec<OwnerApprovalV1, max_root_approvals, max_root_approvals_bytes>,
}
OwnerApprovalV1::Key { owner_index: U8, public_key: PublicKeyV1, signature: SignatureV1 }
OwnerApprovalV1::Account // tag 0x01 stays reserved; invalid under every launch ruleset
GovernanceAuthorizationV1 {
governance_revision: Revision,
approvals: SortedVec<GovernanceApprovalV1, max_governance_keys, max_governance_witness_bytes>,
}
GovernanceApprovalV1 { key_index: U16, signature: FixedBytes64 }
SingleKey is valid only while the addressed account is at version 0 with an unset commitment; its
key must derive the account's Ethereum address. The first reconfiguration permanently removes this
branch. A configured genesis account carries the configuration needed to recompute its CREATE2
address, and is valid only once the chainspec multisig_recovery_factory value is set. P256 and WebAuthn can be owners inside that configuration; this revision does not define them
as standalone single-key account identities.
Root approvals increase by owner_index, contain no duplicate, reach the threshold on the last
approval, and contain no later approval. Every approval is a key approval: the node has one level, and an owner-account approval
(OwnerApprovalV1::Account) is invalid.
Governance approvals increase by key index into the current governance set and contain no duplicate.
Ordinary operations contain the shortest prefix of supplied approvals whose count reaches the
current threshold; no approval follows the threshold-crossing signature. Emergency activation
contains every current key exactly once. A signature verifies raw Ed25519 over
governance_digest = BLAKE3("makechain.governance" || intent_digest). The intent already binds the
governance revision and nonce. This proposed domain is distinct from account and consensus signing.
The revision-1 SSHSIG profile stores only the raw Ed25519 signature. The verifier reconstructs the
OpenSSH PROTOCOL.sshsig signed data with magic SSHSIG, namespace
makechain.command:<lowercase chain_id hex>, empty reserved bytes, hash algorithm sha512, and
message hash SHA-512(intent_digest). The public-key algorithm is exactly ssh-ed25519; security-key
and certificate key types are invalid.
The revision-1 WebAuthn profile accepts a 37-byte assertion authenticator data value: 32-byte
rpIdHash, one flags byte, and four-byte big-endian counter. UP and UV must both be set; AT and ED
must both be clear; BE and BS must be a valid WebAuthn pair; the counter is ignored. The client data
must be one UTF-8 JSON object with exactly type, challenge, and origin, plus optional
crossOrigin; duplicate or unknown keys are invalid. type is webauthn.get, crossOrigin is
absent or false, challenge is unpadded base64url that decodes to the required 32-byte digest, and
origin exactly matches an authorized origin. Verification hashes the exact received JSON bytes and
never reserializes them.
The proposed EIP-712 domain is
EIP712Domain(string name,string version,uint256 chainId,bytes32 salt) with name Makechain,
version 1, Tempo host chainId, and Makechain chain_id as salt. Each account command has its
own primary type. A top owner signs
Makechain<Command>(bytes20 account,uint64 configVersion,bytes32 intentDigest,<Command>Action action).
There is no nested owner type. The command-specific action exposes
these common fields in order:
bytes32 chainId, uint16 envelopeVersion, uint64 rulesetId, bytes32 priceScheduleId,
address actor, bytes32 keyId, uint64 authorizationGeneration, uint64 sequence,
uint256 maxCharge, bool validBeforeTimestampMsPresent, uint64 validBeforeTimestampMs, and
uint8 commandTag. The selected command's action then appends its section 4 payload fields in table
order, beginning with uint8 variantTag for a tagged subvariant. It never replaces those fields with
a payload or action hash. AccountId maps to EIP-712 address; 32-byte identifiers map to
bytes32; fixed integers and amount map to the same-width uintN; enums map to uint8; text maps
to string; other byte strings map to bytes; structs and vectors map to named structs and typed
arrays recursively. ProjectAuthorityContextV1 expands in its stated field order. Optional fields use an
explicit bool <field>Present followed by the field's zero value when absent, and the verifier
requires the zero value in that case. Tagged unions expose the variant tag plus every field of the
selected variant. bytes and string fields use EIP-712's standard hash; arrays use the standard
concatenated element-struct-hash rule. The canonical Rust registry generates the literal
primary/action type strings; vectors publish them for every command and variant. The verifier derives all
typed fields from canonical decoded intent bytes, never from wallet-supplied typed JSON.
3.3 Reusable domain values
CommitmentV1 { scheme_id: SchemeId, native_id: BoundedBytes<max_native_id_bytes> }
OwnerEntryV1 { owner_address: AccountId, weight: U8 }
OwnerConfigurationV1 {
salt: FixedBytes32,
version: U64,
threshold: U8,
owners: BoundedVec<OwnerEntryV1, 48, max_owner_configuration_bytes>,
}
ValidityWindowV1 { starts_at_ms: TimestampMs, ends_at_ms: TimestampMs }
DepositV1 { payer: AccountId, paid: Amount256 }
IncarnationV1 { owner: AccountId, allocated_counter: Counter }
Owners increase by raw 20-byte address; equal addresses are duplicates even when weights differ. The configuration never stores a key family or public key. A key approval supplies its family/key and proves that its derived address equals the indexed owner address. An owner is always a key address; a configured account is invalid as an owner, and Ed25519 is invalid as an owner. Every weight is nonzero, weights sum to at most 255, and the threshold is from 1 to 8 and does not exceed the total weight. A version-zero configuration must not list its own derived address; a positive-version configuration may list the account's original key address.
Secp256k1 uses the standard Ethereum address of the uncompressed public key. P256 direct and
WebAuthn decompress the canonical SEC1 key to 32-byte X and 32-byte Y and derive
keccak256(X || Y)[12..32], with no SEC1 prefix. This P256 rule is pinned to Tempo commit
25b216a661c2cac72807174583371b582b75c286,
crates/primitives/src/transaction/tt_signature.rs:805-814; owner recovery at lines 280-332 uses
the same helper for both families.
The adopted account profile is the Tempo TIP-1107 cluster at commit
e7ba69eb35349d49633e3637f751ce8a3558ebb9 (the text of the open upstream proposal; parent TIP-1107, file SHA-256
ff829cf882848c6722aff3567e0cfdbac033829cacfabc8e6860f222313686b9). The adopted children and their file SHA-256 values are:
| TIP | Adopted for | File SHA-256 |
|---|---|---|
| TIP-1114 | configuration schema, owner and threshold bounds, approval order and exact-stop rule | e1e72eca900e578c2bc83b2990f51c88596657bfa6adc2451093c8e9045c6423 |
| TIP-1110 | address derivation (account_salt, CREATE2) |
e19e6f369903cc429e0dc965a7e1d3b89d6dc2b37481d94f0cce035c769c1513 |
| TIP-1108 | configuration commitment preimage | 9c05d32afb021251d01e9d51dd711f36857ec41e6876d4c4b01b5a6251176bcc |
| TIP-1113 | in-place migration salt and version 1 | bc9a9bf194c508569e6dfc0aeebd7d67b436867483bc1916c74d4bf39120689c |
| TIP-1112 | permanent root-key disablement after the first configuration | abe642539fd8444be832075dbb51369f118f3fd87e33bac1bad016be3a135639 |
TIP-1109 (configuration precompile, SHA-256 455186f6df8f2f0151ea55914305dc3ca0f08f439eeac75fbad7aaaa82a9f619) is read for its error conditions only, and
TIP-1111 (access keys) is not adopted. The exact formulas are:
account_salt = keccak256(
"tempo:multisig:account" || salt32 || threshold_u8 || owner_count_u8 ||
each(owner_address20 || weight_u8)
)
config_commitment = keccak256(
"tempo:multisig:config" || salt32 || version_u64_be || threshold_u8 || owner_count_u8 ||
each(owner_address20 || weight_u8)
)
account = keccak256(0xff || factory20 || account_salt || wallet_init_code_hash)[12..32]. The
wallet init-code hash is 0x583cc63a2e37f645b43eac911b1a6d6de08b83abdc308c61364edda8cfc3bd37. The
factory is the chainspec value multisig_recovery_factory: TIP-1110 marks
MULTISIG_RECOVERY_FACTORY as unpublished until Tempo's release activates and supersedes the
earlier public factory address, so the value is unset at launch, the configured genesis form is
invalid while it is unset, and a release sets it once as a governed set-once parameter. A derived address equal to
the zero address is invalid.
The codec's vector count must equal the u8 count used by the Keccak formula. Configuration updates
preserve salt. Under TIP-1113 the first single-key upgrade installs configuration version 1 with
salt = keccak256("tempo:multisig:upgrade" || account_address20); later updates preserve that salt.
A slug and a username are valid only in canonical form: ASCII lowercase letters a to z, digits
0 to 9, and the hyphen, neither starting nor ending with a hyphen and never containing two
consecutive hyphens, within max_slug_bytes and the username length bounds of section 13. No
normalization is performed: a string outside canonical form is invalid rather than normalized, so
the stored value is the submitted bytes. This is the field-specific character rule BoundedUtf8
names for those two fields.
This commit pins Makechain's adopted account semantics. The upstream proposal is an open draft and does not establish a live Tempo feature; a later change to it reaches Makechain only through a release that re-pins it.
4. Command payload registry
The payload tag precedes the fields below. Variant tags precede variant fields, and a variant
carries its tag byte at every position it appears, including inside another variant's fields. Expected<T> uses
the tags in section 2. Every project-authorized payload starts with its signed authority context so
execution can reject stale control or grants without interpreting later fields:
ProjectAuthorityContextV1 {
project_id: ProjectId,
control_generation: Generation,
role_grant_generation: Option<Generation>,
}
| Command / variant | Fields in canonical order after tags | Status |
|---|---|---|
INIT_ACCOUNT |
no payload fields | Proposed |
ROOT_CONFIGURE |
expected_root_version: U64, new_configuration: OwnerConfigurationV1 |
Proposed |
KEY_AUTHORIZE.Authorize |
expected_keychain_revision: Revision, public_key: PublicKeyV1, capability: Capability, project_scope: Option<ProjectId>, validity_window: Option<ValidityWindowV1>, verification_policy: VerificationPolicyV1, expected_predecessor: Expected<Generation> |
Proposed |
KEY_AUTHORIZE.Renew |
expected_keychain_revision: Revision, key_id: KeyId, expected_generation: Generation |
Proposed |
KEY_REVOKE.One |
expected_keychain_revision: Revision, key_id: KeyId, expected_generation: Generation |
Proposed |
KEY_REVOKE.All |
expected_keychain_revision: Revision, expected_delegation_epoch: Epoch |
Proposed |
PROJECT_CREATE |
client_salt: FixedBytes32, scheme_id: SchemeId, slug: BoundedUtf8<max_slug_bytes>, description: BoundedUtf8<max_project_description_bytes>, origin: Option<CommitmentV1> |
Proposed |
PROJECT_SET_METADATA |
context: ProjectAuthorityContextV1, slug: BoundedUtf8<max_slug_bytes>, description: BoundedUtf8<max_project_description_bytes>, expected_project_mutation_counter: Option<Counter> |
Proposed |
PROJECT_SET_STATUS |
context: ProjectAuthorityContextV1, expected_lifecycle_revision: Revision, status: ProjectStatus |
Proposed |
ROLE_SET.Grant |
context: ProjectAuthorityContextV1, grantee: AccountId, role: ProjectRoleKind, expected_predecessor: Expected<Generation>, expected_project_mutation_counter: Option<Counter> |
Proposed |
ROLE_SET.Revoke |
context: ProjectAuthorityContextV1, grantee: AccountId, expected_generation: Generation |
Proposed |
TRANSFER_OFFER.Set |
context: ProjectAuthorityContextV1, destination: AccountId, expected_offer: Expected<Revision>, expected_project_mutation_counter: Option<Counter> |
Proposed |
TRANSFER_OFFER.Clear |
context: ProjectAuthorityContextV1, expected_offer_revision: Revision |
Proposed |
TRANSFER_ACCEPT |
project_id: ProjectId, expected_control_generation: Generation, expected_offer_revision: Revision, destination_slug: BoundedUtf8<max_slug_bytes>, expected_project_mutation_counter: Counter |
Proposed |
PUSH |
context: ProjectAuthorityContextV1, expected_project_mutation_counter: Option<Counter>, commitments: BoundedVec<CommitmentV1,max_commitments_per_push,max_commitments_bytes>, ref_ops: BoundedVec<RefOpV1,max_ref_ops_per_push,max_ref_ops_bytes> |
Proposed |
RECLAIM.DelegatedKey |
account: AccountId, key_id: KeyId, expected_generation: Generation |
Proposed |
RECLAIM.TransferOffer |
project_id: ProjectId, expected_revision: Revision |
Proposed |
RECLAIM.ProjectRole |
project_id: ProjectId, grantee: AccountId, expected_generation: Generation |
Proposed |
RECLAIM.UsernameLease |
name: BoundedUtf8<max_username_bytes>, expected_incarnation: IncarnationV1, expected_revision: Revision |
Proposed |
USERNAME_CLAIM.Claim |
name: BoundedUtf8<max_username_bytes>, years: U16, expected_allocation_counter: Counter, expected_lease: Expected<UsernameLeaseGuardV1> |
Proposed |
USERNAME_CLAIM.Extend |
name: BoundedUtf8<max_username_bytes>, expected_incarnation: IncarnationV1, expected_revision: Revision, years: U16 |
Proposed |
USERNAME_CLAIM.Restore |
name: BoundedUtf8<max_username_bytes>, expected_incarnation: IncarnationV1, expected_revision: Revision, years: U16 |
Proposed |
USERNAME_CLAIM.Rename |
source: UsernameLeaseGuardV1, destination_name: BoundedUtf8<max_username_bytes>, destination_years: U16, expected_allocation_counter: Counter, expected_destination: Expected<UsernameLeaseGuardV1> |
Proposed |
USERNAME_RELEASE |
name: BoundedUtf8<max_username_bytes>, expected_incarnation: IncarnationV1, expected_revision: Revision |
Proposed |
IMPORT_CREDITS.Credit |
beneficiary: AccountId, settlement_profile_id: ProfileId |
Proposed |
IMPORT_CREDITS.Refresh |
settlement_profile_id: ProfileId |
Proposed |
SCHEDULE_RELEASE.Activate |
descriptor: ReleaseDescriptorV1 |
Proposed |
SCHEDULE_RELEASE.Defer |
pending_release_digest: ReleaseDigest, new_activation_height: Height |
Proposed |
SCHEDULE_RELEASE.Cancel |
pending_release_digest: ReleaseDigest |
Proposed |
SCHEDULE_RELEASE.EmergencyActivate |
descriptor: ReleaseDescriptorV1 |
Proposed |
VALIDATOR_SET_SCHEDULE.Append |
target_epoch: Epoch, expected_schedule_revision: Revision, participants: SortedVec<ValidatorParticipantV1,64,max_validator_participants_bytes>, directory: NativeValidatorDirectory, proofs_of_possession: SortedVec<ValidatorPopV1,2,max_validator_pop_bytes> |
Proposed |
VALIDATOR_SET_SCHEDULE.Cancel |
entry_id: Digest32, expected_schedule_revision: Revision |
Proposed |
PROJECT_SET_METADATA.expected_project_mutation_counter is Some(current_counter) exactly when
the requested slug differs from the current slug and execution will create a new SlugClaim; it is
None exactly when the slug is unchanged. TRANSFER_ACCEPT always creates the destination claim and
therefore always carries the exact current project mutation counter. These are closed shape rules,
not caller-selected optional guards.
ROLE_SET.Grant.expected_project_mutation_counter and
TRANSFER_OFFER.Set.expected_project_mutation_counter are Some(current_counter) exactly when
their target key is absent and the command allocates a new child; they are None for a live update.
Replacement of a resident expired record instead binds its exact generation or revision through
expected_predecessor or expected_offer and does not claim absence.
Supporting variant layouts are:
RefOpV1::Create {
ref_name: BoundedUtf8<max_ref_name_bytes>,
target: CommitmentV1,
}
RefOpV1::Set {
ref_name: BoundedUtf8<max_ref_name_bytes>,
expected_revision: Revision,
target: CommitmentV1,
}
RefOpV1::Delete {
ref_name: BoundedUtf8<max_ref_name_bytes>,
expected_revision: Revision,
}
UsernameLeaseGuardV1 { owner: AccountId, allocated_counter: Counter, revision: Revision }
expected_project_mutation_counter is Some exactly when a PUSH contains at least one CREATE and
None otherwise. The payload carries no independent ref-name digest, project ID derivation result,
offer expiry, key expiry, username deadline, or allocated revision. Execution derives those values
from signed inputs, parent counters, active rules, and block time.
For TRANSFER_OFFER.Set, the mutation counter is Some exactly when expected_offer is Absent;
it is None for a live update or expired replacement that names the predecessor revision.
5. Unsigned evidence and external wrappers
PushEvidenceV1 {
witnesses: BoundedVec<MembershipWitnessV1,max_ref_witnesses,max_push_evidence_bytes>,
}
MembershipWitnessV1::SameCommand { index: SameCommandIndex }
MembershipWitnessV1::Location { location: MembershipLocation }
MembershipWitnessV1::Proof {
anchor_size: MembershipLocation,
location: MembershipLocation,
proof: NativeMembershipProof,
}
TempoCreditEvidenceV1 {
finalization_certificate_bytes: BoundedBytes<max_tempo_certificate_bytes>,
tempo_header_rlp: BoundedBytes<max_tempo_header_rlp_bytes>,
contract_account_proof_nodes:
BoundedVec<BoundedBytes<max_tempo_proof_node_bytes>,max_tempo_account_proof_nodes,max_tempo_account_proof_bytes>,
beneficiary_storage_proof_nodes:
BoundedVec<BoundedBytes<max_tempo_proof_node_bytes>,max_tempo_storage_proof_nodes,max_tempo_storage_proof_bytes>,
}
TempoRefreshEvidenceV1 {
finalization_certificate_bytes: BoundedBytes<max_tempo_certificate_bytes>,
tempo_header_rlp: BoundedBytes<max_tempo_header_rlp_bytes>,
}
MembershipLocation and HistoryLocation are aliases of NativeMmrLocation. They are not fixed
eight-byte integers. Their semantic domain is the native MMR location range.
PUSH evidence contains exactly one witness per CREATE or SET in operation order and none for DELETE. Tempo evidence does not repeat beneficiary, profile ID, contract, code hash, state root, slot, cumulative, or source chain ID. The verifier derives them from the intent, authenticated header, proof nodes, and active profile.
The external Tempo header codec is RLP, not commonware-codec. At the Tempo pin,
crates/primitives/src/header.rs::TempoHeader encodes
[general_gas_limit, shared_gas_limit, timestamp_millis_part, inner, consensus_context?] with the
trailing consensus context omitted for pre-fork blocks. Makechain requires one exact canonical RLP
item, re-encode equality, timestamp_millis_part < 1000, and checked
inner.timestamp * 1000 + timestamp_millis_part. The certificate authenticates
keccak256(tempo_header_rlp) under the separately pinned certificate profile.
6. State key and value registry
The key formula is:
state_key = BLAKE3(family_domain || canonical(full_identity))
Family domains are the exact ASCII strings below. A terminating NUL is not present. Every value starts with its complete identity fields in the same order. Revision 1 adds no in-band record version; the active ruleset selects the one live representation.
| Family | Domain | Identity fields | Value fields after identity, in order | Class |
|---|---|---|---|---|
AccountLedger |
makechain.state.account-ledger |
account: AccountId |
credited: Amount256, spent: Amount256, locked: Amount256 |
Permanent |
AccountRoot |
makechain.state.account-root |
account: AccountId |
config_commitment: Option<Digest32>, version: U64, root_sequence: Sequence, delegation_epoch: Epoch, key_allocation_counter: Counter, epoch_start_key_counter: Counter, keychain_revision: Revision |
Permanent |
DelegatedKey |
makechain.state.delegated-key |
account: AccountId, key_id: KeyId |
public_key: PublicKeyV1, capability: Capability, project_scope: Option<ProjectId>, validity_window: Option<ValidityWindowV1>, verification_policy: VerificationPolicyV1, expires_at_ms: TimestampMs, authorization_generation: Generation, issued_delegation_epoch: Epoch, sequence_mark: Sequence, deposit: DepositV1 |
Deposited |
Project |
makechain.state.project |
project_id: ProjectId |
owner: AccountId, scheme_id: SchemeId, status: ProjectStatus, control_generation: Generation, lifecycle_revision: Revision, mutation_counter: Counter, origin: Option<CommitmentV1> |
Permanent |
ProjectMetadata |
makechain.state.project-metadata |
project_id: ProjectId |
slug: BoundedUtf8<max_slug_bytes>, description: BoundedUtf8<max_project_description_bytes> |
Permanent |
SlugClaim |
makechain.state.slug-claim |
owner: AccountId, slug: BoundedUtf8<max_slug_bytes> |
project_id: ProjectId, allocated_counter: Counter, deposit: DepositV1 |
Deposited |
ProjectRole |
makechain.state.project-role |
project_id: ProjectId, grantee: AccountId |
role: ProjectRoleKind, grant_generation: Generation, issued_control_generation: Generation, deposit: DepositV1 |
Deposited |
TransferOffer |
makechain.state.transfer-offer |
project_id: ProjectId |
destination: AccountId, revision: Revision, issued_control_generation: Generation, expires_at_ms: TimestampMs, deposit: DepositV1 |
Deposited |
Ref |
makechain.state.ref |
project_id: ProjectId, ref_name_digest: Digest32 |
target: CommitmentV1, revision: Revision, deposit: DepositV1 |
Deposited |
UsernameLease |
makechain.state.username-lease |
name: BoundedUtf8<max_username_bytes> |
owner: AccountId, allocated_counter: Counter, revision: Revision, resolution_deadline_ms: TimestampMs, reservation_deadline_ms: TimestampMs, deposit: DepositV1 |
Deposited |
AccountUsername |
makechain.state.account-username |
account: AccountId |
current_name: Option<BoundedUtf8<max_username_bytes>>, current_incarnation_counter: Option<Counter>, allocation_counter: Counter, last_selection_at_ms: Option<TimestampMs> |
Permanent |
SettlementAnchor |
makechain.state.settlement-anchor |
singleton: U8 = 0 |
settlement_profile_id: ProfileId, group_key: BoundedBytes<max_tempo_group_key_bytes>, from_epoch: U64, installed_at_makechain_height: Height, tempo_height: U64, tempo_timestamp_ms: TimestampMs, accepted_at_makechain_timestamp_ms: TimestampMs, imports_paused: Bool |
Permanent singleton |
ProtocolReleaseEntry |
makechain.state.protocol-release-entry |
record_kind: ReleaseRecordKind, entry_digest: Digest32 |
record: ProtocolReleaseRecordV1 |
Permanent append-only family |
ProtocolScheduleState |
makechain.state.protocol-schedule |
singleton: U8 = 0 |
revision: Revision, governance_nonce: U64, governance_set: GovernanceSetV1, active_release_digest: ReleaseDigest, tail_entry_digest: Option<Digest32>, pending_release_digest: Option<ReleaseDigest>, pending_entry_digest: Option<Digest32>, effective_parameters: EffectiveParametersV1 |
Permanent singleton |
ValidatorScheduleEntry |
makechain.state.validator-schedule |
identity: ValidatorScheduleIdentityV1 |
entry: ValidatorScheduleRecordV1 |
Permanent append-only family |
ProtocolReleaseRecordV1::Descriptor carries one ReleaseDescriptorV1 whose digest is the identity
digest. ScheduleEntry carries one immutable activate, defer, cancel, or emergency entry whose
separate entry_digest = BLAKE3("makechain.release-schedule-entry" || canonical(entry)) is the
identity digest. ActivationMarker carries only the installed release digest and uses
entry_digest = release_digest; its distinct record-kind tag prevents collision with the descriptor.
This is the required release_applied/{release_digest} marker without adding a sixteenth family.
The corresponding ProtocolReleaseIdentityV1 variants are
Descriptor { release_digest }, ScheduleEntry { entry_digest }, and
ActivationMarker { release_digest } in that tag order.
ValidatorScheduleIdentityV1 is a closed tagged identity union:
Schedule { entry_id }, Cancel { entry_id }, KeyReservation { identity_key }, and
ScheduleState { singleton: U8 = 0 }. Schedule and cancel entry IDs use their distinct digest
domains over the complete immutable record. A reservation is a direct lookup by identity key, and
the schedule state is a direct singleton lookup. This lets the family hold immutable lineage
entries, permanent key reservations, and the pending pointer without a scan. A future release that
permits two simultaneous live value shapes must add an explicit leading record-revision tag; it
cannot reinterpret revision-1 bytes.
Ref intentionally does not repeat the clear name. Its value still repeats its complete registered
identity: project ID plus digest. SlugClaim and UsernameLease retain clear bounded strings because
the string itself is part of their full identity.
Creation values are explicit. INIT_ACCOUNT stores root version zero, the accepted intent sequence
as root_sequence, delegation_epoch = 1, key_allocation_counter = 1,
epoch_start_key_counter = 1, and keychain_revision = 0; its commitment is None for a
single-key account and Some(genesis_configuration_commitment) for a configured account. A new
delegated key starts with sequence_mark = 0. PROJECT_CREATE starts control generation and
lifecycle revision at one, consumes mutation counter one for its initial slug incarnation, and
stores project mutation counter two. The first username lease similarly consumes account username
allocation counter one and stores two. Protocol and validator schedule revisions and the governance
nonce start at zero as described in section 7. Implementations do not obtain any of these values
from language defaults.
7. Header, body, genesis, and profile wrappers
HeaderV1 {
height: Height,
timestamp_ms: TimestampMs,
parent_hash: Digest32,
consensus_context: ConsensusContextV1,
active_release_digest: ReleaseDigest,
state_root: Digest32,
state_target: NativeStateTarget,
membership_target: NativeMembershipTarget,
history_target: NativeHistoryTarget,
command_root: Digest32,
command_count: U32,
genesis_epoch_info_hash: Option<Digest32>,
dealer_log_hash: Option<Digest32>,
dkg_outcome_hash: Option<Digest32>,
}
BodyV1 {
commands: BoundedVec<SignedCommandV1,max_commands_per_block,max_commands_bytes>,
genesis_epoch_info: Option<ProfileEpochInfoV1>,
dealer_log: Option<NativeDealerLog>,
dkg_outcome: Option<NativeEpochInfo>,
}
ConsensusContextV1 { epoch: Epoch, view: View }
command_count equals the command vector count. The three body optionals and their header hashes
must correspond exactly. genesis_epoch_info is present exactly at height zero. Under
FixedEpochZeroV1, dealer log and DKG outcome are always None; Some(empty) is invalid.
The genesis hash covers the complete ProfileEpochInfoV1 wrapper, including its profile tag. The
dealer-log and DKG-outcome hashes cover the exact pinned native inner encodings, without their body
option tags or a synthetic Payload wrapper.
Under DynamicReshareV1, dealer_log and dkg_outcome are mutually exclusive and project to
exactly one pinned glue::dkg::types::Payload variant when present. A final epoch block carries the
epoch outcome; an eligible non-final dealing block may carry a dealer log; every other height carries
neither. Genesis uses only genesis_epoch_info, projected separately to the required epoch-info
payload, and keeps both ordinary DKG options absent.
GenesisChainSpecV1 {
deployment_incarnation_nonce: FixedBytes32,
genesis_timestamp_ms: TimestampMs,
consensus_profile: ConsensusProfileConfigV1,
genesis_committee: SortedVec<ValidatorParticipantV1,64,max_genesis_committee_bytes>,
genesis_directory: NativeValidatorDirectory,
exporter_roster: SortedVec<ExporterRosterEntryV1,max_exporter_roster,max_exporter_roster_bytes>,
genesis_release: ReleaseDescriptorV1,
governance_set: GovernanceSetV1,
format_ids: FormatIdentifiersV1,
limits: LimitsV1,
price_schedule: PriceScheduleV1,
work_table: WorkTableV1,
scheme_registry: SchemeRegistryV1,
time_parameters: TimeParametersV1,
consensus_parameters: ConsensusParametersV1,
admission_rates: AdmissionRatesV1,
root_webauthn_origins: SortedVec<BoundedUtf8<max_webauthn_origin_bytes>,max_webauthn_origins,max_webauthn_origins_bytes>,
membership_window: U64,
username_claims_paused: Bool = false,
reclaims_paused: Bool = false,
multisig_recovery_factory: Option<AccountId>,
settlement: SettlementConfigurationV1,
}
The outer order and nested catalog are revision-1 design selections. chain_id, private shares,
production DKG output, and credit allocations are absent.
exporter_roster lists the non-voting mesh peers that publish history, one entry per exporter, in
strictly increasing identity-key order. Specification R-13.43 makes that roster part of the
transport set under every profile, and never part of the scheduled, ceremony, or effective signing
set. multisig_recovery_factory is the set-once configured-genesis factory address of specification
R-7.5 to R-7.7; it is None at launch and a nonzero address once set.
Genesis construction is exact. HeaderV1.height = 0, timestamp_ms is the chainspec value,
parent_hash is 32 zero bytes, consensus context is epoch zero/view zero, and
active_release_digest is the digest of genesis_release. The body has an empty command vector,
the profile's validated ProfileEpochInfoV1, and no dealer log or DKG outcome. command_count is
zero and command_root is the root returned by the pinned native BMT builder for zero leaves.
The initial authenticated state seed contains exactly: the genesis release descriptor record, its
activation marker, the ProtocolScheduleState singleton with the chainspec's complete effective
parameter snapshot, the ValidatorScheduleState singleton, one validator key-reservation record for
each genesis identity, and, when settlement is enabled, the SettlementAnchor singleton. It contains no ledger, account,
credit frontier, or user record. Encode each full family value, sort the writes by derived 32-byte
state key, reject duplicate keys, and seal them as one batch on top of the native current database's
bootstrap commit. state_root is that database's resulting canonical current root and
state_target is its resulting native ops-root target; neither is a hard-coded empty digest.
The genesis release has activation height zero, a 32-zero-byte predecessor, ruleset ID zero, class
Reset, consensus_critical = false, empty declared surfaces, and no installer. Its name and notes
digest are explicit chainspec inputs. Its parameter-delta list is empty, its format IDs equal the
chainspec format IDs, and its price-schedule and work-table IDs equal the chainspec values. This is
a genesis construction rule only; continuing-chain schedule commands never bypass
delay, predecessor, installer, or class validation.
The protocol schedule starts at revision and governance nonce zero, with the chainspec governance
set, active genesis release, no schedule tail, and no pending release. The settlement anchor copies
the enabled chainspec profile ID and bootstrap fields and records installed height zero. The validator
schedule starts at revision zero with no tail and no pending entry; each genesis identity has a
reservation whose first_entry_id is None.
The genesis effective snapshot copies price_schedule, work_table, limits, and
admission_rates; copies both activation delays, block-time bounds, and root WebAuthn origins;
constructs ConsensusTimeoutsV1 from the five corresponding consensus fields; copies
exporter_roster and multisig_recovery_factory; and maps the chainspec settlement branch to the
same effective branch. Both command-pause booleans must be false at genesis, and
multisig_recovery_factory is None at genesis. The enabled branch uses the derived settlement
singleton key. No other chainspec field is silently projected into governed parameters.
The membership and history databases remain at their native bootstrap commits for genesis: full
targets have range [0,1) and compact history has size 1, with the corresponding pin-derived
initial roots. Genesis performs no artificial append. When building child height H, append the
parent header leaf before the child's commit; header height h is consequently at history location
2h + 1, and the child target size is 2H + 1, with checked arithmetic.
On fresh Start::Genesis, before Stateful starts, the engine opens only the current-state member.
If it is at the pin-derived native bootstrap target, it applies the one bounded sorted seed batch,
finalizes it, waits for the durability barrier, reopens it, and requires both the expected canonical
root and expected native target. If it already equals those two expected values, bootstrapping is an
idempotent no-op; any third state is fatal. It then drops the bootstrap handles and starts Stateful.
It does not call application genesis execution: Stateful never applies genesis and its fresh-start
rewind path cannot advance an empty database. A peer-state-sync start skips the seed and obtains the
operations from its selected floor. Membership and history must not receive an empty merkleize,
which would advance their bootstrap commits.
Revision 1 proposes this complete nested catalog. Every field is present; profiles do not omit inapplicable fields outside their tagged profile branch.
TimeParametersV1 {
delegated_key_term_ms: U64,
transfer_offer_term_ms: U64,
username_term_ms: U64,
username_grace_ms: U64,
username_rename_cooldown_ms: U64,
username_horizon_ms: U64,
max_block_advance_ms: U64,
min_empty_block_interval_ms: U64,
min_activation_delay_blocks: U64,
min_emergency_delay_blocks: U64,
testnet_lead_blocks: U64,
post_activation_soak_blocks: U64,
}
ConsensusParametersV1 {
random_version: U16,
term_length: U64,
track_historical_votes: Bool,
marshal_variant: U8,
retained_marshal_blocks: U64,
max_pending_acks: U32,
timeout_initial_ms: U64,
timeout_min_ms: U64,
timeout_max_ms: U64,
timeout_growth_numerator: U32,
timeout_growth_denominator: U32,
max_participants: U16,
min_participants: U16,
max_additions_per_epoch: U8,
max_removals_per_epoch: U8,
}
AdmissionRatesV1 {
per_account_commands_per_window: U32,
per_account_bytes_per_window: U32,
anonymous_commands_per_window: U32,
anonymous_bytes_per_window: U32,
window_ms: U64,
max_pool_commands: U32,
max_pool_bytes: U64,
}
NamespaceInputsV1 {
consensus_suffix: NonEmptyBytes<max_namespace_suffix_bytes>,
dkg_suffix: NonEmptyBytes<max_namespace_suffix_bytes>,
p2p_suffix: NonEmptyBytes<max_namespace_suffix_bytes>,
}
DynamicBootstrapParametersV1 {
sharing_mode: NativeSharingMode,
reveal_mode: DkgRevealModeV1,
transcript_version: U8 = 0,
bootstrap_timeout_ms: U64,
reshare_timeout_ms: U64,
}
ExporterRosterEntryV1 {
identity_key: ValidatorPublicKey,
address: NativeValidatorAddress,
}
FormatIdentifiersV1 { ... section 8 ... }
LimitsV1 { ... section 8 ... }
PriceScheduleV1 { ... section 8 ... }
WorkTableV1 { ... section 8 ... }
SchemeRegistryV1 {
schemes: SortedVec<SchemeDescriptorV1,max_schemes,max_scheme_registry_bytes>,
}
SchemeDescriptorV1 {
scheme_id: SchemeId,
name: NonEmptyBytes<max_scheme_name_bytes>,
hash_algorithm: HashAlgorithmTag,
id_length: U16,
object_format_version: U16,
addressing_epoch: U16,
canonical_decoding: CanonicalDecodingTag,
permitted_kinds: U32,
per_kind_preimage_rules: BoundedVec<PreimageRuleV1,max_scheme_kinds,max_scheme_preimage_rules_bytes>,
}
PreimageRuleV1 {
object_kind: U8,
rule: PreimageRuleTag,
}
NativeSharingMode uses the pinned Commonware codec and decode mode. DkgRevealModeV1 is a
Makechain byte because pinned feldman_desmedt::Reveal has no native Read or Write; tag 01
maps to Reveal::V1, while deprecated Reveal::V0 is rejected. transcript_version is a manifest
consistency assertion against the pinned private TRANSCRIPT_VERSION = Version::V0, not a runtime
configuration passed to Commonware. Dealer-log capacity is not encoded here: derive
blocks - (blocks / 2 + 1) for epoch lengths of at least four, otherwise zero, exactly as pinned
glue/src/dkg/types.rs, and reject a chainspec whose derived capacity is below the required dealer
logs.
PreimageRuleTag allocates 00 FlatBytes, 01 MkitDomainBmtV1, and
02 GitTypeSpaceLengthNulBody. The launch registry has exactly these three descriptors in
increasing scheme_id order: mkit-blake3 at commit
cace229e6ae464bdec25ed1b87f063db16e9d1a1, with 32-byte IDs, MkitObjectV1, and its
kind-specific flat/BMT rules; git-sha1 with 20-byte IDs, GitLooseObjectV1, and the Git preimage rule; and
git-sha256 with 32-byte IDs and the same Git decoding/preimage rule. The manifest computes each
scheme_id = BLAKE3("makechain.scheme" || canonical(descriptor_without_scheme_id)) and verifies the
stored ID.
The mkit profile permits object kinds blob 1, tree 2, commit 3, remix 4, chunked blob 5,
and tag 7. Delta 6 is pack-only and cannot be a standalone native ID. Publication verification
requires kind 3. Flat objects hash their canonical bytes. Tree and chunked-blob BMT leaves bind
position as big-endian u32; their exact domain bytes are mkit.tree\x00 and
mkit.chunked\x00. Mkit domain hashing is
BLAKE3(le16(domain_length) || domain || body). The pinned SPEC-OBJECTS and
SPEC-MERKLE-OBJECTS vectors remain the external byte authority; Makechain does not reimplement the
object schema.
random_version = 1, term_length = 1, track_historical_votes = true, and
marshal_variant = 0 for standard deferred marshal at genesis. The seven-validator fixed profile
sets min_participants = max_participants = 7, while dynamic sets 7 and 64. Addition and removal
limits are each one. Fixed durations are encoded as their exact millisecond values from the
state-machine companion. Calibrated durations and ceilings remain required manifest inputs.
ConsensusProfileConfigV1::FixedEpochZero {
epoch_length: U64 = u64::MAX,
namespace_inputs: NamespaceInputsV1,
trusted_public_output: NativeDkgOutput,
epoch_zero_info: NativeEpochInfo,
}
ConsensusProfileConfigV1::DynamicReshare {
epoch_length: U64,
namespace_inputs: NamespaceInputsV1,
bootstrap_parameters: DynamicBootstrapParametersV1,
}
ProfileEpochInfoV1::Fixed { epoch_info: NativeEpochInfo }
ProfileEpochInfoV1::Dynamic { epoch_info: NativeEpochInfo }
The fixed profile validates exactly seven ordered identities, total 7, required 5, epoch 0, empty revealed set, and identical dealers, output players, players, and next players. These wrappers carry public data only. A reset chooses fresh incarnation, fixture output/shares, chain ID, genesis block, and storage namespaces.
8. Release and pinned-format wrappers
FormatIdentifiersV1 {
canonical_schema_id: SchemaId,
canonical_schema_revision: SchemaRevision,
commonware_codec_id: CodecId,
state_layout_id: LayoutId,
membership_layout_id: LayoutId,
history_layout_id: LayoutId,
command_bmt_id: LayoutId,
consensus_certificate_id: CodecId,
scheme_registry_digest: Digest32,
}
ReleaseDescriptorV1 {
name: BoundedUtf8<max_release_name_bytes>,
notes_digest: Digest32,
predecessor: ReleaseDigest,
class: ReleaseClass,
consensus_critical: Bool,
activation_height: Height,
ruleset_id: RulesetId,
price_schedule_id: PriceScheduleId,
work_table_id: WorkTableId,
declared_surfaces: SortedVec<ReleaseSurface,max_release_surfaces,max_release_surfaces_bytes>,
parameter_deltas: SortedVec<ParameterDeltaV1,max_parameter_deltas,max_parameter_delta_bytes>,
installer: Option<ActivationInstallerV1>,
format_ids: FormatIdentifiersV1,
}
Each release digest hashes one canonical descriptor under makechain.release. Schedule entries
never inline a second copy of mutable current state. The remaining nested schemas are:
GovernanceSetV1 {
revision: Revision,
threshold: U16,
keys: SortedVec<FixedBytes32,max_governance_keys,max_governance_keys_bytes>,
}
ReleaseScheduleEntryV1::Activate {
entry_sequence: U64,
predecessor_entry_digest: Option<Digest32>,
descriptor: ReleaseDescriptorV1,
authorizing_governance_revision: Revision,
governance_nonce: U64,
}
ReleaseScheduleEntryV1::Defer {
entry_sequence: U64,
predecessor_entry_digest: Digest32,
previous_release_digest: ReleaseDigest,
deferred_descriptor: ReleaseDescriptorV1,
authorizing_governance_revision: Revision,
governance_nonce: U64,
}
ReleaseScheduleEntryV1::Cancel {
entry_sequence: U64,
predecessor_entry_digest: Digest32,
pending_release_digest: ReleaseDigest,
authorizing_governance_revision: Revision,
governance_nonce: U64,
}
ReleaseScheduleEntryV1::EmergencyActivate {
entry_sequence: U64,
predecessor_entry_digest: Option<Digest32>,
descriptor: ReleaseDescriptorV1,
authorizing_governance_revision: Revision,
governance_nonce: U64,
}
ActivationInstallerV1::WriteSettlementAnchor { input: SettlementAnchorInstallV1 }
ActivationInstallerV1::RotateGovernanceSet { governance_set: GovernanceSetV1 }
ActivationInstallerV1::ReserveValidatorKey { identity_key: ValidatorPublicKey }
ActivationInstallerV1::QualifiedHandler {
handler_id: Digest32,
input_schema_id: SchemaId,
input_bytes: BoundedBytes<max_installer_input_bytes>,
}
ValidatorParticipantV1 { identity_key: ValidatorPublicKey }
ValidatorPopV1 { identity_key: ValidatorPublicKey, signature: FixedBytes64 }
ValidatorScheduleRecordV1::Schedule {
revision: Revision,
predecessor_entry_id: Option<Digest32>,
target_epoch: Epoch,
participants: SortedVec<ValidatorParticipantV1,64,max_validator_participants_bytes>,
directory: NativeValidatorDirectory,
}
ValidatorScheduleRecordV1::Cancel {
predecessor_entry_id: Digest32,
cancelled_entry_id: Digest32,
cancel_sequence: U64,
}
ValidatorScheduleRecordV1::KeyReservation {
identity_key: ValidatorPublicKey,
first_entry_id: Option<Digest32>,
}
ValidatorScheduleRecordV1::ScheduleState {
revision: Revision,
tail_entry_id: Option<Digest32>,
pending_entry_id: Option<Digest32>,
}
Only the first release schedule entry may use predecessor_entry_digest = None; every later entry,
including activate, defer, cancel, and emergency activate, names the exact current tail. Only the
first validator schedule record may omit its predecessor, and every later schedule or cancel record
names that family's exact current tail. A cancel record never rewrites the cancelled record.
Every installer variant maps to one closed handler in the activated ruleset. A qualified handler ID
is registered in that release's manifest with its exact input schema, maximum reads/writes, and
permitted destination families before scheduling. Revision 1 forbids every installer from creating
or changing AccountLedger, credited, spent, locked, deposited user records, or replay marks.
It also forbids an arbitrary family/value write. A new-family initializer may be introduced only as
a new reviewed handler in a versioned-state release. The activation marker and all handler writes
commit in one batch.
Validator schedule and cancel records are immutable. Cancel appends its own record and clears the
singleton pending pointer; it does not mutate or delete the Schedule record or key reservation.
Effective status derives from the append-only entries plus ScheduleState.
A defer command's executor derives deferred_descriptor from the current pending descriptor by
changing only activation_height, then recomputes its release digest. Every other field must be
byte-equal. The schedule state points at that new release digest and the immutable defer-entry digest.
The validator identity is the Commonware participant identity key. A participant entry never embeds
a per-validator 96-byte threshold key; the DKG public polynomial and local share machinery own that
material. Each newly added identity supplies Ed25519 proof of possession over
BLAKE3("makechain.validator-possession" || chain_id || U64(target_epoch) || identity_key).
The proofs vector contains exactly the lexicographically sorted added identities, which is at most
one at launch. Existing participants supply no repeated proof.
ReleaseSurface tags are: 00 CommandValidity, 01 Execution, 02 Pricing, 03 Encoding,
04 ProofFormat, 05 SigningNamespace, 06 HandshakeNamespace, 07 RandomVersion,
08 TermLength, 09 ConsensusTimeouts, 0a DkgSharingMode, 0b DkgRevealMode,
0c DkgTranscriptVersion, 0d CommitteeBounds, 0e MarshalVariant, and 0f P2pWireCodec.
The list is strictly increasing. Tags 05..=0f require consensus_critical = true; the first five
do so only when the declared change also names a critical surface.
ParameterDeltaV1 is a tagged key/value union. Tags and value domains are:
| Tag | Parameter | Value and validation |
|---|---|---|
00 |
PriceSchedule |
Complete PriceScheduleV1; ID must match its digest |
01 |
WorkTable |
Complete WorkTableV1; ID must match its digest |
02 |
Limits |
Complete LimitsV1; aggregate relationships validate |
03 |
AdmissionRates |
AdmissionRatesV1; every count/byte rate is nonzero |
04 |
MinActivationDelayBlocks |
nonzero U64 |
05 |
MinEmergencyDelayBlocks |
nonzero U64, no greater than ordinary delay |
06 |
RootWebAuthnOrigins |
sorted nonempty bounded origin vector |
07 |
ImportsPaused |
Bool |
08 |
SettlementTrustingPeriodMs |
nonzero U64 |
09 |
SettlementMaxSourceClockSkewMs |
U64 |
0a |
SettlementEvidenceLimits |
complete SettlementEvidenceLimitsV1; aggregates validate |
0b |
SettlementProfile |
complete SettlementSourceProfileV1; changed only with the anchor installer |
0c |
MaxBlockAdvanceMs |
nonzero U64 |
0d |
MinEmptyBlockIntervalMs |
nonzero U64, no greater than max block advance |
0e |
ConsensusTimeouts |
complete ConsensusTimeoutsV1; all nonzero |
0f |
GovernanceSet |
complete GovernanceSetV1; revision is current plus one |
10 |
UsernameClaimsPaused |
Bool |
11 |
ReclaimsPaused |
Bool |
12 |
ExporterRoster |
complete sorted roster vector; every entry distinct by identity key and by address |
13 |
MultisigRecoveryFactory |
nonzero AccountId; valid only while the effective value is None |
No delta carries an arbitrary string key or untyped bytes. A descriptor contains at most one delta
of each tag, in increasing tag order.
Tags 08, 09, and 0a are field-specific updates to the effective settlement profile; they are
mutually exclusive with the complete-profile tag 0b. Either form derives and validates the new
profile ID and requires WriteSettlementAnchor in the same descriptor. A descriptor cannot change
profile bytes while leaving the anchor's profile ID behind.
Tag 13 is the set-once path of specification R-7.6: a descriptor carrying it is invalid unless the
parent snapshot's multisig_recovery_factory is None, and the delta value is never the zero
address (R-7.7). Tag 12 replaces the complete roster; it changes transport membership only and no
scheduled, ceremony, or signing set.
ReleaseDescriptorV1 has no separate settlement-profile field. The typed settlement deltas above
are its only continuing-chain representations. ImportsPaused writes
SettlementAnchor.imports_paused; the effective snapshot below carries only the singleton state-key
reference, so there is one authoritative pause bit. Both deltas are invalid under
DisabledDiagnostic; that branch is reset-only.
At an activation height, rule resolution reads the parent snapshot and referenced anchor, validates
the descriptor, and applies its bounded deltas and installer to an in-memory overlay before any
command in that block is checked. The activated pause values and replacement anchor therefore
govern the first activation block. username_claims_paused rejects every USERNAME_CLAIM variant
before its command logic, and reclaims_paused rejects RECLAIM; no other command family is
affected by these two flags.
If one descriptor contains both ImportsPaused and WriteSettlementAnchor, their Boolean values
must agree; disagreement is invalid before the overlay is applied.
LimitsV1 {
max_block_bytes: U32,
max_block_work_units: U64,
max_new_records: U32,
max_command_bytes: U32,
max_command_work_units: U64,
max_command_new_records: U32,
max_commands_per_block: U32,
max_commitments_per_push: U32,
max_ref_ops_per_push: U32,
max_ref_name_bytes: U32,
max_slug_bytes: U32,
max_project_description_bytes: U32,
max_username_bytes: U32,
max_native_id_bytes: U32,
max_state_value_bytes: U32,
max_release_descriptor_bytes: U32,
max_proof_response_bytes: U32,
max_receipt_bytes: U32,
max_historical_proof_work_units: U64,
max_reconciliation_blocks: U64,
max_preparation_response_bytes: U32,
top_level: TopLevelLimitsV1,
keys: KeyLimitsV1,
project_push: ProjectPushLimitsV1,
username: UsernameLimitsV1,
state: StateLimitsV1,
release: ReleaseLimitsV1,
validator: ValidatorLimitsV1,
schemes: SchemeLimitsV1,
settlement: SettlementLimitsV1,
proof_response: ProofResponseLimitsV1,
events: EventLimitsV1,
}
TopLevelLimitsV1 {
max_genesis_chainspec_bytes: U32,
max_header_bytes: U32,
max_body_bytes: U32,
max_commands_bytes: U32,
max_signed_command_bytes: U32,
max_command_intent_bytes: U32,
max_authorization_bytes: U32,
max_unsigned_evidence_bytes: U32,
}
KeyLimitsV1 {
max_public_key_bytes: U32,
max_signature_container_bytes: U32,
max_root_approvals: U32 = 8,
max_root_node_levels: U32 = 1,
max_nested_account_edges: U32 = 0,
max_root_approvals_bytes: U32,
max_owner_configuration_bytes: U32,
max_governance_keys: U32,
max_governance_keys_bytes: U32,
max_governance_witness_bytes: U32,
max_webauthn_credential_id_bytes: U32,
max_webauthn_client_data_json_bytes: U32,
max_webauthn_rp_id_bytes: U32,
max_webauthn_origin_bytes: U32,
max_webauthn_origins: U32,
max_webauthn_origins_bytes: U32,
}
ProjectPushLimitsV1 {
max_commitments_bytes: U32,
max_ref_ops_bytes: U32,
max_ref_witnesses: U32,
max_push_evidence_bytes: U32,
max_membership_proof_digests: U32,
}
UsernameLimitsV1 {
min_username_bytes: U32,
max_username_years_per_command: U32,
}
StateLimitsV1 {
max_account_ledger_value_bytes: U32,
max_account_root_value_bytes: U32,
max_delegated_key_value_bytes: U32,
max_project_value_bytes: U32,
max_project_metadata_value_bytes: U32,
max_slug_claim_value_bytes: U32,
max_project_role_value_bytes: U32,
max_transfer_offer_value_bytes: U32,
max_ref_value_bytes: U32,
max_username_lease_value_bytes: U32,
max_account_username_value_bytes: U32,
max_settlement_anchor_value_bytes: U32,
max_protocol_release_entry_value_bytes: U32,
max_protocol_schedule_value_bytes: U32,
max_validator_schedule_entry_value_bytes: U32,
max_effective_parameters_bytes: U32,
max_genesis_state_writes: U32,
max_genesis_state_bytes: U32,
max_current_state_proof_digests: U32,
state_bitmap_chunk_bytes: U32 = 64,
}
ReleaseLimitsV1 {
max_release_name_bytes: U32,
max_release_surfaces: U32,
max_release_surfaces_bytes: U32,
max_parameter_deltas: U32,
max_parameter_delta_bytes: U32,
max_release_lineage_entries: U32,
max_release_lineage_bytes: U32,
max_installer_input_bytes: U32,
max_installer_writes: U32,
max_family_amount_table_bytes: U32,
max_command_work_table_bytes: U32,
max_signature_work_table_bytes: U32,
max_root_depth_work_bytes: U32,
}
ValidatorLimitsV1 {
max_validator_participants: U32 = 64,
max_validator_participants_bytes: U32,
max_validator_directory_bytes: U32,
max_validator_single_pop_bytes: U32,
max_validator_pop_bytes: U32,
max_genesis_committee_bytes: U32,
max_genesis_directory_bytes: U32,
max_namespace_suffix_bytes: U32,
max_exporter_roster: U32,
max_exporter_roster_bytes: U32,
}
SchemeLimitsV1 {
max_schemes: U32,
max_scheme_registry_bytes: U32,
max_scheme_name_bytes: U32,
max_scheme_kinds: U32,
max_scheme_preimage_rules_bytes: U32,
}
SettlementLimitsV1 {
max_tempo_group_key_bytes: U32,
}
ProofResponseLimitsV1 {
max_finality_evidence_bytes: U32,
max_history_proof_digests: U32,
max_command_proof_bytes: U32,
}
EventLimitsV1 {
max_events_per_block: U32,
max_event_batch_bytes: U32,
max_event_payload_bytes: U32,
max_deposit_entries_per_event: U32,
max_deposit_entries_bytes: U32,
max_ref_changes_bytes: U32,
}
PriceScheduleV1 {
price_schedule_id: PriceScheduleId,
work_table_id: WorkTableId,
byte_price: Amount256,
work_unit_price: Amount256,
permanent_creation_fees: SortedVec<FamilyAmountV1,9,max_family_amount_table_bytes>,
deposited_creation_fees: SortedVec<FamilyAmountV1,6,max_family_amount_table_bytes>,
deposited_locks: SortedVec<FamilyAmountV1,6,max_family_amount_table_bytes>,
username_year_price: Amount256,
minimum_initialized_balance: Amount256,
}
FamilyAmountV1 { family: StateFamilyTag, amount: Amount256 }
WorkTableV1 {
work_table_id: WorkTableId,
base_by_command: SortedVec<CommandWorkV1,17,max_command_work_table_bytes>,
signature_by_family: SortedVec<SignatureWorkV1,4,max_signature_work_table_bytes>,
per_commitment: U64,
per_ref_op: U64,
per_proof_digest: U64,
per_mpt_node: U64,
per_state_read: U64,
per_state_write: U64,
per_membership_append: U64,
per_new_record: U64,
root_depth_surcharge: BoundedVec<U64,3,max_root_depth_work_bytes>,
}
CommandWorkV1 { command_tag: U8, base_work_units: U64 }
SignatureWorkV1 { family: KeyFamily, verify_work_units: U64 }
ConsensusTimeoutsV1 {
initial_ms: U64,
minimum_ms: U64,
maximum_ms: U64,
growth_numerator: U32,
growth_denominator: U32,
}
SettlementEvidenceLimitsV1 {
max_certificate_bytes: U32,
max_header_rlp_bytes: U32,
max_account_proof_nodes: U32,
max_storage_proof_nodes: U32,
max_proof_node_bytes: U32,
max_account_proof_bytes: U32,
max_storage_proof_bytes: U32,
max_credit_evidence_bytes: U32,
}
EffectiveParametersV1 {
price_schedule: PriceScheduleV1,
work_table: WorkTableV1,
limits: LimitsV1,
admission_rates: AdmissionRatesV1,
min_activation_delay_blocks: U64,
min_emergency_delay_blocks: U64,
root_webauthn_origins: SortedVec<BoundedUtf8<max_webauthn_origin_bytes>,max_webauthn_origins,max_webauthn_origins_bytes>,
username_claims_paused: Bool,
reclaims_paused: Bool,
settlement: EffectiveSettlementV1,
max_block_advance_ms: U64,
min_empty_block_interval_ms: U64,
consensus_timeouts: ConsensusTimeoutsV1,
exporter_roster: SortedVec<ExporterRosterEntryV1,max_exporter_roster,max_exporter_roster_bytes>,
multisig_recovery_factory: Option<AccountId>,
}
EffectiveSettlementV1::DisabledDiagnostic {}
EffectiveSettlementV1::Enabled {
profile: SettlementSourceProfileV1,
anchor_state_key: FixedBytes32,
}
Each price table covers exactly its own storage class, and every family of that class appears in it
exactly once, in increasing state-family tag order. The nine permanent families are AccountLedger,
AccountRoot, Project, ProjectMetadata, AccountUsername, SettlementAnchor,
ProtocolReleaseEntry, ProtocolScheduleState, and ValidatorScheduleEntry. The six deposited
families are DelegatedKey, SlugClaim, ProjectRole, TransferOffer, Ref, and
UsernameLease. A deposited family never appears in permanent_creation_fees, and a permanent
family never appears in deposited_creation_fees or deposited_locks. Every deposited lock is
nonzero.
LimitsV1 carries one typed field for every symbol of the bound registry in section 13, except the
eight settlement evidence bounds that SettlementEvidenceLimitsV1 carries under their unprefixed
names, and username_horizon_ms, which TimeParametersV1 carries. Its first twenty-one fields are
the block, command, record, and response ceilings; the eleven sub-structs that follow carry the
section 13 groups (the settlement evidence group lives in SettlementEvidenceLimitsV1, and
username_horizon_ms in TimeParametersV1), and no bound appears in two places. max_validator_participants
is the fixed collection cardinality of every validator vector and is distinct from the governed
ConsensusParametersV1.max_participants, which selects the committee bound within it. Delta tag
02 replaces the complete structure, so a limits change is one reviewed value set rather than a
patch. Work multiplication uses checked widened arithmetic and fits U64 before resource
comparison. PriceScheduleV1.work_table_id must equal the ID of the effective WorkTableV1; a
signed price-schedule ID therefore binds every work coefficient. The effective snapshot is bounded
by max_effective_parameters_bytes, stored in ProtocolScheduleState, and rehashed after every
activation. In its enabled branch, the settlement anchor key must equal the derived singleton key,
the profile ID must equal the anchor's profile ID, and rules read the pause bit only from that
authenticated anchor.
SettlementSourceProfileV1 {
profile_id: ProfileId,
tempo_chain_id: U64,
certificate_codec_id: CodecId,
header_codec_id: CodecId,
settlement_contract: AccountId,
settlement_runtime_code_hash: Digest32,
contract_layout_id: LayoutId,
cumulative_mapping_base_slot: FixedBytes32,
cumulative_value_bits: U16 = 256,
accepted_asset_address: AccountId,
accepted_asset_currency: FixedAscii3,
accepted_asset_decimals: U8,
trusting_period_ms: U64,
max_source_clock_skew_ms: U64,
evidence_limits: SettlementEvidenceLimitsV1,
}
SettlementConfigurationV1::DisabledDiagnostic {}
SettlementConfigurationV1::Enabled {
profile: SettlementSourceProfileV1,
anchor: SettlementAnchorBootstrapV1,
}
TempoBlockDigestV1 = FixedBytes32
NativeTempoFinalization = commonware_consensus::simplex::types::Finalization<
commonware_consensus::simplex::scheme::bls12381_threshold::vrf::Scheme<
commonware_cryptography::ed25519::PublicKey,
commonware_cryptography::bls12381::MinSig
>,
tempo_consensus::Digest
>
SettlementAnchorBootstrapV1 {
group_key: BoundedBytes<max_tempo_group_key_bytes>,
from_epoch: U64,
tempo_height: U64,
tempo_timestamp_ms: TimestampMs,
accepted_at_makechain_timestamp_ms: TimestampMs,
imports_paused: Bool,
}
SettlementAnchorInstallV1 {
group_key: BoundedBytes<max_tempo_group_key_bytes>,
from_epoch: U64,
tempo_height: U64,
tempo_timestamp_ms: TimestampMs,
imports_paused: Bool,
}
Every devnet and testnet deployment profile selects Tempo Moderato. Mainnet selects Tempo mainnet.
The profile carries real addresses, hashes, identities, and checkpoints through a reviewed deployment
manifest; this document supplies no placeholder address or default checkpoint. Slot zero is encoded
as 32 zero bytes. The accepted asset currency is ASCII USD, decimals is 6, and the cumulative is a
full 256-bit value. from_epoch is explicit in the active anchor and rejects certificates from an
earlier Tempo identity epoch. The profile assumes the pinned Tempo group key identifies only the
selected source network for every accepted epoch at or after from_epoch; certificate namespace
TEMPO does not provide network separation. A deployment review must verify that uniqueness and
record the evidence with the anchor.
DisabledDiagnostic is valid only for an unfunded, nondeployed local diagnostic chain. It creates
no settlement anchor, makes both import variants invalid, and supplies no other credit path. It
cannot be enabled by a continuing-chain delta; doing so requires a reset. It is an explicit tag,
never a zero address or partial source profile.
The genesis bootstrap's accepted_at_makechain_timestamp_ms must equal
genesis_timestamp_ms. A release installer never accepts either installed_at_makechain_height or
the accepted-at time from governance bytes: execution derives them from the activation height and
activation block timestamp. It takes the profile ID from the activated effective settlement profile
and the source checkpoint fields from SettlementAnchorInstallV1.
The external certificate candidate is pinned to Tempo commit
25b216a661c2cac72807174583371b582b75c286,
crates/consensus/src/finalization_verifier/mod.rs:9-24,89-104: the native finalization above uses
the Commonware 9.0 codec, namespace TEMPO, and a 32-byte Tempo consensus digest whose bytes equal
the Keccak-256 header hash. Makechain decodes the native certificate and compares the authenticated
payload to TempoBlockDigestV1; it does not reproduce that codec. This pin is a candidate until
an authentic deployment certificate and header fixture qualify it.
9. Native Commonware contracts
Makechain wraps native types but does not reimplement their bytes. The format manifest must record
the complete generic instantiation, decoder configuration, crate, path, tag, and commit. The verified
source paths at v2026.9.0 are listed below. The proof path (specification section 6.2) instantiates
these types with a Keccak256 hasher: the library's own cryptography::keccak256 is on its main
branch after that tag, at commit v2026.9.0-69-gffe2cf2af2 and at ALPHA, so Makechain instantiates
them with its own implementation of the pinned version's Hasher trait, whose vectors cross-check
the upstream one. Every other instantiation is available at the tag. The paths are:
NativeStateTarget = qmdb::sync::Target<mmr::Family, keccak256::Digest>
NativeMembershipTarget = qmdb::sync::Target<mmr::Family, keccak256::Digest>
NativeHistoryTarget = qmdb::sync::CompactTarget<mmr::Family, keccak256::Digest>
NativeMembershipProof = merkle::Proof<mmr::Family, keccak256::Digest>
NativeHistoryProof = merkle::Proof<mmr::Family, keccak256::Digest>
NativeCurrentStateProof = qmdb::current::proof::OperationProof<mmr::Family, keccak256::Digest, 64>
NativeBmtProof = bmt::Proof<keccak256::Digest>
NativeMmrLocation = commonware_storage::merkle::Location<mmr::Family>
MembershipLocation = NativeMmrLocation
HistoryLocation = NativeMmrLocation
NativeSharingMode = commonware_cryptography::bls12381::primitives::sharing::Mode
ValidatorPublicKey = commonware_cryptography::ed25519::PublicKey
ValidatorIdentitySigner = commonware_cryptography::ed25519::PrivateKey
NativeValidatorAddress = commonware_p2p::Address
NativeValidatorDirectory = commonware_glue::dkg::network::Addresses<ValidatorPublicKey>
ThresholdVrfMinSig = commonware_consensus::simplex::scheme::bls12381_threshold::vrf::Scheme<ValidatorPublicKey, MinSig>
NativeSimplexFinalization = simplex::types::Finalization<ThresholdVrfMinSig, keccak256::Digest>
NativeEpochInfo = glue::dkg::types::EpochInfo<MinSig, ValidatorPublicKey, NativeValidatorDirectory>
NativeDkgOutput = commonware_cryptography::bls12381::dkg::feldman_desmedt::Output<MinSig, ValidatorPublicKey>
NativeDealerLog = commonware_cryptography::bls12381::dkg::feldman_desmedt::SignedDealerLog<MinSig, ValidatorIdentitySigner>
The displayed paths omit crate prefixes only for readability. The Rust aliases use the exact public paths recorded in the format manifest.
| Role | Native source contract | Native field order at the pin |
|---|---|---|
| BMT command proof | commonware_storage::bmt::Proof<D>; storage/src/bmt/mod.rs |
leaf_count: u32, counted siblings: Vec<D>; decode config is maximum proved items and derives at most items * MAX_LEVELS siblings |
| Merkle inclusion proof | commonware_storage::merkle::Proof<F,D>; storage/src/merkle/proof.rs |
leaves: Location<F>, shortest-form UInt<u64> inactive_peaks, counted digests: Vec<D> |
| Current-state activeness proof | commonware_storage::qmdb::current::proof::OperationProof<F,D,N>; storage/src/qmdb/current/proof/mod.rs |
loc: Location<F>, fixed bitmap chunk: [u8;N], native RangeProof<F,D> |
| Compact QMDB sync target | commonware_storage::qmdb::sync::CompactTarget<F,D>; same path |
root: D, size: Location<F> |
| Simplex finalization | commonware_consensus::simplex::types::Finalization<S,D>; consensus/src/simplex/types.rs |
Native proposal followed by native scheme certificate |
| Threshold VRF certificate | commonware_consensus::simplex::scheme::bls12381_threshold::vrf::Certificate<V>; consensus/src/simplex/scheme/bls12381_threshold/vrf.rs |
Native fixed-size certificate for selected BLS variant |
| DKG epoch artifact | commonware_glue::dkg::types::EpochInfo<V,P,D>; glue/src/dkg/types.rs |
outcome, epoch, native output, players, next_players, directory; decode config (max_participants, max_supported_mode) |
| DKG block payload | commonware_glue::dkg::types::Payload<V,C,D>; same path |
tag 00 SignedDealerLog, tag 01 EpochInfo; Makechain stores the selected inner value in its separate body option |
The application uses the native BMT proof directly in command receipts. It uses the native Merkle
proof directly for membership and history inclusion. It uses the native current-state operation
proof for value plus activeness. state_root remains the application-derived canonical current
root, while the native QMDB target carries the operations root and range. These fields are never
flattened into a generic root/siblings wrapper.
Revision 1 proposes the following complete storage instantiation:
| Member | Instantiation |
|---|---|
| Validity state | qmdb::current::unordered::variable, merkle::mmr::Family, cryptography::keccak256::Digest, 32-byte keys, variable values, bitmap chunk N = 64 bytes |
| Membership log | full qmdb::keyless::fixed, merkle::mmr::Family, cryptography::keccak256::Digest, fixed 32-byte leaves |
| Block history | validator and exporter compact qmdb::keyless, full operation log in the proof service, merkle::mmr::Family, cryptography::keccak256::Digest, fixed 32-byte leaves |
| Command root | storage::bmt::Builder<cryptography::keccak256::Keccak256> over the block's ordered 32-byte command-tree leaves, each Keccak256(canonical(SignedCommandV1)) |
| Simplex | simplex::scheme::bls12381_threshold::vrf::Scheme<ValidatorPublicKey, MinSig> and its Certificate<MinSig> |
| DKG public artifacts | glue::dkg::types::EpochInfo<MinSig, ValidatorPublicKey, NativeValidatorDirectory>, its native Output<MinSig,ValidatorPublicKey>, and SignedDealerLog<MinSig,ValidatorIdentitySigner> |
ValidatorPublicKey and ValidatorIdentitySigner are the pinned Commonware Ed25519 public- and
private-key types. The latter fixes the public-key and signature associated types that determine
SignedDealerLog wire bytes; custody implementations must expose those exact associated types and
bytes. No private key is encoded in the log or any canonical public artifact. The launch directory is the
stock commonware_glue::dkg::network::Addresses<ValidatorPublicKey>, a native ordered map from that
key to commonware_p2p::Address; its decoder configuration is the exact peer-set cardinality
derived by the pinned Directory trait. Genesis, validator scheduling, and NativeEpochInfo use
these same bytes and type. MMR locations and sizes use the native
commonware_storage::merkle::Location<mmr::Family> codec, which writes and reads the shortest-form
commonware_codec::varint::UInt<u64> and enforces the family's maximum of 2^62 leaves. It is not
a fixed-width u64. The bitmap choice follows
the pinned glue any-adapter's ANY_BITMAP_CHUNK_BYTES = 64 value in glue/src/stateful/db/any.rs:49;
the current adapter does not impose it, so revision 1 selects N = 64 explicitly because it affects the
root. Glue exposes full qmdb::sync::Target<mmr::Family,keccak256::Digest> for the current state and
full logs, and qmdb::sync::CompactTarget<mmr::Family,keccak256::Digest> for the compact history member;
compile-time assertions and native vectors pin those exact target shapes. The BLS MinSig selection keeps 48-byte signatures and 96-byte public keys and aligns the
separate Tempo certificate profile, while retaining distinct namespace and profile IDs. Dynamic DKG
wrappers use these exact glue::dkg output, dealer-log, and epoch-info types. The final manifest
records the compiler-resolved aliases and their source paths; it may not copy their fields into
Makechain structs.
Working HEAD types are irrelevant even if their names match.
Every revision-1 current, membership, and history commit passes native commit metadata None,
including the current-state genesis seed. A block hash is never placed there because that would
cycle through the header roots. This rule fixes the operation bytes and therefore the roots. It does
not change inner native operation encodings: for example, pinned keyless fixed Commit encodes its
floor using that operation's native big-endian u64 field even though standalone
Location<mmr::Family> uses UInt<u64>.
Every history commit on validators and the exporter is the identical native fixed-keyless
Commit(None, Location(0)); compact storage affects local retention, not the committed floor or
root. Validators and the exporter prune witness material locally. The proof service retains it from
zero.
The canonical header always carries the validator's compact history target. The proof service, which
holds the published operation logs from location zero, constructs the full sync target as
Target { root: header.history_target.root, range: [Location(0), header.history_target.size) } and
requires a nonzero valid size and exact root equality. It does not use the native
TryFrom<&CompactTarget> convenience conversion, which represents only the final-leaf range
[size - 1,size). Proof-service ingestion and proof generation retain floor zero.
10. Proof and receipt wrappers
ProofContextV1 { header: HeaderV1, finality: FinalityEvidenceV1, release: ReleaseLineageV1 }
FinalityEvidenceV1::Direct { finalization: NativeSimplexFinalization }
FinalityEvidenceV1::DescendantAncestry {
descendant_header: HeaderV1,
descendant_finalization: NativeSimplexFinalization,
ancestor_height: Height,
history_location: HistoryLocation,
history_proof: NativeHistoryProof,
}
ReleaseLineageV1 {
schedule_value_bytes: BoundedBytes<max_protocol_schedule_value_bytes>,
schedule_proof: NativeCurrentStateProof,
entries: BoundedVec<ReleaseLineageRecordProofV1,max_release_lineage_entries,max_release_lineage_bytes>,
}
ReleaseLineageRecordProofV1 {
identity: ProtocolReleaseIdentityV1,
value_bytes: BoundedBytes<max_protocol_release_entry_value_bytes>,
proof: NativeCurrentStateProof,
}
ProofEnvelopeV1 {
schema_revision: U16 = 1,
context: ProofContextV1,
payload: ProofPayloadV1,
}
ProofPayloadV1::CurrentState {
identity: StateIdentityV1,
value_bytes: BoundedBytes<max_state_value_bytes>,
proof: NativeCurrentStateProof,
}
ProofPayloadV1::Membership {
project_id: ProjectId,
commitment: CommitmentV1,
location: MembershipLocation,
proof: NativeMembershipProof,
}
ProofPayloadV1::Command {
signed_command_bytes: BoundedBytes<max_signed_command_bytes>,
command_index: CommandIndex,
proof: NativeBmtProof,
}
ProofPayloadV1::Finality {}
ProofPayloadV1::ReleaseLineage {}
ProofPayloadV1::PublicationReceipt {
signed_command_bytes: BoundedBytes<max_signed_command_bytes>,
command_index: CommandIndex,
proof: NativeBmtProof,
commitment_index: CommitmentIndex,
}
This acyclic envelope carries one header, one finality object, one release-lineage object, and one
kind-specific payload. ReleaseLineageV1.schedule_proof verifies the supplied schedule-state value
under context.header.state_root; it does not contain a second proof context.
StateIdentityV1 is the 15-variant tagged union whose variant fields are exactly the identity fields
in section 6. For a current-state proof, the verifier derives the 32-byte key from that typed
identity, decodes value_bytes as the matching family value, requires its repeated identity to
match, constructs the exact native unordered-current Operation::Update(key,value_bytes), and
verifies that operation's activeness. A valid opaque key/value proof cannot be relabeled as another
family. The release-lineage schedule key is the derivable ProtocolScheduleState singleton key and
is not repeated.
Each release-lineage record similarly derives its family key from ProtocolReleaseIdentityV1,
checks the decoded record's repeated identity, and verifies its native current-state proof. The
records start at the active descriptor and follow every authenticated schedule predecessor needed
to reach the proven schedule tail; activation markers are included where activation is claimed.
Empty lineage is valid only when the proven schedule state names the genesis release with no tail.
Finality verification first verifies the native certificate under the epoch-selected scheme, then
requires finalization.proposal.payload == block_hash(header) and
finalization.proposal.round == header.consensus_context. The descendant form applies the same two
checks to descendant_finalization and descendant_header, requires
ancestor_height == header.height < descendant_header.height, derives the exact history location
2 * ancestor_height + 1, requires it equal history_location, and verifies the exact
history leaf(header.height, block_hash(header)) against descendant_header.history_target.
The proof target root and size must equal that header target; a certificate and unrelated header
cannot be paired.
Receipt verification recomputes the command-tree leaf Keccak256(canonical(SignedCommandV1)) from
signed_command_bytes, requires the BMT proof's native leaf count to equal
header.command_count, checks the zero-based index, requires PUSH, and checks the commitment index.
It derives every claim from authenticated bytes. A membership location, event, charge, or resulting
ref revision needs separate evidence.
Membership and history proofs authenticate encoded fixed-keyless operations rather than bare digest
leaves. The verifier constructs the exact native Operation::Append(membership_leaf) or
Operation::Append(history_leaf), uses that operation's native encoding in range-inclusion
verification, and requires proof.leaves to equal the authenticated target's end/size. History uses
floor zero. Both proof kinds call
proof.matches_canonical_inactive_peaks(target_end, target_floor) using the authenticated target
range; for history the floor argument is zero. Verifying the raw 32-byte application leaf against
the Merkle proof is invalid.
Every receipt proves exactly one command, so native bmt::Proof::Read::Cfg is 1; that parameter
is the number of proved items, not a sibling limit. The library consequently permits at most its
32-level derived sibling count. Decode the enclosing proof slice under max_command_proof_bytes
before native decoding and require full consumption.
11. Digest and signing preimages
The fixed digest registry is:
| Name | Preimage |
|---|---|
chain_id |
BLAKE3("makechain.chainspec" || canonical(GenesisChainSpecV1)) |
intent_digest |
BLAKE3("makechain.command" || canonical(CommandIntentV1)) |
command_id |
BLAKE3("makechain.command-envelope" || canonical(SignedCommandV1)) |
block_hash |
Keccak256("makechain.block-header" || canonical(HeaderV1)) (proof path) |
| root node | BLAKE3("makechain.root-authorization" || intent_digest || account || U64(config_version)) |
| ref name | BLAKE3("makechain.ref-name" || canonical(BoundedUtf8<max_ref_name_bytes>)) |
| membership leaf | Keccak256(project_id || scheme_id || native_id) (proof path), the commitment's own CommitmentV1 fields in that order, scheme_id fixed at 32 bytes and native_id running to the end of the preimage |
| command-tree leaf | Keccak256(canonical(SignedCommandV1)) (proof path), one per command of a block, in execution order under command_root |
| history leaf | Keccak256("makechain.block-history" || chain_id || U64(parent_height) || parent_hash) (proof path) |
| project ID | BLAKE3("makechain.project" || chain_id || creator || client_salt) |
| key ID | BLAKE3("makechain.key-id" || canonical(AccountScopedKeyV1)) |
| root slot ID | BLAKE3("makechain.root-slot" || account) |
| commitment ID | BLAKE3("makechain.commitment" || canonical(CommitmentV1)) |
| release digest | BLAKE3("makechain.release" || canonical(ReleaseDescriptorV1)) |
| schedule-entry digest | BLAKE3("makechain.release-schedule-entry" || canonical(ReleaseScheduleEntryV1)) |
| scheme ID | BLAKE3("makechain.scheme" || canonical(SchemeDescriptorV1 without scheme_id)) |
| price-schedule ID | BLAKE3("makechain.price-schedule" || canonical(PriceScheduleV1 without price_schedule_id)) |
| work-table ID | BLAKE3("makechain.work-table" || canonical(WorkTableV1 without work_table_id)) |
| settlement profile ID | BLAKE3("makechain.settlement-profile" || canonical(SettlementSourceProfileV1 without profile_id)) |
| scheme-registry digest | BLAKE3("makechain.scheme-registry" || canonical(SchemeRegistryV1)) |
| effective-parameters digest | BLAKE3("makechain.effective-parameters" || canonical(EffectiveParametersV1)) |
| validator schedule entry ID | BLAKE3("makechain.validator-schedule-entry" || canonical(ValidatorScheduleRecordV1::Schedule)) |
| validator cancel entry ID | BLAKE3("makechain.validator-cancel-entry" || canonical(ValidatorScheduleRecordV1::Cancel)) |
| validator proof of possession | BLAKE3("makechain.validator-possession" || chain_id || U64(target_epoch) || identity_key) |
| governance digest | BLAKE3("makechain.governance" || intent_digest) |
| event ID | BLAKE3("makechain.event" || chain_id || block_hash || U32(command_index) || U32(event_index)) |
| genesis epoch-info hash | BLAKE3("makechain.genesis-epoch-info" || canonical(ProfileEpochInfoV1)) |
| dealer-log hash | BLAKE3("makechain.dealer-log" || canonical(NativeDealerLog)) |
| DKG-outcome hash | BLAKE3("makechain.dkg-outcome" || canonical(NativeEpochInfo)) |
Do not infer any other signature preimage from this table. Revision 4 fixes these family adapters:
- Ed25519 delegated commands use SSHSIG namespace
makechain.command:<chain_id_hex>overintent_digest. - P256 direct signs
SHA-256("makechain.p256" || d), wheredisintent_digestfor a delegate and the root node digest for an owner. - WebAuthn challenge is
intent_digestfor a delegate and the root node digest for an owner. - A secp256k1 owner uses EIP-712 rebuilt from decoded intent/node fields. Makechain
chain_idis the domain salt and the Tempo host chain ID isdomain.chainId.
The canonical Rust registry emits the literal EIP-712 type strings selected by section 3.2, and the reviewed vectors preserve them. Vectors demonstrate these definitions; they never replace them.
12. System event payloads
Finalized events are the derived report of specification section 14. This section fixes the batch
framing, the closed registry of kind tags, and the payload layout of every kind, so that two
implementations produce byte-identical batches for a height and any consumer decodes one without
private knowledge. Every table in this section is Proposed. An event enters no root, so these
bytes are a delivery contract rather than a consensus input; they belong in spec/ because a
consumer re-derives a batch byte for byte from certified inputs instead of trusting a supplied one
(specification R-14.15), which is an output the boundary of README.md keeps
normative.
12.1 Batch and event framing
EventBatchV1 = BoundedVec<EventV1,max_events_per_block,max_event_batch_bytes>
EventV1 {
kind: U8,
command_index: Option<CommandIndex>,
event_index: U32,
payload: BoundedBytes<max_event_payload_bytes>,
}
One batch covers one height and carries no height field, because delivery is one batch per height
and the event identifier already binds the chain and the block. The identifier is the event ID
row of section 11 and is not restated here. command_index is None for a block-level event, and
that event's identifier preimage takes the block-level sentinel fixed by specification section 14.
A wire Some(0xFFFF_FFFF) is a decode error, and section 13 bounds max_commands_per_block below
that sentinel, so no command event and block-level event of one block can share an EventId.
payload is the length-prefixed canonical encoding of the kind's layout in section 12.4, which is
what lets a consumer skip an unknown kind; a decoder that knows the kind consumes the payload slice
completely and rejects trailing bytes.
Batch order is ascending (command_index, event_index), comparing the section 14 sentinel value of
a block-level event rather than the wire Option tag, so the block-level group orders last. For
each applied command, in the flat block order of specification section 12, event_index zero is
CommandFinalized and the command's domain events follow in the order its transition produces them:
for PUSH, one CommitmentAdded per commitment in commitment order and then RefsChanged; for a
rename, the source name before the destination name. event_index restarts at zero for the
block-level group, whose events are ordered by ascending kind tag.
12.2 Kind registry
The registry is closed. A kind is emitted exactly as its tag and layout below, an unregistered tag is invalid, and a later release adds a kind only by appending the next unassigned tag.
| Tag | Kind | Emitted for |
|---|---|---|
0x01 |
CommandFinalized |
every applied command, at event_index zero |
0x02 |
AccountInitialized |
INIT_ACCOUNT |
0x03 |
RootConfigured |
ROOT_CONFIGURE |
0x04 |
KeyAuthorized |
KEY_AUTHORIZE, both variants |
0x05 |
KeyRevoked |
KEY_REVOKE.One |
0x06 |
DelegationEpochAdvanced |
KEY_REVOKE.All |
0x07 |
ProjectCreated |
PROJECT_CREATE |
0x08 |
ProjectMetadataSet |
PROJECT_SET_METADATA |
0x09 |
ProjectStatusSet |
PROJECT_SET_STATUS |
0x0a |
RoleChanged |
ROLE_SET, both variants |
0x0b |
TransferOfferChanged |
TRANSFER_OFFER, both variants |
0x0c |
ProjectTransferred |
TRANSFER_ACCEPT |
0x0d |
CommitmentAdded |
each commitment of an applied PUSH, in commitment order |
0x0e |
RefsChanged |
an applied PUSH, once, covering every affected ref |
0x0f |
RecordReclaimed |
RECLAIM |
0x10 |
UsernameLeaseChanged |
USERNAME_CLAIM, and the destination name of a rename |
0x11 |
UsernameReleased |
USERNAME_RELEASE, and the source name of a rename |
0x12 |
CreditsImported |
IMPORT_CREDITS, both variants |
0x13 |
ReleaseScheduled |
SCHEDULE_RELEASE, all four variants |
0x14 |
ValidatorSetScheduled |
VALIDATOR_SET_SCHEDULE, both variants |
0x15 |
ReleaseActivated |
block level, the activation of a release |
0x16 |
ValidatorSetLockedIn |
block level, the lock-in of a scheduled validator-set entry |
0x17 |
DkgCarryOver |
block level, a reshare carry-over |
0x00 is invalid. 0x18..=0x7f are unassigned and available to a later release.
0x80..=0xff are reserved for a future format revision and are invalid in revision 1. A
retired tag is never reassigned.
12.3 Shared payload shapes
ResourceVectorV1 { block_bytes: U32, work_units: U64, new_records: U32 }
ChargeV1 { fees_charged: Amount256, deposits_locked: Amount256, deposits_discharged: Amount256 }
DepositEntryV1 { record: DepositedRecordV1, deposit: DepositV1 }
DepositListV1 = SortedVec<DepositEntryV1,max_deposit_entries_per_event,max_deposit_entries_bytes>
DepositedRecordV1::DelegatedKey {
account: AccountId, key_id: KeyId, authorization_generation: Generation,
}
DepositedRecordV1::SlugClaim {
owner: AccountId, slug: BoundedUtf8<max_slug_bytes>, project_id: ProjectId,
allocated_counter: Counter,
}
DepositedRecordV1::ProjectRole {
project_id: ProjectId, grantee: AccountId, grant_generation: Generation,
}
DepositedRecordV1::TransferOffer { project_id: ProjectId, revision: Revision }
DepositedRecordV1::Ref { project_id: ProjectId, ref_name_digest: Digest32, revision: Revision }
DepositedRecordV1::UsernameLease {
name: BoundedUtf8<max_username_bytes>, incarnation: IncarnationV1, revision: Revision,
}
RefChangeV1 {
ref_name: BoundedUtf8<max_ref_name_bytes>,
operation: RefOp,
old_target: Option<CommitmentV1>,
old_revision: Option<Revision>,
new_target: Option<CommitmentV1>,
new_revision: Option<Revision>,
}
DepositedRecordV1 uses the record's StateFamilyTag value from section 2 as its variant tag, so
the six deposited families are the only valid tags here and the nine permanent families are invalid.
Each variant carries its family's registered identity fields from section 6 followed by every value
the family's compare-and-set binds (an incarnation, a revision, or both), and DepositV1 carries
the stored payer and the stored amount unchanged.
A discharged entry names the record as it stood before the transition deleted or replaced it; a
locked entry names the record the transition wrote. DepositListV1 is a SortedVec, so its
entries increase by canonical encoding and no entry repeats, which is the canonical order
specification section 14 requires. A payload below carries a list only for a family that can lock
or discharge a deposit, and a transition that locks or discharges nothing carries the empty list.
ResourceVectorV1 holds the three dimensions of specification section 10.1 and ChargeV1 holds
the command's totals, the fees added to spent and the deposits added to and released from
locked under specification section 9.6. fees_charged is every non-refundable amount the command
added to spent, summed across every ledger it touched: the canonical-byte charge, the reserved
work charge, each record creation fee, the username lease price, and the first-import ledger
creation fee that IMPORT_CREDITS charges to the proved beneficiary's own ledger rather than to a
signer's. It never includes a deposit, which deposits_locked carries. Per-record deposit detail is
in the domain event, never in ChargeV1.
In a RefChangeV1, old_target and old_revision are present for a SET or a DELETE and
absent for a CREATE; new_target and new_revision are present for a CREATE or a SET and
absent for a DELETE. A compare-and-set that allocates a fresh revision reports the new value, and
a no-op SET repeats its revision and its target, which is how the outcome of each operation is
read. Operations appear in the order the command applied them, so a repeated name appears once per
operation.
12.4 Payload layouts
CommandFinalized, tag 0x01. The event exists only for an applied command, so it carries
no status field; the command's outcome is its resource vector, its charge, and the domain events
that follow it.
| Field | Type | Width | Meaning |
|---|---|---|---|
command_id |
Digest32 |
32 | the envelope identity of section 11 |
intent_digest |
Digest32 |
32 | the intent identity of section 11 |
command_tag |
U8 |
1 | the CommandPayload tag of section 2.1 |
actor |
Option<AccountId> |
variable | the signing account of an account-context command; absent for a system command, whose charged party the paired domain event names |
key_id |
Option<KeyId> |
variable | the signing slot; absent for a system command |
sequence |
Option<Sequence> |
variable | the accepted replay mark; absent for a system command |
resources |
ResourceVectorV1 |
16 | the reserved resource vector |
charge |
ChargeV1 |
96 | fees charged, deposits locked, deposits discharged |
AccountInitialized, tag 0x02.
| Field | Type | Width | Meaning |
|---|---|---|---|
account |
AccountId |
20 | the initialized account |
config_commitment |
Option<Digest32> |
variable | present for a configured genesis account |
root_sequence |
Sequence |
8 | the accepted positive sequence |
RootConfigured, tag 0x03.
| Field | Type | Width | Meaning |
|---|---|---|---|
account |
AccountId |
20 | the reconfigured account |
config_commitment |
Digest32 |
32 | the stored commitment of the new configuration |
version |
U64 |
8 | the new AccountRoot.version |
KeyAuthorized, tag 0x04. A renewal repeats the preserved capability, scope, window,
generation, and issuing epoch, and carries empty deposit lists.
| Field | Type | Width | Meaning |
|---|---|---|---|
account |
AccountId |
20 | the keychain's account |
key_id |
KeyId |
32 | the authorized slot |
action |
KeyAuthorize |
1 | 00 authorize, 01 renew |
capability |
Capability |
1 | the key's class |
project_scope |
Option<ProjectId> |
variable | the key's scope |
validity_window |
Option<ValidityWindowV1> |
variable | the key's window |
expires_at_ms |
TimestampMs |
8 | the resulting expiry |
authorization_generation |
Generation |
8 | the resulting generation |
issued_delegation_epoch |
Epoch |
8 | the epoch the key was issued in |
keychain_revision |
Revision |
8 | the advanced keychain revision |
deposits_locked |
DepositListV1 |
variable | the new key's deposit |
deposits_discharged |
DepositListV1 |
variable | a replaced invalidated generation, if any |
KeyRevoked, tag 0x05.
| Field | Type | Width | Meaning |
|---|---|---|---|
account |
AccountId |
20 | the keychain's account |
key_id |
KeyId |
32 | the deleted slot |
authorization_generation |
Generation |
8 | the deleted record's generation |
keychain_revision |
Revision |
8 | the advanced keychain revision |
deposits_discharged |
DepositListV1 |
variable | the deleted key's deposit |
DelegationEpochAdvanced, tag 0x06. It carries no discharge list and no key count.
| Field | Type | Width | Meaning |
|---|---|---|---|
account |
AccountId |
20 | the account whose epoch advanced |
old_delegation_epoch |
Epoch |
8 | the epoch before the command |
new_delegation_epoch |
Epoch |
8 | the epoch after the command |
keychain_revision |
Revision |
8 | the keychain revision after the command (R-7.37) |
ProjectCreated, tag 0x07.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the derived project identity |
owner |
AccountId |
20 | the creating controller |
scheme_id |
SchemeId |
32 | the project's fixed scheme |
origin |
Option<CommitmentV1> |
variable | the creator-asserted origin |
slug |
BoundedUtf8<max_slug_bytes> |
variable | the stored canonical slug |
description |
BoundedUtf8<max_project_description_bytes> |
variable | the stored description |
deposits_locked |
DepositListV1 |
variable | the initial slug claim's deposit |
ProjectMetadataSet, tag 0x08.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
slug |
BoundedUtf8<max_slug_bytes> |
variable | the stored canonical slug |
description |
BoundedUtf8<max_project_description_bytes> |
variable | the stored description |
deposits_locked |
DepositListV1 |
variable | the successor slug claim, when the slug changes |
deposits_discharged |
DepositListV1 |
variable | the replaced slug claim, when the slug changes |
ProjectStatusSet, tag 0x09.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
status |
ProjectStatus |
1 | 00 active, 01 frozen |
lifecycle_revision |
Revision |
8 | the advanced lifecycle sub-revision |
RoleChanged, tag 0x0a. The three optional fields are present for a grant and absent for a
revoke, whose deleted record is named by its discharged entry.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
grantee |
AccountId |
20 | the grantee |
action |
RoleSet |
1 | 00 grant, 01 revoke |
role |
Option<ProjectRoleKind> |
variable | the granted role |
grant_generation |
Option<Generation> |
variable | the fresh grant generation |
issued_control_generation |
Option<Generation> |
variable | the project control generation it was issued under |
deposits_locked |
DepositListV1 |
variable | the new grant's deposit |
deposits_discharged |
DepositListV1 |
variable | a replaced or deleted grant's deposit |
TransferOfferChanged, tag 0x0b. The optional fields are present for a set and absent for
a clear.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
action |
TransferOfferAction |
1 | 00 set, 01 clear |
destination |
Option<AccountId> |
variable | the named destination |
revision |
Option<Revision> |
variable | the offer's resulting revision |
issued_control_generation |
Option<Generation> |
variable | the control generation it was issued under |
expires_at_ms |
Option<TimestampMs> |
variable | the offer's expiry |
deposits_locked |
DepositListV1 |
variable | a new or replacement offer's deposit |
deposits_discharged |
DepositListV1 |
variable | a cleared or expired offer's deposit |
ProjectTransferred, tag 0x0c. The event carries the rotated control generation and never
a count of deactivated grants.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
previous_owner |
AccountId |
20 | the controller before acceptance |
new_owner |
AccountId |
20 | the accepting destination |
control_generation |
Generation |
8 | the rotated control generation |
offer_revision |
Revision |
8 | the consumed offer's revision |
destination_slug |
BoundedUtf8<max_slug_bytes> |
variable | the slug claimed under the new owner |
deposits_locked |
DepositListV1 |
variable | the destination slug claim's deposit |
deposits_discharged |
DepositListV1 |
variable | the consumed offer and the old slug claim |
CommitmentAdded, tag 0x0d.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the publishing project |
commitment |
CommitmentV1 |
variable | the published {scheme_id, native_id} pair |
commitment_index |
CommitmentIndex |
4 | the zero-based index in the command's commitment list |
location |
MembershipLocation |
variable | the membership-log location the leaf took |
RefsChanged, tag 0x0e.
| Field | Type | Width | Meaning |
|---|---|---|---|
project_id |
ProjectId |
32 | the project |
changes |
BoundedVec<RefChangeV1,max_ref_ops_per_push,max_ref_changes_bytes> |
variable | one entry per applied ref operation, in operation order |
deposits_locked |
DepositListV1 |
variable | every ref deposit the command locked |
deposits_discharged |
DepositListV1 |
variable | every ref deposit the command discharged |
RecordReclaimed, tag 0x0f.
| Field | Type | Width | Meaning |
|---|---|---|---|
deposits_discharged |
DepositListV1 |
variable | exactly one entry, naming the deleted record, its incarnation, its stored payer, and the stored amount |
UsernameLeaseChanged, tag 0x10.
| Field | Type | Width | Meaning |
|---|---|---|---|
name |
BoundedUtf8<max_username_bytes> |
variable | the affected name |
action |
UsernameClaim |
1 | 00 claim, 01 extend, 02 restore, 03 rename |
owner |
AccountId |
20 | the lease's owner |
incarnation |
IncarnationV1 |
28 | the lease's incarnation |
revision |
Revision |
8 | the resulting lease revision |
resolution_deadline_ms |
TimestampMs |
8 | the resulting resolution deadline |
reservation_deadline_ms |
TimestampMs |
8 | the resulting reservation deadline |
deposits_locked |
DepositListV1 |
variable | a new incarnation's deposit |
deposits_discharged |
DepositListV1 |
variable | a replaced post-reservation lease's deposit |
UsernameReleased, tag 0x11. A release and the source of a rename retain the deposit, so
the payload carries no list.
| Field | Type | Width | Meaning |
|---|---|---|---|
name |
BoundedUtf8<max_username_bytes> |
variable | the released name |
owner |
AccountId |
20 | the lease's owner |
incarnation |
IncarnationV1 |
28 | the lease's incarnation |
revision |
Revision |
8 | the advanced lease revision |
resolution_deadline_ms |
TimestampMs |
8 | the resolution deadline, ended at consensus time |
reservation_deadline_ms |
TimestampMs |
8 | the grace reservation deadline |
CreditsImported, tag 0x12. A refresh moves the checkpoint only, so its beneficiary and
credited fields are absent.
| Field | Type | Width | Meaning |
|---|---|---|---|
action |
ImportCredits |
1 | 00 credit, 01 refresh |
beneficiary |
Option<AccountId> |
variable | the proved beneficiary |
credited |
Option<Amount256> |
variable | the resulting credited cumulative |
settlement_profile_id |
ProfileId |
32 | the profile the evidence verified under |
tempo_height |
U64 |
8 | the accepted certified source height |
tempo_timestamp_ms |
TimestampMs |
8 | the accepted certified source timestamp |
ReleaseScheduled, tag 0x13. A cancellation carries no activation height.
| Field | Type | Width | Meaning |
|---|---|---|---|
action |
ScheduleRelease |
1 | 00 activate, 01 defer, 02 cancel, 03 emergency activate |
release_digest |
ReleaseDigest |
32 | the release the entry names |
entry_digest |
Digest32 |
32 | the appended schedule entry's digest |
activation_height |
Option<Height> |
variable | the resulting activation height |
governance_revision |
Revision |
8 | the key-set revision that authorized the entry |
governance_nonce |
U64 |
8 | the governance nonce the entry consumed |
ValidatorSetScheduled, tag 0x14. The entry's participants and directory stay in the
command body and in state.
| Field | Type | Width | Meaning |
|---|---|---|---|
action |
ValidatorSetSchedule |
1 | 00 append, 01 cancel |
entry_id |
Digest32 |
32 | the appended or cancelled entry's identity |
target_epoch |
Epoch |
8 | the epoch the entry targets |
schedule_revision |
Revision |
8 | the resulting validator-schedule revision |
ReleaseActivated, tag 0x15. Block level.
| Field | Type | Width | Meaning |
|---|---|---|---|
release_digest |
ReleaseDigest |
32 | the release now in force |
ruleset_id |
RulesetId |
8 | the ruleset its manifest names |
ValidatorSetLockedIn, tag 0x16. Block level.
| Field | Type | Width | Meaning |
|---|---|---|---|
entry_id |
Digest32 |
32 | the locked entry's identity |
target_epoch |
Epoch |
8 | the epoch the entry takes effect in |
DkgCarryOver, tag 0x17. Block level. The emitting block is the boundary block of epoch,
the final block of previous_epoch, which is the block whose dkg_outcome body field carries an
EpochInfo whose outcome is Failure.
| Field | Type | Width | Meaning |
|---|---|---|---|
epoch |
Epoch |
8 | the epoch that opens under the carried output |
previous_epoch |
Epoch |
8 | the epoch whose threshold output continues |
dkg_outcome_hash |
Digest32 |
32 | the emitting block's own dkg_outcome_hash header field, the DKG-outcome digest of section 11 taken over that block's dkg_outcome payload, and never the hash of an earlier successful EpochInfo |
13. Bound registry
Every symbol below is a typed rules/profile field in the canonical Rust registry and is recorded by the format manifest. A profile supplies a reviewed numeric value and chain validation checks that all aggregate relationships hold. No implementation default is valid.
Every symbol has exactly one such field, and no field carries a bound that is absent here. Of the
119 symbols below, LimitsV1 and its eleven group sub-structs (section 8) carry 110; the eight
settlement evidence bounds are the fields of SettlementEvidenceLimitsV1, whose names drop the
tempo_ infix, so max_tempo_certificate_bytes is its max_certificate_bytes and likewise for the
header, node-count, per-node, per-path, and total-evidence bounds; and username_horizon_ms is a
field of TimeParametersV1. The struct carrying each group's symbols is the sub-struct named after that group in section 8, except the two cases named above.
Every max_*_bytes, max_*_items, max_*_nodes, max_*_digests, and collection-cardinality
field is U32 unless its fixed value is stated in the table. Work bounds and block/history ranges
are U64. Millisecond durations are U64. A value used as a pinned native decoder usize
configuration is first checked to fit the supported target and then converted; the canonical field
remains U32.
| Group | Required names |
|---|---|
| Top level | max_genesis_chainspec_bytes, max_header_bytes, max_body_bytes, max_block_bytes, max_block_work_units, max_new_records, max_commands_per_block, max_commands_bytes, max_signed_command_bytes, max_command_bytes, max_command_intent_bytes, max_command_work_units, max_command_new_records, max_authorization_bytes, max_unsigned_evidence_bytes, max_reconciliation_blocks |
| Keys and signatures | max_public_key_bytes, max_signature_container_bytes, max_root_approvals = 8, max_root_node_levels = 1, max_nested_account_edges = 0, max_root_approvals_bytes, max_owner_configuration_bytes, max_governance_keys, max_governance_keys_bytes, max_governance_witness_bytes, max_webauthn_credential_id_bytes, max_webauthn_client_data_json_bytes, max_webauthn_rp_id_bytes, max_webauthn_origin_bytes, max_webauthn_origins, max_webauthn_origins_bytes |
| Project and push | max_slug_bytes, max_project_description_bytes, max_native_id_bytes, max_commitments_per_push, max_commitments_bytes, max_ref_ops_per_push, max_ref_ops_bytes, max_ref_name_bytes, max_ref_witnesses, max_push_evidence_bytes, max_membership_proof_digests |
| Username | min_username_bytes, max_username_bytes, max_username_years_per_command, username_horizon_ms |
| State | max_state_value_bytes, one max_<family>_value_bytes for all 15 families, max_protocol_schedule_value_bytes, max_protocol_release_entry_value_bytes, max_effective_parameters_bytes, max_genesis_state_writes, max_genesis_state_bytes, max_current_state_proof_digests, fixed state_bitmap_chunk_bytes = 64 |
| Release | max_release_name_bytes, max_release_descriptor_bytes, max_release_surfaces, max_release_surfaces_bytes, max_parameter_deltas, max_parameter_delta_bytes, max_release_lineage_entries, max_release_lineage_bytes, max_installer_input_bytes, max_installer_writes, max_family_amount_table_bytes, max_command_work_table_bytes, max_signature_work_table_bytes, max_root_depth_work_bytes |
| Validator and namespaces | max_validator_participants = 64, max_validator_participants_bytes, max_validator_directory_bytes, max_validator_single_pop_bytes, max_validator_pop_bytes, max_genesis_committee_bytes, max_genesis_directory_bytes, max_namespace_suffix_bytes, max_exporter_roster, max_exporter_roster_bytes |
| Schemes | max_schemes, max_scheme_registry_bytes, max_scheme_name_bytes, max_scheme_kinds, max_scheme_preimage_rules_bytes |
| Settlement | max_tempo_group_key_bytes, max_tempo_certificate_bytes, max_tempo_header_rlp_bytes, max_tempo_account_proof_nodes, max_tempo_storage_proof_nodes, max_tempo_proof_node_bytes, max_tempo_account_proof_bytes, max_tempo_storage_proof_bytes, max_tempo_credit_evidence_bytes |
| Proof response | max_finality_evidence_bytes, max_history_proof_digests, max_command_proof_bytes, max_proof_response_bytes, max_receipt_bytes, max_historical_proof_work_units, max_preparation_response_bytes |
| Events | max_events_per_block, max_event_batch_bytes, max_event_payload_bytes, max_deposit_entries_per_event, max_deposit_entries_bytes, max_ref_changes_bytes |
Chain validation rejects a profile unless each per-command maximum fits
max_signed_command_bytes, each legal command vector fits max_body_bytes, native proof maxima fit
their library decoder configurations, aggregate byte maxima cover their components without overflow,
the p2p ceiling fits the largest legal block, max_commands_per_block is below the block-level
sentinel 0xFFFF_FFFF of section 12.1, max_events_per_block is at least
3 + max_commands_per_block * (2 + max_commitments_per_push), max_event_batch_bytes is at least
max_events_per_block * max_event_payload_bytes plus the batch and per-event framing, and
max_deposit_entries_per_event is at least max(max_ref_ops_per_push, 2), because
ProjectTransferred discharges two entries, the consumed offer and the old slug claim, and
max_exporter_roster is at least two, because a deployment runs one active exporter and one
standby (specification R-17.15). Numeric calibration is a release input. It is not permission to
omit a bound.
The settlement interface's current candidates, 512-byte certificate, 4 KiB header, 65 nodes per path, 1 KiB per node, 64 KiB per path, and 136 KiB total evidence, remain calibration candidates. They are not defaults in this contract.
14. Expected-vector design
Each vector is reviewed semantic input plus literal expected canonical bytes. Hash vectors also carry the complete preimage bytes and expected digest. Verification vectors carry the expected typed result or stable error class. A missing byte field means unresolved schema, never empty bytes.
VectorCaseV1 {
vector_id,
contract_id,
schema_revision,
profile_id,
ruleset_id,
source_pins,
semantic_input,
expected_canonical_hex,
expected_preimage_hex?,
expected_digest_hex?,
expected_result,
mutation_of?,
}
The vector container is test metadata and is never hashed. Decimal strings represent Amount256
and JSON-unsafe integers. Hex is lowercase and even length.
The minimum accepted corpus contains:
- Zero, one, 127, 128,
u32::MAX, and out-of-range collection counts; every primitive boundary; invalid bool, option, and enum tags; truncation and trailing bytes. - One positive and one negative case for every command and every subvariant in section 4, including fixed-profile rejection of both validator variants and every consensus-critical release path.
- Every authorization wrapper, alternative sufficient root subsets, threshold crossing before the last approval, duplicate/out-of-order approvals, maximum recursion, and one-too-deep recursion.
- Every one of the 15 state keys and values, identity/key mismatch, maximum value, and deposited payer/amount preservation through replacement and discharge.
- Empty, one-command, odd-count, maximum-depth, wrong-count, wrong-index, reordered, missing, and surplus native BMT proof cases.
- Native membership, history, and current-state proofs at floor and size boundaries; wrong generic
profile; inactive current operation; exact decode/re-encode bytes; and roots for metadata
Noneversus rejectedSomemetadata. - Both genesis profiles, missing/mismatched genesis epoch info, post-genesis presence, fixed-profile
nonzero epoch, and every forbidden DKG optional including
Some(empty). - A release lineage with zero and multiple predecessors, defer/cancel/lock boundaries, installer marker replay, a parameter-only release retaining the ruleset ID, and each of the three pause families taking effect on the first activation block.
- Signer-generated secp256k1, SSHSIG, P256 direct, and WebAuthn cases on native and wasm. Negative cases mutate domain, chain, node digest, typed field, low-S form, origin, challenge, flags, and exact received JSON.
- Authentic Moderato and mainnet settlement fixtures per deployment manifest. Generated MPT/RLP vectors cover path structure and malformed inputs but never qualify a positive live import.
- Publication receipts with duplicate commitment leaves, alternative envelope evidence, wrong command count/index, ref-only PUSH, reset-chain replay, direct finality, and ancestry finality.
Generation is reproducible and refuses to overwrite a reviewed vector when bytes change unless the release explicitly authorizes a breaking format. Native and wasm use the same Rust codec and verifier; no hand-written JavaScript encoder or proof verifier is a conformance authority.
15. Remaining inputs and qualification gates
The widths, tags, field order, envelope split, state layouts, signing profiles, storage generics, chainspec catalog, release schemas, and proof framing above are design selections. Conformance has not run. The following finite inputs still prevent a byte-complete, launch-qualified claim.
| ID | Missing input or evidence | Effect |
|---|---|---|
OPEN-01 |
Fill every numeric calibration value named in section 13, every price/work entry, consensus timeout, admission rate, response cap, and historical-work bound. Run deterministic chainspec validation of every aggregate relationship. | Safe decoder configs and maximum-size vectors cannot be emitted. |
OPEN-02 |
Compile the selected Commonware instantiations against tag v2026.9.0, assert the exact Ed25519, Addresses<ValidatorPublicKey>, Output, EpochInfo, SignedDealerLog, target, and proof types, and capture native expected bytes. |
The design is source-backed but native genesis, DKG, target, proof, and certificate bytes are not yet demonstrated. |
OPEN-03 |
Pin the external implementation/text revisions for EIP-712, OpenSSH PROTOCOL.sshsig, P256, WebAuthn, and native Git object hashing; emit every literal generated EIP-712 type string and signer-produced native/wasm vector. |
Signature and Git profile interoperability is designed but not qualified. |
OPEN-04 |
Deploy the reviewed immutable settlement contract on Moderato and mainnet. Supply address, runtime bytes/hash, deployment checkpoint, active identity/from-epoch, and authentic certificate, header, account-proof, and storage-proof fixtures for the pinned certificate candidate. | Positive credit import and deployed settlement profiles remain unavailable. |
OPEN-05 is resolved. Section 12 fixes the event batch framing, the closed registry of kind tags,
and the payload layout of every kind, so event consumers are wire-compatible and the derived history
tables of docs/history-query.md are frozen against it. The expected vectors for those kinds are
carried by SB-09 in docs/implementation-second-batch.md, and the numeric values of the event
bounds are part of the section 13 calibration listed under OPEN-01.
Implementation may build independent selected contracts before these gates finish. It must not fill a missing input with a Rust default, dependency HEAD, local limit, guessed external literal, zero address, or synthetic settlement proof.
15.1 Specification coherence disposition
The revision-4 coherence pass applies these selected representations. This table records the completed alignment surface; it is not an additional approval gate:
| Specification location | Applied disposition |
|---|---|
| §6.1 and §3.4 | The normative wire contract, executable Rust authority, and record-only format manifest roles are aligned. |
| §7.4 | CommandIntent uses the tagged IntentAuthority union; permissionless import has no replay/charge sentinel and governance carries revision and nonce. |
| §§7.2–7.5 | Root authorization uses the secp256k1-only single-key branch and configured branch with address-recomputable configuration. |
| §7.4 and §18.3 | Governance uses the selected digest, sorted threshold rule, all-key emergency rule, and readable per-command EIP-712 projection. |
| §8 command sections | Guard placement uses the §4 Expected<T> branches and conditional parent-counter shapes; derived execution values are not caller fields. |
| §9.2 and §18.5 | Release descriptor, immutable schedule entry, activation marker, validator cancel, and singleton pending records are distinct. |
| §11 and §15 | Native MMR/Keccak256/QMDB/BMT instantiations, the 64-byte current bitmap chunk, MinSig threshold VRF, and acyclic proof envelope are named. |
| §13.3 | Validator proof of possession is chain-, epoch-, and identity-bound; participant records carry no per-validator threshold public key. |
| §18.5 and §18.6 | Installers use the closed handler registry and cannot write monetary ledgers, deposited user records, or replay marks. Rule resolution composes the schedule snapshot with the referenced anchor. |
| §16 | Amount256, slot-zero uint256, and SettlementAnchor.identity.from_epoch use the same contracts as §§1, 6, and 8. |
16. Source ledger
| Source | Contract used here |
|---|---|
spec/SPECIFICATION.md revision 14, sections 5–18 |
Identity, signing semantics, commands, state, blocks, releases, proofs, settlement, and fixed-profile behavior |
spec/state-machine.md |
Variant guards, expected identities/revisions/counters, access shapes, and system-command split |
docs/client-api.md |
Signed-byte handling, witness replacement, proof wrappers, receipt derivation, and reset partitioning |
spec/settlement-interface.md |
Immutable slot-zero cumulative contract, proof fields, uint256 recommendation, external verification order, and candidate bounds |
Commonware v2026.9.0 at d476a2361ce6840d2b9d0aa6fb30a924429046d4 |
commonware-codec framing and the exact native BMT, Merkle, QMDB, Simplex, and threshold certificate types named in section 9 |
Tempo 25b216a661c2cac72807174583371b582b75c286 |
TempoHeader RLP shape and timestamp split; Finalization<Scheme<ed25519::PublicKey,MinSig>, consensus::Digest>, namespace TEMPO, and Keccak header-payload binding; candidate codec evidence, not a settlement-contract deployment manifest or authentic qualification fixture |
Tempo TIP-1107 cluster (TIP-1108, TIP-1110, TIP-1112, TIP-1113, TIP-1114) at e7ba69eb35349d49633e3637f751ce8a3558ebb9 |
Adopted configurable-account owner layout, address/configuration formulas, and single-key upgrade salt; reference semantics independent of live Tempo activation |
mkit cace229e6ae464bdec25ed1b87f063db16e9d1a1 |
Launch object kinds, native IDs, position-bound Merkle object rules, and exact tree/chunked domain bytes |