RFC-0014 — Provenance Commitments & Merkle Scheme¶
| RFC | 0014 |
| Title | Provenance Commitments & Merkle Scheme |
| Slug | provenance-commitments |
| Status | Accepted |
| Track | Standards |
| Authors | @AbdelStark |
| Created | 2026-06-02 |
| Target milestone | v0.1 (episode hashing, Merkle commitment, binding, ledger scaffold); residency over a real boundary and the networked ledger in v0.3; the STARK-friendly-hash migration in Stage D (post-v1.0) |
| Area | provenance |
| Requires | RFC-0003 (the Commitment message and binding), RFC-0004 (the data layer and provenance requirements) |
| Defers to | RFC-0006 (the Phase-2 proofs the commitments enable), RFC-0010 (the global-model content hash recorded in the ledger) |
Summary¶
This RFC specifies the cryptographic commitment scheme that binds each participant's contribution to the data it was computed from. It is the Phase-1 bridge to the Phase-2 verifiable layer (RFC-0006): the commitments are cheap engineering disciplines we honor from day one (RFC-0006 §3) so that adding proofs later needs no rework.
The scheme has four parts. (1) Episode hashing: a canonical, domain-separated byte serialization of
an episode hashes to a SHA-256 leaf. (2) A dataset Merkle tree: a binary tree over the sorted leaf
hashes with domain-separated node hashing, whose root is the dataset commitment R_c
(RFC-0004 §4). (3) Binding: every released pseudo-gradient Δ_c
carries exactly one R_c (INV-COMMIT-BINDING); a delta not bound to a valid root is rejected at the
coordinator with CommitmentMismatch. (4) A contribution ledger: an append-only log of
(round, contributing participants, their roots, the resulting global-model hash) — the audit substrate
(RFC-0004 §5) the Phase-2 layer formalizes.
The public surface this RFC stabilizes — commit_dataset, DatasetCommitment, ContributionLedger
(02 §Provenance) — is consumed by the federation protocol
(RFC-0003 §3, §8), the data layer
(RFC-0004 §4–5), the data model (03 §DatasetCommitment),
and the security model (06 §Provenance). The boundary it states plainly: this
proves data origin/provenance, not data quality and not honest computation.
Motivation¶
In a data-sovereign federation, raw trajectories never cross a boundary (INV-RESIDENCY,
RFC-0004 §2); only released model deltas and commitments do. The current
coordinator sees each released delta in plaintext; privatization and secure aggregation remain target
protections (RFC-0003 §2). That residency property is also a
verifiability problem: a
coordinator that never sees the data cannot, by inspection of a delta, say which data produced it. For
reproducible provenance and for the Phase-2 statement "this update was computed from data under
committed root R_c"
(RFC-0006 §3), the federation needs a succinct, tamper-evident
commitment to each participant's dataset that the participant produces locally and publishes once.
A Merkle tree is the natural construction: the root R_c is a single 32-byte value that commits to the
entire dataset, while inclusion proofs let a Phase-2 prover argue that specific episodes are members of
the committed set without revealing the rest of the dataset. The commitment must be deterministic and
cross-platform stable so two honest implementations compute the identical R_c from the identical
episodes — the same discipline as the checkpoint content hash
(RFC-0010 §Hashing, INV-CHECKPOINT-HASH).
These are inexpensive disciplines in Phase 1 (hashing the data once per epoch) and are the entire prerequisite for the Phase-2 provenance binding (RFC-0006 §3, roadmap step 2b). Honoring them now is strictly cheaper than retrofitting them after the science ships.
Goals¶
- Define episode hashing: a canonical, domain-separated byte serialization of an
Episode(03 §Episode) to a SHA-256 leaf hash, stable across platforms and serialization backends (lance/hdf5/lerobot, RFC-0004 §2). - Define the dataset Merkle tree: leaf ordering, domain-separated leaf vs internal-node hashing,
odd-node handling, and the root
R_ccarried by aDatasetCommitment. - Define inclusion proofs (the membership witness Phase 2 needs) and their verification.
- Define the binding (
INV-COMMIT-BINDING): each releasedΔ_cis associated with exactly oneR_c; theCommitmentmessage (RFC-0003 §8); the ingress check and the errors on failure (CommitmentMismatch,MerkleVerificationError). - Define the
ContributionLedger: the append-only record schema and its append-only invariant. - Pin the Phase-1 commitment hash (SHA-256) and state the STARK-friendly-hash migration as a versioned, deferred choice shared with RFC-0006.
- State the provenance boundary plainly: origin, not quality, not honesty.
Non-Goals¶
- The Phase-2 proofs themselves (aggregation STARK, provenance-binding proof, TEE attestation). Owned by RFC-0006; this RFC ships only the commitments those proofs bind to.
- The data layer (formats, loaders, residency flag) and the public-probe governance. Owned by
RFC-0004; this RFC consumes the
Episodeschema and the data-quality metadata it declares. - Data-quality gating or scoring. Provenance proves origin; quality enforcement beyond declared metadata is out of scope for v0.1 (RFC-0004 §6) and is an Open Question.
- The pseudo-gradient construction and the round lifecycle. Owned by
RFC-0003; this RFC owns only the
Commitmentpayload and its binding semantics. - The checkpoint/artifact format and its content hash. Owned by RFC-0010; the ledger records that hash, it does not define it.
Proposed Design¶
The provenance subsystem lives in lensemble.provenance (01 §Module Map):
merkle.py (tree construction, inclusion proofs), commit.py (commit_dataset, DatasetCommitment),
ledger.py (ContributionLedger, ContributionRecord). It depends only on core/errors,
config, and the Episode type from data; nothing in provenance imports model, gauge, or
federation (01 §Dependency Layering).
1. Domain-separated hashing primitive¶
All hashing is SHA-256 (Phase-1 canonical commitment hash, 09 §Dependencies) with a single-byte domain-separation tag prepended to the preimage. Domain separation prevents a leaf hash from ever colliding with or being reinterpreted as an internal-node hash (the classic second-preimage attack on naive Merkle trees), and isolates the episode/leaf/node/root domains.
import hashlib
from enum import IntEnum
class HashDomain(IntEnum):
"""One-byte domain-separation tag prepended to every preimage."""
EPISODE = 0x00 # canonical episode bytes -> leaf preimage
LEAF = 0x01 # leaf node in the Merkle tree
NODE = 0x02 # internal node (concatenation of two child digests)
ROOT = 0x03 # the committed dataset root R_c
COMMITMENT_HASH = "sha256" # Phase-1 canonical; versioned (see CommitmentScheme below)
DIGEST_SIZE = 32 # bytes, SHA-256
def _h(domain: HashDomain, payload: bytes) -> bytes:
"""Domain-separated SHA-256: H(domain_tag || payload) -> 32 bytes."""
return hashlib.sha256(bytes([domain]) + payload).digest()
The hash is selected by a CommitmentScheme record (below), not hard-coded into call sites, so the
Stage-D migration to a STARK-friendly hash is a versioned change, not a rewrite.
from dataclasses import dataclass
@dataclass(frozen=True)
class CommitmentScheme:
"""The pinned hash + tree parameters a DatasetCommitment was built under.
Recorded on every commitment so a verifier reconstructs the exact scheme."""
hash_name: str = COMMITMENT_HASH # "sha256" (Phase 1); "poseidon2" reserved (Stage D)
digest_size: int = DIGEST_SIZE # bytes
scheme_version: int = 1 # bumped on any tree/hash/serialization change
2. Episode hashing¶
An Episode (03 §Episode) is a sequence of Transition tuples
(o_t, a_t, o_{t+1}) (03 §Transition) plus declared metadata
(RFC-0004 §6). Its leaf hash MUST be independent of the on-disk
serialization backend (lance default, hdf5 portable, lerobot:// adapter,
RFC-0004 §2) so the same logical episode commits to the same leaf
regardless of how it is stored. The canonical episode serialization therefore hashes a normalized
logical form, not raw file bytes:
from typing import Any, Mapping
def canonical_episode_bytes(episode: "Episode") -> bytes:
"""Canonical, backend-independent byte serialization of one episode.
Ordering and encoding are fixed so the bytes are identical cross-platform:
- frames in time order t = 0..T-1;
- each tensor encoded as (shape tuple, dtype string, C-contiguous little-endian
raw bytes) — fixed dtype/endianness, never the platform-native layout;
- scalar metadata serialized as canonical JSON (sorted keys, no whitespace,
UTF-8), restricted to declared data-quality fields (modality, embodiment_id,
ActionSpec digest, episode length) — NEVER raw observations beyond the frames
already covered.
"""
...
def episode_leaf_hash(episode: "Episode") -> bytes:
"""SHA-256 over the EPISODE-domain canonical bytes -> 32-byte leaf preimage source."""
return _h(HashDomain.EPISODE, canonical_episode_bytes(episode))
RISK: the cross-platform stability of canonical_episode_bytes rests on a fixed dtype/endianness and a
fixed frame ordering. Float tensors stored at different precisions (a bf16 capture vs an fp32
re-export of the same episode) hash differently. Resolution plan: episodes are hashed at their stored
dtype and the stored dtype is part of the canonical bytes (so a re-quantized copy is, correctly, a
different episode); the conformance test (Testing Strategy) asserts determinism across two processes and
two backends for a fixed-dtype fixture. The exact canonical byte ordering is shared with the checkpoint
canonicalization Open Question (RFC-0010 §Open Questions).
3. The dataset Merkle tree and R_c¶
Given the multiset of episode leaf preimages, the tree is built deterministically:
- Leaf construction. For each episode, compute
leaf_i = _h(LEAF, episode_leaf_hash(episode_i)). TheLEAFdomain re-hash binds the leaf to its position in the tree-domain and separates it from any rawEPISODEdigest. - Leaf ordering (
INVof the commitment). Sort the leaf digests lexicographically (byte order). Sorting makesR_ca commitment to the set of episodes independent of dataset enumeration order, so two honest builds over the same episodes yield the identical root. (Duplicate episodes produce duplicate leaves; this is intentional — a dataset with two identical episodes is a different set than one with a single copy.) - Internal nodes. Combine adjacent pairs:
parent = _h(NODE, left || right), left-to-right. An odd node at any level is promoted unchanged to the next level (it is not duplicated — duplication is the well-known source of the CVE-2012-2459 Merkle malleability bug; promotion avoids it). - Root. When one node remains, the committed root is
R_c = _h(ROOT, top_node). TheROOTdomain tag ensures the published root is never confusable with an internal node digest.
def merkle_root(leaf_digests: list[bytes]) -> bytes:
"""Deterministic Merkle root over LEAF-domain digests.
Preconditions: every element is DIGEST_SIZE bytes; len >= 1.
Empty input raises ProvenanceError (a dataset with zero episodes cannot commit).
"""
if not leaf_digests:
raise ProvenanceError(code=LensembleErrorCode.EMPTY_DATASET, remediation=...)
level = sorted(leaf_digests) # lexicographic, deterministic
while len(level) > 1:
nxt: list[bytes] = []
for i in range(0, len(level) - 1, 2):
nxt.append(_h(HashDomain.NODE, level[i] + level[i + 1]))
if len(level) % 2 == 1:
nxt.append(level[-1]) # promote odd node (no duplication)
level = nxt
return _h(HashDomain.ROOT, level[0])
ASCII view of a four-leaf tree (the prose above says the same): leaves L0..L3 are sorted, paired into
internal nodes N01 = H(N, L0||L1) and N23 = H(N, L2||L3), combined into T = H(N, N01||N23), and the
published root is R_c = H(ROOT, T).
R_c = H(ROOT, T)
T = H(N, N01 || N23)
┌──────────┴──────────┐
N01 = H(N, L0||L1) N23 = H(N, L2||L3)
┌─────┴─────┐ ┌─────┴─────┐
L0 L1 L2 L3 (L* sorted lexicographically)
4. DatasetCommitment and commit_dataset¶
commit_dataset (02 §Provenance) hashes every episode, builds the tree, and
returns a DatasetCommitment carrying the root R_c, the episode count, the WMCP metadata, and the
scheme it was built under. The commitment is the on-disk, schema-versioned
(09 §Schema Versioning) provenance record validated by a pydantic v2
model (03 §Serialization).
from pydantic import BaseModel
class DatasetCommitment(BaseModel):
"""Tamper-evident commitment to a participant's local dataset (03 §Data Model).
On-disk metadata: pydantic v2 JSON with an explicit integer schema_version."""
schema_version: int # on-disk schema (09 §Schema Versioning); SchemaVersionMismatch on unknown/too-new
root: bytes # R_c, 32 bytes (DIGEST_SIZE)
episode_count: int # number of leaves; >= 1
wmcp_version: str # the latent-contract version the episodes conform to (RFC-0007)
embodiment_id: str # the embodiment whose ActionSpec the actions satisfy (RFC-0007)
scheme: CommitmentScheme # hash + tree params used (versioned for the Stage-D migration)
created_at: str # RFC 3339 UTC
def commit_dataset(dataset: "Dataset") -> DatasetCommitment:
"""Build the Merkle commitment for a local dataset (02 §Provenance).
Precondition: dataset is non-empty and WMCP-conformant (RFC-0007); every episode's
ActionSpec matches the declared embodiment_id (else ContractViolation, raised by data).
Postcondition: commit_dataset(D).root == commit_dataset(D).root for any two builds over
the same logical episodes on any platform (deterministic; cf. INV-CHECKPOINT-HASH style).
Raises: ProvenanceError (empty dataset); the per-episode ContractViolation is raised upstream
by the data layer during ingest (RFC-0004), not here.
Determinism: pure function of the episode set + the CommitmentScheme. No RNG, no I/O ordering
dependence (leaves are sorted).
"""
...
The participant publishes R_c once per epoch via the Commitment message
(RFC-0003 §8) before contributing; the full episodes never leave the
boundary (INV-RESIDENCY, RFC-0004 §2) — only the 32-byte root and the
declared metadata.
5. Inclusion proofs (the Phase-2 membership witness)¶
An inclusion proof witnesses that a specific episode is a member of the committed set without revealing the rest of the dataset — the membership argument the Phase-2 provenance binding (RFC-0006 §3, roadmap 2b) is built on. It is the ordered list of sibling digests on the path from the leaf to the root, with a side bit per level.
@dataclass(frozen=True)
class MerkleProof:
"""Membership witness for one leaf under a committed root."""
leaf: bytes # the LEAF-domain digest being proven
siblings: tuple[bytes, ...] # sibling digest at each level, leaf -> top
path_bits: tuple[bool, ...] # False=sibling on right, True=sibling on left, per level
def prove_inclusion(leaf_digests: list[bytes], target_index: int) -> MerkleProof: ...
def verify_inclusion(proof: MerkleProof, root: bytes, scheme: CommitmentScheme) -> bool:
"""Recompute the root from the leaf + siblings using the scheme's domains.
Returns True iff the recomputed root equals `root`. Promoted odd nodes contribute
no sibling at that level (the proof skips them), matching merkle_root's promotion rule.
A structurally malformed proof raises MerkleVerificationError; a well-formed proof that
simply does not match returns False (callers decide whether a False is an error)."""
...
verify_inclusion is a pure function of public inputs (the proof, the published root, the scheme); like
the frame-alignment recomputation (RFC-0006 §4,
RFC-0002 §5) it is "free" — anyone can check it with no prover.
6. Binding Δ_c to R_c (INV-COMMIT-BINDING)¶
INV-COMMIT-BINDING (03 §Invariants): every released
Δ_cis bound to exactly one dataset Merkle rootR_c. Enforced at coordinator ingress inlensemble.federationagainst theCommitmentmessage, and carried structurally byPseudoGradient.dataset_root(RFC-0003 §3).
The binding has two halves. (a) At construction, the participant sets
PseudoGradient.dataset_root = R_c for the commitment under which the H inner steps were run
(RFC-0003 §3); the field is part of the frozen PseudoGradient
dataclass, so a delta cannot be released without a root. (b) At ingress, the coordinator validates
that the dataset_root on the incoming Update matches the R_c in the participant's Commitment
message for that round and that the root is a syntactically valid 32-byte digest under the round's
CommitmentScheme. The check is a binding check, not a membership recomputation (the coordinator has no
episodes); membership is the Phase-2 inclusion-proof job.
def verify_binding(committed_root: bytes, declared_root: bytes, scheme: CommitmentScheme) -> None:
"""Raise unless the released delta's root matches the participant's committed root.
Pure check: no dataset access. CommitmentMismatch on inequality (never swallowed,
04 §Error Model); MerkleVerificationError on a malformed/short root."""
if len(declared_root) != scheme.digest_size:
raise MerkleVerificationError(code=LensembleErrorCode.MERKLE_VERIFICATION, remediation=...)
if declared_root != committed_root:
raise CommitmentMismatch(code=LensembleErrorCode.COMMITMENT_MISMATCH, remediation=...)
CommitmentMismatch is security-critical and is never caught-and-ignored
(04 §Error-Handling Rules); the
mismatched update is rejected (RFC-0003 §7, §9) and does not enter the
sum, so a participant cannot launder an unattributed delta into the global model.
7. The ContributionLedger¶
The ledger is the append-only audit substrate (RFC-0004 §5): one record
per round, recording the contributing participants, the roots they were bound to, and the global-model
content hash that round produced (RFC-0010,
INV-CHECKPOINT-HASH). It is the data substrate the Phase-2 layer formalizes
(RFC-0006 §5).
class ContributionRecord(BaseModel):
"""One round's contribution provenance (03 §Data Model). On-disk pydantic v2, schema_versioned."""
schema_version: int
round_index: int # the outer round t
participants: tuple[str, ...] # contributing participant ids (sorted, deterministic)
dataset_roots: tuple[bytes, ...] # R_c per participant, index-aligned with `participants`
global_model_hash: bytes # content hash of (θ_{t+1}, φ_{t+1}) (RFC-0010)
prev_record_hash: bytes | None # hash of the prior record (the ledger chain); None at t=0
created_at: str # RFC 3339 UTC
class ContributionLedger:
"""Append-only log of ContributionRecords (02 §Provenance)."""
def append(self, record: ContributionRecord) -> bytes:
"""Append and return the record's content hash. Sets record.prev_record_hash to the
previous tail (a hash chain) so any rewrite of history is detectable.
Raises ProvenanceError if the record's round_index is not strictly greater than the
tail's (the append-only / monotone invariant) or if prev_record_hash does not match
the current tail."""
...
def verify_chain(self) -> bool:
"""Walk the chain: each record's prev_record_hash equals the prior record's content
hash, round_index strictly increases. False (not silently) on any break; a structural
corruption raises MerkleVerificationError."""
...
The prev_record_hash chain makes the ledger tamper-evident: rewriting any past round breaks every
subsequent link. In Phase 1 this is tamper-evidence; the chain plus the per-round commitments are what
the Phase-2 layer upgrades to a tamper-proof statement
(RFC-0006 §2).
8. The provenance boundary (stated plainly)¶
A DatasetCommitment and its binding prove origin: that a released update was computed from data
under committed root R_c. They prove neither:
- data quality —
R_ccommits to whatever episodes the participant supplied; garbage commits as cleanly as gold. Quality is declared metadata (RFC-0004 §6), not proven, and quality gating beyond provenance is out of scope for v0.1 (Open Questions). - honest computation — the binding says nothing about whether the gradient was honestly derived from that data. Honest computation is the Phase-2 problem split across the aggregation STARK, the inner-step TEE attestation, and the inclusion-proof membership argument (RFC-0006 §5).
Stating this boundary is part of the design's integrity (RFC-0006 §2); the security model repeats it (06 §Provenance).
9. Failure modes¶
| Trigger | Detection | Error | System response |
|---|---|---|---|
commit_dataset on an empty dataset |
leaf-count check in merkle_root |
ProvenanceError |
reject the commit; a dataset with zero episodes cannot join a round |
Released Δ_c.dataset_root ≠ participant's committed R_c |
binding check at coordinator ingress (verify_binding) |
CommitmentMismatch |
reject the update; never swallowed; excluded from the sum (INV-COMMIT-BINDING, RFC-0003 §9) |
| Declared root is not a valid 32-byte digest | length check under the round CommitmentScheme |
MerkleVerificationError |
reject the Update/Commitment message at ingress |
| Inclusion proof structurally malformed (wrong sibling count, bad length) | verify_inclusion structural validation |
MerkleVerificationError |
reject the proof (Phase-2 path) |
| Inclusion proof well-formed but does not reconstruct the root | verify_inclusion returns False |
(no raise) | caller decides; a Phase-2 verifier treats False as a failed claim |
Ledger append with non-monotone round_index or wrong prev_record_hash |
append-only invariant check | ProvenanceError |
reject the append; the chain is not extended |
On-disk DatasetCommitment/ContributionRecord with unknown/too-new schema_version |
pydantic load + version gate (RFC-0010) | SchemaVersionMismatch |
refuse to load; run the migration if available |
Two honest builds over the same episodes disagree on R_c |
determinism conformance test (Testing Strategy) | (CI failure) | a defect in canonical_episode_bytes or merkle_root; blocks release |
CommitmentMismatch and MerkleVerificationError derive from ProvenanceError, which derives from
LensembleError; every one carries .code (a LensembleErrorCode value) and .remediation
(04 §Provenance).
Alternatives Considered¶
- A flat hash of the whole dataset vs a Merkle tree. What it is:
R_c = H(concat of all episode bytes). Why considered: simplest possible commitment, smallest code, and sufficient for Phase-1 tamper-evidence alone. Why rejected: a flat hash supports no inclusion proof and no incremental commitment, so it forecloses the Phase-2 provenance-binding membership argument (RFC-0006 §3) — a verifier could not argue that a specific episode is in the committed set without re-hashing the entire dataset, and a participant could not append episodes without recomputing the whole hash. The Merkle tree'sO(log n)proofs and reusable internal nodes are exactly the proof-ready property we honor now to avoid rework later (RFC-0006 §3). - Insertion-order leaves vs sorted leaves. What it is: build the tree in dataset enumeration order
rather than sorting leaf digests. Why considered: avoids the sort and makes inclusion proofs index-
stable. Why rejected: enumeration order is backend- and platform-dependent (
lanceindex order ≠hdf5order ≠lerobotorder, RFC-0004 §2), so two honest builds of the same dataset would produce different roots — breaking the determinism postcondition that makesR_ca reproducible commitment. Sorting makesR_ca commitment to the set, independent of read order, at the cost of a one-time sort. - Duplicating the odd node vs promoting it. What it is: when a level has an odd count, the classic
Bitcoin construction duplicates the last node (
H(N, last||last)). Why considered: it keeps every level a perfect binary level and simplifies proof indexing. Why rejected: duplication is the source of Merkle malleability (CVE-2012-2459) — distinct leaf multisets can hash to the same root — which would undermine the very tamper-evidence the commitment exists for. Promotion (carry the odd node up unchanged) plus per-level domain separation removes the malleability at no security cost. - SHA-256 vs a STARK-friendly hash (Poseidon2) now. What it is: build the tree directly over a
prime-field hash whose circuit is cheap for the Phase-2 STARK (RFC-0006).
Why considered: it would make the Phase-2 provenance-binding proof circuit far smaller, since SHA-256
is expensive to prove in a Circle-STARK. Why rejected for Phase 1: SHA-256 is conservative,
interoperable, and in the standard library (09 §Dependencies);
Phase-2 proofs are
post-v1.0 (RFC-0006); and the
CommitmentSchemerecord makes the hash a versioned, swappable choice, so committing to Poseidon2 prematurely would couple the Phase-1 data layer to an immature prover stack for no Phase-1 benefit. The migration is the shared Open Question. - No ledger / commitments only. What it is: bind
Δ_ctoR_cper round but keep no durable log. Why considered: the binding alone supports per-round rejection of unattributed deltas. Why rejected: without an append-only, hash-chained record there is no durable provenance audit (RFC-0004 §5) and nothing for the Phase-2 layer to formalize; the ledger records which dataset root was bound to each round and resulting model hash.
Drawbacks¶
- Hashing cost over large datasets. Computing leaf hashes touches every episode's frame tensors once per commitment epoch; for foundation-scale data (Stage E) this is non-trivial. Mitigation: commit per epoch, not per round; cache leaf hashes keyed by episode identity so unchanged episodes are not re-hashed; the Phase-2 incremental re-commitment (Open Questions) reuses internal nodes on append.
- Proves origin only. The provenance boundary (§8) is a genuine limitation, not a bug: the commitment cannot detect a participant that commits to honest-looking but adversarial or low-quality data. This is stated plainly rather than papered over; quality gating is deferred and honest computation is the Phase-2 problem.
- SHA-256 is expensive to prove. The conservative Phase-1 choice is exactly the wrong hash for a
cheap Phase-2 circuit; the
CommitmentSchemeversioning is the planned escape, but a migration is real future work with a re-commitment cost (every participant re-hashes its dataset under the new scheme). - Cross-platform hash stability is fragile. It depends on a fixed canonical serialization (§2); a
silent change in dtype handling or frame ordering would split honest implementations. The determinism
conformance test is the guard, but the canonicalization is shared surface area with the checkpoint hash
(RFC-0010) and must be changed only with a
scheme_versionbump.
Migration / Rollout¶
The commitments are honored from day one, ahead of any proof (RFC-0006 §3):
- v0.1 / Stage A.
episode_leaf_hash,merkle_root,commit_dataset/DatasetCommitment,verify_binding, and theContributionLedgerscaffold land with the data layer. Single-site Stage A exercisescommit_dataseton the pooled dataset and recordsContributionRecords locally even though there is no federation yet — this is the cheap tamper-evidence (RFC-0006 §3). - v0.2 / Stage B. The binding is exercised in the simulated federation: each simulated participant
publishes
R_cvia theCommitmentmessage (RFC-0003 §8) and the coordinator runsverify_bindingat ingress; the ledger records the per-round roots and the committed global-model hash. - v0.3 / Stage C. The
Commitmentmessage crosses a real network boundary; the ledger becomes a durable, networked audit log; residency enforcement over the wire guarantees only the 32-byte root and declared metadata leave a boundary, never episodes (INV-RESIDENCY). - Stage D (post-v1.0). Migrate the
CommitmentSchemehash to a STARK-friendly choice (Open Questions) and add the inclusion-proof-backed provenance-binding proof (RFC-0006 §6, roadmap 2b). Thescheme_version/CommitmentSchemecarried on every commitment is what makes this a versioned migration, not a rewrite: old commitments remain verifiable under their recorded scheme.
The hash function is a versioned choice; a scheme change forces re-commitment (every participant rebuilds
R_c under the new scheme) but does not invalidate old records, which carry their own CommitmentScheme.
Testing Strategy¶
CPU-runnable tests on tiny synthetic fixtures (no large downloads; 07 §CI Gates). These realize the Phase-1 proof-readiness checks (RFC-0006 §Testing Strategy) for provenance:
- Merkle root correctness. Build the tree over known leaf sets (sizes 1, 2, 3, 4, 5, 8 — exercising
the odd-node promotion path) and assert
R_cmatches independently hand-computed roots; assert the empty set raisesProvenanceError. - Inclusion-proof correctness. For every leaf in a fixture,
prove_inclusionthenverify_inclusionreturnsTrue; a proof with a flippedpath_bit, a corrupted sibling, or against a wrong root returnsFalse; a structurally malformed proof raisesMerkleVerificationError. Exercise a leaf whose path crosses a promoted odd node. - Hash determinism (cross-platform proxy).
commit_datasetover the same fixed-dtype episode set in two separate processes (and via thelanceandhdf5backends) produces the byte-identicalR_c; permute the dataset enumeration order and assertR_cis unchanged (leaf-sorting property). - Domain separation. Assert a leaf preimage never collides with a node preimage by construction (a
property test that no
EPISODE/LEAF/NODE/ROOTdigest equals another for the same payload). - Binding verification (
INV-COMMIT-BINDING).verify_bindingraisesCommitmentMismatchwhen a delta'sdataset_rootdiffers from the committedR_c, raisesMerkleVerificationErroron a short/malformed root, and passes on a match; assert the error is never swallowed (RFC-0003 §9, 04 §Error-Handling Rules). - Ledger append-only invariant.
appendrejects a non-monotoneround_indexand a wrongprev_record_hashwithProvenanceError;verify_chainreturnsFalseon a rewritten past record andTrueon an intact chain. - Schema round-trip + migration. A
DatasetCommitment/ContributionRecordround-trips through JSON; an unknown/too-newschema_versionraisesSchemaVersionMismatch(RFC-0010). - No-pickle / no-raw-data. Assert the canonical episode serialization contains no pickle opcodes and
that no metadata field carries raw observations beyond the hashed frames (
INV-RESIDENCY, 05 §Redaction).
Numerical-tolerance policy: commitment hashing requires exact byte equality (no tolerance) — a hash that is "close" is wrong (07 §Numerical Tolerance).
Open Questions¶
OPEN QUESTION: Migrate the Phase-1 commitment hash (SHA-256) to a STARK-friendly hash (e.g. Poseidon2)
to keep the Phase-2 provenance-binding circuit cheap. The CommitmentScheme record makes this a
versioned change. Owner @AbdelStark; resolution: Stage D (post-v1.0), shared with
RFC-0006 §Open Questions and the canonical-hash decision in
09 §Dependencies.
OPEN QUESTION: Incremental re-commitment on dataset append. When a participant adds episodes between
rounds, recomputing R_c from scratch is wasteful; a Merkle tree admits O(log n) re-rooting if the
leaf-sorting and odd-node-promotion rules are made append-stable. The current sorted construction
re-sorts on every commit. Owner @AbdelStark; resolution: a follow-up in Stage D when incremental commits
become a cost concern; v0.1 commits per epoch from scratch.
OPEN QUESTION: Quality-gating policy beyond provenance. The commitment proves origin, not quality (RFC-0004 §6); whether and how the federation weights or gates contributions on declared (or attested) quality is unspecified. Owner @AbdelStark; resolution: a Stage-B (v0.2) experiment on whether declared data-quality metadata correlates with contribution value, then a follow-up RFC if a gating mechanism is warranted.
RISK: The cross-platform stability of canonical_episode_bytes (§2) is the load-bearing assumption for a
reproducible R_c; a silent dtype/endianness/ordering change splits honest implementations. Resolution
plan: pin the canonicalization behind CommitmentScheme.scheme_version, gate every change on a
scheme_version bump, and run the two-process two-backend determinism conformance test (Testing
Strategy) on every CI run; share the canonicalization decision with
RFC-0010 §Open Questions.
References¶
- RFC-0004 — Data, Sovereignty & Provenance (the data layer §2, the provenance-commitment requirements §4, provenance records §5, data-quality metadata §6).
- RFC-0006 — Verifiable Contribution (the Phase-2 proofs these commitments bind to §2, the proof-ready requirements §3, the roadmap §7).
- RFC-0003 — Federated Training Protocol (the
Commitmentmessage §8, thePseudoGradient.dataset_rootbinding §3, ingress rejection §9). - RFC-0010 — Checkpoint & Artifact Format (the global-model
content hash recorded in the ledger; canonical-byte hashing,
INV-CHECKPOINT-HASH). - RFC-0007 — WMCP Latent Contract & Embodiment Adapters (the
wmcp_versionandembodiment_id/ActionSpecmetadata on aDatasetCommitment). - RFC-0002 — The Latent Gauge & Frame-Anchored Aggregation (the public-recomputation synergy that, like inclusion-proof verification, needs no prover).
- 03 — Data Model (
Episode,DatasetCommitment,ContributionRecordschemas) · 04 — Error Model (ProvenanceError,CommitmentMismatch,MerkleVerificationError) · 06 — Security (the provenance boundary) · 07 — Testing Strategy (Merkle + binding tests). - External: Merkle (1980) hash trees; the CVE-2012-2459 Merkle-duplication malleability bug (the reason for odd-node promotion + domain separation); Poseidon2 (the candidate STARK-friendly hash, Stage D); Stwo / Circle-STARK (the Phase-2 prover, RFC-0006).