Makechain module and process architecture
| Field | Current value |
|---|---|
| Status | P2 architecture definition; bounded FB-01–FB-12 primitive implementation exists; complete protocol and qualification remain NOT RUN |
| Updated | 2026-09-22 |
| Baseline | Specification revision 15, 2026-09-25 (working tree); Commonware v2026.9.0; exoware 2026.9.0 |
| Authority | Informative architecture artifact; the specification and companions remain authoritative |
This document defines responsibility, dependency direction, and process assembly before repository or crate layout. It preserves protocol-first development, the separate consuming application, and trusted permissioned operators. It introduces no generic blockchain framework, database framework, custom consensus, production hardware profile, or application/product scaffold.
Status labels used below: fixed by the current specification or an accepted owner decision; proposed for review; deferred with a named gate; open pending a decision or evidence.
1. Design rules
- Fixed: canonical protocol meaning is independent of processes, cloud services, and storage products. One Rust implementation produces native and wasm verification behavior.
- Fixed: one deterministic executor owns command semantics for proposal, verification, and application. Admission may call it for an advisory dry-run but never becomes validity authority.
- Fixed: Commonware owns Simplex, marshal, stateful orchestration, DKG progression, database-set barriers, pruning, and sync mechanics. Makechain supplies the exact application, membership, release, schema, mesh-set, and storage-adapter contracts those primitives require.
- Fixed: three authenticated database members have distinct proof and retention semantics. They are not hidden behind an all-purpose key/value repository or transaction abstraction.
- Fixed: finalized events, query tables, and status are derived views. Object availability is a separate external fact; it cannot be reconstructed from a chain commitment. Proof verification starts from caller trust material and authenticated headers, never a service claim.
- Accepted development direction: the first disposable devnet uses a fixed committee and one epoch, then resets to real DKG/reshare qualification. Its minimal process co-location is fixed in §5; production placement remains deferred. Correctness within the enabled profile is mandatory; production hardening follows its stated gates.
2. Domain modules and dependency direction
clients / external services
└─ admission, proof, query, export adapters
process assembly
├─ Commonware actors ── runtime / mesh / storage / custody
└─ Makechain application adapter
├─ application executor ── schema / release / command authentication
│ └─ owned typed-state interface
└─ three-member state adapter ── exact glue DatabaseSet / QMDB types
proof verifier ── schema / release profiles / Commonware crypto and proof types
Arrows point toward dependencies. Process composition is outside the modules; co-location does not permit a dependency to reverse.
| Domain module | Owned interface and behavior | May depend on | Must not own |
|---|---|---|---|
| Canonical schema | Exact bounded types, domain tags, IDs, signing presentations, exact-consumption codec, header/body/proof shapes | commonware-codec, pinned crypto primitives |
RPC framework, database handles, release activation, cloud configuration |
| Release evaluator | rules_for(executing_parent_branch, target_height_time), resolving authenticated schedule/effective parameters through pending ancestors; price/work/profile lookup, activation installers and format support |
Canonical schema, owned typed-state view | Finalized-head shortcuts, scattered height checks, process rollout, storage migration, fallback after activation |
| Command authentication | Family-specific signatures, root witnesses, scoped-key/role decisions, replay context under supplied rules/state | Schema, release evaluator, standard verifiers | HTTP identity, gateway trust, sequence allocation policy |
| Application executor | Ordered command evaluation, bounded overlay, resource accounting, ledger aggregation, membership appends, events, the ordered change set, trace records, and header derivation except member roots and targets, which MerkleizeBlock computes |
Schema, release evaluator, command authentication, settlement verifier, typed state interface | Consensus rounds, mempool policy, durable writes, background jobs, query indexes |
| Three-member state adapter | Typed reads/staged writes for validity state; ordered membership appends/floor; block-history append; targets and canonical roots | Exact Commonware QMDB/glue contracts | Generic repositories, a fourth consensus database, cross-member atomicity claims |
| Engine assembly | Runtime lifecycle; Simplex/marshal/stateful/reshare composition; cancellation; readiness; shared application SyncPlan, separate DKG sync plan, database and custody wiring |
Executor, state adapter, mesh membership, Commonware | Command semantics, protocol bytes, service queries |
| Mesh membership | One tracked peer-set owner for current/scheduled/departing validators plus the exporter and its standby, all primary and all non-voting mesh peers where they do not vote; ceremony-only projection; channel quotas | Chainspec/release view including the governed exporter roster, validator schedule, authenticated lookup mesh |
Voting membership inference, discovery gossip, command validity |
| Proof verifier | Finality/release lineage, command BMT, publication receipt, membership and current-state verification from caller trust anchors | Schema, release profiles, Commonware storage/crypto primitives | Network fetch, status interpretation, object availability, query results as authority |
| Admission and fan-out | Bounded pool, structural/signature/replay checks, optional isolated dry-run, rate limits, f+1 fan-out acknowledgements, advisory status | Schema, finalized snapshot reader, executor as optional caller | Validity, durability promises, validator-to-validator gossip, fresh-sequence retry decisions |
| Finalized export | Idempotent bounded handoff, durable queue of blocks, certificates and operation-log slices, drain to the store and the mirror as one batch per block including the projected history-table rows, durable watermark, watermark sequencing and mirror height-index scan, event re-derivation | Slices from the members' merkleized batches in capture, schema, executor replay |
Blocking any validator's consensus following on its own durability (at its queue bound the exporter holds back only its own marshal acknowledgement, R-12.25), consensus roots for events |
| Query projection | Deterministic history-table rows under a versioned schema prefix, occurrence-preserving, projected inside the exporter's per-block upload batch, with object metadata and publisher assertions in a separate non-reproducible prefix written by the indexer's ingestion worker; watermark-bounded query answers and consumer cursors | Certified blocks/logs, re-derived events, verified objects | Consensus writes, cryptographic absence claims, singleton publisher inference |
| Service authentication | Edge/service identities, ACLs, quotas, availability retrieval policy | Deployment identity and verified chain facts under a freshness policy | Account authorization, consensus validity, object availability claims from hashes |
| Client workflow | Command preparation, durable signing-slot journal, submission/reconciliation, and complete-push composition | Canonical schema, release/profile data, proof verifier, transport adapters | Canonical encoding forks, proof interpretation, automatic fresh-sequence retries under uncertainty |
Inner dependencies on the specifically selected Commonware codec, cryptography, BMT, and QMDB primitives are intentional. A broad trait wall around them would hide consensus-critical behavior without providing a second real adapter.
Forbidden edges
- Schema, releases, authentication, execution, and proof verification → cloud SDKs, HTTP servers, object storage, query engines, or node configuration.
- Executor → Simplex rounds, marshal archives, durable database handles, wall-clock time, admission pools, indexers, or availability reads.
- Consensus/state → query tables, events as inputs, service “not found,” object presence, or gateway status.
- Proof verifier → gateway trust, server-selected chain identity, unauthenticated header/format hints, or native-only code unavailable to wasm.
- DKG/mesh transport → application authorization; transport membership never selects voters.
- Any service → a second implementation of canonical encoding, signing, release rules, or command transitions.
3. Core domain interfaces
These are semantic interfaces, not Rust trait or crate prescriptions.
| Interface | Input | Output / errors | Owner |
|---|---|---|---|
DecodeCanonical<T> |
Bounded bytes and authenticated format profile | Exact value and consumed length; structural/unsupported-format error | Canonical schema |
ResolveRules |
Executing fork's authenticated parent branch, including pending ancestors, plus target height/time | Immutable Rules, prices, limits, verifier profiles; unsupported release is fatal readiness, not fallback |
Release evaluator |
ExecuteOrderedBlock |
Fork-scoped parent batches, authenticated block context/rules, exact ordered envelopes, and Proposal, Verify, or Replay mode |
Deterministic outcome: ordered change set per member, derived header fields other than member roots and targets, command root/count, resource totals, events, trace records; no roots or targets; typed invalid/pending/fatal-local result | Application executor |
EvaluateCommand |
Rules, block-local overlay, exact envelope | Isolated CommandChangeSet or typed rejection; merge occurs only after complete success |
Application executor |
OpenParentView |
Fork-scoped parent branch and authenticated target context | Typed validity reads plus membership/history positions; corruption or unavailable target is never invalidity | Executor owns the narrow read contract; state adapter implements it |
MerkleizeBlock |
Final accumulated changes in stable order | Exactly one batch/commit per member, canonical roots and targets, and the change-set digest of what was staged; no inference of fees/events from collapsed DB diffs | State adapter |
VerifyEvidence |
Caller trust anchor, authenticated header/lineage, exact proof bytes | Typed verified fact or bounded verification/capability error | Proof verifier |
TrackPeerSet |
The governed chainspec exporter roster, which lists the active exporter and the standby, plus scheduled/effective validator views and a set id | One deterministic all-primary tracked union plus ceremony-only projection | Mesh membership |
PublishFinalizedRange |
Durable source watermark and certified block range | Idempotent artifacts/events and advanced durable watermark, or explicit gap/failure | Finalized export |
ExecuteOrderedBlock is shared by propose, verify, and apply. Proposal mode may discard an invalid
candidate before fixing the body; verification rejects a body containing any invalid command; apply
reconstructs ancestors/winners. Those control outcomes differ, but EvaluateCommand, overlay order,
resource reservation, roots, and events do not. Verify is cancellable and side-effect free. Apply
returns database operations to Commonware; it does not persist them itself.
3.1 Stateful application adapter
The Makechain adapter implements the pinned glue::stateful::Application interface directly. The
following contract is fixed even though Rust module paths and canonical field layouts remain P3/P5
work.
| Adapter call | Makechain input and output | Cancellation and ownership |
|---|---|---|
sync_targets(block) |
Reads the three authenticated targets already committed by the decoded block; returns them in (current, membership, history) order |
Pure and synchronous; Stateful selects the certified anchor and owns sync progression |
genesis() |
Returns the one canonical genesis block for the supplied chainspec/profile | Engine assembly owns the chainspec for the process lifetime; mismatch is fatal-local |
propose(context, ancestry, batches, input) |
input contains only a ProposalSource handle. The adapter asks it for candidate leases under explicit block-byte, block-work, new-record, and failed-attempt budgets, executes serially, drops invalid candidates, and returns Proposed { block, merkleized } |
Stateful owns and may drop the future. A candidate remains in the pool unless the returned lease is explicitly committed as included or dropped with a reason; cancellation commits neither. No durable effect occurs |
verify(context, ancestry, batches) |
Takes the candidate block from the first entry of Stateful's candidate-first ancestry, re-executes its exact canonical body and every header commitment, and returns the three merkleized batches only on equality | Stateful owns and may retry/drop the future. Transient ancestry/resource availability keeps the future pending; deterministic invalidity returns None; local corruption stops the process |
apply(context, block, batches) |
Replays the exact block body, compares every header commitment including the canonical state_root (R-12.11, TE-6), and returns the same merkleized batches as verification. The comparison is the adapter's because glue checks only the ops target and commits an apply replay without re-checking block-specific commitments (glue/src/stateful/mod.rs:267-270, :298-304) |
Stateful owns lazy-recovery/finalized-winner reconstruction. Cancellation leaks no effect; deterministic failure, a commitment mismatch included, returns None, which rejects the dependents of an uncertified ancestor, and inability to reconstruct a finalized winner is fatal in the wrapper |
capture(context, block, batches, readers) |
Copies FinalizedHint { chain_id, height, block_hash, targets, ruleset_id }; it does not derive events or retain readers. On an exporter it also extracts the block's membership-log and block-history slices from the two members' merkleized batches (operations, operation-range proof, pinned nodes; R-17.10) and returns them as owned data |
Runs on Stateful's serial mailbox; bounded by one small owned record, and on an exporter by the block's own slices. The batches are still unapplied, which the compact member's proof requires |
finalized(context, block, hint, readers) |
On a validator, calls non-blocking EnqueueFinalized(hint) and records its outcome, returning without storage or network I/O. Production engine assembly installs DiscardSink, the no-op record sink defined with the RecordSink trait in makechain-execution, and makechain-node never depends on makechain-trace; under glue::simulate only, the StashSink of the tests/trace-sim harness additionally reads the block's state back through readers (trace design record docs/execution/design/vp-02-trace-emission.md, TE-19). On an exporter it additionally appends one durable queue entry holding the slices capture returned, since it receives no batch and the compact history member keeps no operations; it still performs no network I/O |
Redelivery is allowed. On a validator, Full or Closed marks a scan-needed metric but is success for this observer because the certified block store is the recovery source. On an exporter the queue append is its only storage effect and the queue entry is the only slice source (R-17.10): an upload failure grows the queue, and at the queue bound the hook waits, holding back the block's marshal acknowledgement |
ExecuteOrderedBlock and its overlay are one implementation used by the first three execution
calls. Adapter control flow may differ, but command evaluation, reservations, MerkleizeBlock root
construction, and header comparison may not. Commonware owns ancestry reconstruction, pending-branch lifetime,
database application, barriers, pruning, and marshal acknowledgement.
Genesis database handoff
Pinned Stateful does not call Application::apply for genesis. On the marshal-start path it opens
the database set, treats genesis as the already-processed anchor, and compares the committed targets
with sync_targets(genesis); a mismatch enters rewind recovery, which cannot advance a fresh
database to a post-bootstrap genesis target. A redelivered genesis is acknowledged without running
the application hooks. Makechain therefore owns one pre-Stateful bootstrap task; no second component
may seed genesis records.
The task first derives the complete chainspec, chain_id, body, and every genesis-header input that
does not depend on application state. It opens only the current-state member at its native bootstrap
target and builds one bounded batch containing the canonical protocol-genesis records in increasing
state-key order. Merkleization is speculative: the task uses its canonical root and sync target to
construct and verify the complete genesis block and hash before applying any current-state
operation. It then applies the exact batch, starts and awaits that member's durability handle, drops
the handle, reopens the member, and requires both canonical root and sync target to equal the
genesis reference before Stateful or a network actor starts. Mutation failure consumes the owned
database handle and is fatal-local; recovery reopens the partition rather than continuing with an
in-memory object.
Membership and history remain at their pinned native bootstrap commitments for genesis. The task
does not merkleize an empty batch for them because a keyless empty batch still appends a commit
operation. The expected current root and target are the idempotence marker: native bootstrap means
the task may build and apply once, exact genesis means it must not apply again, and any other target
or root is fatal-local. No separate local marker or database initialization flag selects this path.
When the application SyncPlan has a selected or persisted peer-sync floor, the task skips genesis
seeding and state sync reconstructs the certified operations in fresh members. Ordinary restart
also skips seeding because its committed current member already equals or advances from the genesis
anchor.
Source basis at v2026.9.0: glue/src/stateful/actor/syncer/mod.rs:380-451 opens and reconciles the
marshal anchor; glue/src/stateful/actor/core/processing.rs:425-445 treats that anchor's redelivery as
a no-op; and the current, standard-keyless, and compact-keyless ManagedDb::initial_sync_target
implementations use the native bootstrap commitment at operation size one. This source behavior is
the reason genesis seeding is an engine-assembly responsibility rather than an executor transition.
3.2 Admission and proposal interfaces
Admission and proposal deliberately use separate state and work adapters.
| Interface | Contract | Owner |
|---|---|---|
OpenAdmissionSnapshot |
Returns one immutable, finalized AdmissionSnapshot { checkpoint, rules, bounded_records } for the explicitly requested read set. Unavailable or an over-budget read yields NotReady; it never falls back to speculative batches |
Gateway or validator admission adapter; production may use an authenticated follower/read replica, while the first devnet uses the local finalized projection |
Admit |
Takes exact envelope bytes, snapshot, ingress identity, and admission budget. Returns the specification admission class plus checkpoint, intent/command IDs, and optional conflict detail | Admission task; structural/signature/chain/replay work is mandatory, dry-run is off by default |
DryRun |
Calls EvaluateCommand against a disposable overlay made from the admission snapshot, under a dedicated semaphore and CPU/work budget |
Admission worker pool; timeout/cancellation returns advisory NotReady/StateConflict only and never consumes proposal permits |
InsertCandidate |
Atomically applies same-slot replacement, count/byte bounds, and drop-oldest eviction; returns the inserted/known/replaced/evicted identities | Each validator's pool task; status is local and finite |
SelectForProposal |
Takes ruleset plus remaining block and attempt budgets; returns an ordered CandidateLease batch and charges every selected attempt until commit, defer, or drop(reason) |
Consensus proposal path owns the lease; selection never calls admission dry-run |
FanOut |
Sends an admitted envelope concurrently to the fixed validator set with one bounded in-flight queue per validator; returns per-target outcomes and Accepted after f+1 acknowledgements |
Gateway fan-out task; cancellation closes unsent work and preserves received outcomes |
The snapshot interface is separate from OpenParentView: advisory admission sees one finalized cut;
proposal, verification, and replay receive branch-scoped Commonware batches. They share canonical
schema, authentication, and EvaluateCommand, but no mutable view, semaphore, queue, or work budget.
This resolves A3 without relying on a generic repository interface.
In the first devnet, OpenAdmissionSnapshot reads a separately owned derived projection updated by
certified sequential block replay. The projection commits its explicit admission records and
checkpoint together, then atomically swaps the readable snapshot. It may lag and return NotReady;
it never reads the live QMDB batches. A later production adapter may use an authenticated follower or
proof-backed snapshot without changing callers.
3.3 Finalized export interface
EnqueueFinalized is a count-and-byte-bounded in-memory hint channel. It is deliberately lossy:
Queued, Coalesced, Full, and Closed are observable outcomes, and neither Full nor process
exit blocks Stateful. The exporter owns one durable at-least-once staging queue and one durable
ExportWatermark { chain_id, next_height, predecessor_hash }.
The exporter enforces its durable queue bound before Commonware enqueue using
tip - ack_floor plus the encoded bytes of non-pruned entries, recomputed during restart replay.
At the bound the finalized hook waits for space instead of skipping the append, which holds back
the block's marshal acknowledgement until the drain frees an entry (R-17.10); no height is left
unstaged. Commonware supplies durable FIFO positions and restart redelivery,
while Makechain owns this admission bound and never treats queue presence as exported progress.
The worker always computes work from the watermark and the queue, never from a hint; the queue entry is the only source of a height's material (R-17.10):
- Select exactly
next_height, take its canonical body/header/finality evidence and both operation-log ranges, each with its operation-range proof and pinned nodes, from its queue entry, and verify continuity frompredecessor_hash. - Upload the operation ranges, the block, the certificate, and the projected history rows to the exoware store in one batch, and publish the same per-block object to the mirror with create-if-absent semantics; an existing different digest is fatal-local. Re-derived event inputs are built from the same material. Uploads are idempotent and may overlap or arrive out of order, so a retry repeats the identical range.
- Verify the uploaded rows and the published mirror object, then atomically persist the next watermark. Only afterward acknowledge/prune the staging entry.
- Repeat until the highest staged height. A hint can wake or coalesce work, but can never skip a height.
On restart the Commonware queue re-delivers every non-pruned item and the drain resumes from the watermark; marshal redelivers every block above the processed height, whose hooks stage it again. If the queue is unavailable after an interrupted mutation, reopen it from its partition; a height above the watermark and at or below the highest returned hook with no queue entry, outside a state-synced range, is a fatal publication gap: stop publication and alert (R-17.10). The scan keeps only watermark sequencing and the mirror height-index comparison. This resolves A4 without making exporter durability part of any validator's consensus acknowledgement; the wait at the queue bound holds back only the exporter's own marshal acknowledgement (R-12.25).
4. Commonware assembly and ownership
Fixed production baseline: Tokio runtime; authenticated lookup mesh; Simplex with Random V1
rotating terms; marshal::standard::Deferred; dkg::reshare::Application; stateful::Stateful;
the Makechain application; a three-tuple DatabaseSet; one shared application SyncPlan; one
injected parallel strategy; and the separate DKG sync plan/fence/gate. Coding marshal and stable
terms require later release decisions; an upstream Commonware fork is prohibited.
| Commonware seam | Makechain supplies | Commonware owns | Required check |
|---|---|---|---|
| Stateful application | canonical block context, propose/verify/apply/finalized/capture behavior, all header checks including state_root |
fork-scoped execution lifecycle, ancestry reconstruction, cancellation | APP parity, cancellation, late invalidity, finalized-winner reconstruction |
| Database set | exact current-unordered-variable, membership-keyless-fixed, history-keyless role adapters and chainspec root parameters | per-member finalize handles, durability barrier, pruning dispatch, target reconciliation | roots/targets agree after crash; no cross-member atomicity claim |
| Marshal | block codec/limits, retention coupling and the per-role block-store choice | deferred proposal verification/persistence, certificates, backfill | largest legal body fits p2p; retained history covers selected recovery envelope |
| Reshare/DKG | immutable epoch participant/directory provider, schedule policy, payload attachment, durable SecretStore adapter |
ceremony progression, carry-over, epoch output, DKG sync plan and startup gates | set-change plus DkgFailed, restart, share handoff/erasure policy |
| Mesh | the single all-primary tracked union, including the exporter and the standby as non-voting mesh peers from the governed roster, and the channel plan | authenticated transport and peer-set mechanics | the exporter and standby peers survive ceremony projections; bounds cover largest union |
| Sync source | certified targets, untrusted bulk/tail sources, role-specific retained operations | strictly advancing state sync and persisted plan | all three targets/root match one certified header before voting |
The canonical header carries history as CompactTarget { root, size }. Every node role, validator
and exporter alike, runs the compact history adapter and consumes that target directly, and every
role supplies inactivity floor zero when merkleizing a history batch, so all roles execute the same
encoded operation stream and obtain the same root. The full operation history lives in the store,
rebuilt from the slices the exporter uploads, and the full target Target { root, range: 0..size }
is derived there as a proof-service reconstruction step rather than by any node's startup check.
That reconstruction must not use Commonware's native TryFrom<&CompactTarget>: at the pinned
revision it deliberately creates size - 1..size, which retains only the last operation.
Commonware's compact keyless implementation documents that compact and full databases compute
the same root when they receive the identical encoded operation stream and inactivity floor
(storage/src/qmdb/keyless/compact.rs; conversion in
storage/src/qmdb/sync/target.rs:112-128, revision pinned below).
Background work and fault ownership
| Work | Queue/backpressure owner | Cancellation and safe outcome |
|---|---|---|
| Admission and fan-out | Admission adapter; bounded count/bytes, dedicated dry-run permits, and bounded per-validator in-flight sends | Eviction/replacement/rejection is an explicit advisory outcome; shutdown/release change cancels sends, never invents inclusion |
| Proposal/verification/apply | Commonware actors own fork jobs; executor accounts local work | Dead-fork verification is cancellable; apply ancestry follows Commonware recovery; no partial executor effects escape |
| Finalized handoff | Makechain application adapter offers one small hint to a bounded in-memory channel | Full/Closed records scan-needed and returns; the durable watermark, not the hint, finds every gap |
| Export/query | The exporter owns the single-reader durable staging queue, retry state, and the upload batch; consumers own their own cursors and backpressure | Restart reopens/replays the queue and resumes from watermark; lag affects service freshness, not validity |
| State/DKG sync | Stateful/reshare actors own plans and gates; SecretStore owns custody-write completion |
Cancellation cannot expose readiness; transient lack stays pending, corruption stops the role |
| Fault category | Meaning and owner response |
|---|---|
| Invalid | Deterministic protocol failure. Proposal may discard a candidate; verification rejects a block; no effects merge |
| Pending | Transient local inability such as missing ancestry/history or bounded future-time wait. Keep pending or abstain; never vote invalid |
| Fatal-local | Corruption, failed finalized-winner reconstruction, unsupported active release, or irrecoverable local contract failure. Stop readiness and recover |
| Service outcome | Rate limit, unavailable proof/object/query, stale view, or unsupported capability. Report explicitly; never convert it into a consensus result |
The finalized application hook sees readable state before the database-set durability barrier and
may be redelivered or skipped by state sync. It hands off bounded owned data promptly. Exporters and
indexers advance their own watermarks only after durable publication; an exporter publishes only
from its queue entries (R-17.10), and an indexer fills gaps from the stored blocks. A finalized event is never a receipt; proof availability is a separate
milestone.
Pinned source basis, not Makechain qualification evidence: Commonware v2026.9.0
(d476a2361ce6840d2b9d0aa6fb30a924429046d4), especially glue::stateful,
glue::dkg::{bootstrap,reshare}, consensus::marshal::standard::Deferred, and the QMDB adapters.
The exact primitive mapping at that revision is: application calls and cancellation from
glue/src/stateful/mod.rs:124-390; tuple member batches/barriers/readers from
glue/src/stateful/db/mod.rs:210-435,966-1084; staged current-state reads from
glue/src/stateful/db/current.rs; and the export staging buffer from
storage/src/queue/{mod,shared,storage}.rs. The queue is durable and at least once, exposes cloned
writers and one reader, and persists pruning only on sync; it does not provide Makechain's byte
limit, certified gap choice, artifact idempotence, or exporter watermark, so those remain in the
Finalized export module rather than being attributed to Commonware.
5. Process assemblies
Logical roles remain distinct even when one process hosts several adapters.
| Assembly | Modules hosted | Persistent authority / serving rule |
|---|---|---|
| Validator | Engine, executor, state adapter, mesh, local admission pool, diagnostics | Votes; three databases, marshal window, journal and DKG custody; serves no public reads or historical proofs |
| Exporter | Non-voting Stateful/executor follower on the same binary and a non-voting mesh peer from the governed roster; the same three-member tuple and marshal block store with validator-shaped retention, capture-time slice extraction, the durable publication queue, and the drain to the store and the mirror | Holds no vote; publishes every finalized block, certificate, and operation-log slice, and serves current-state proofs with activeness plus within-window membership proofs from its live databases. One active exporter publishes; one standby follows with publication off, keeps its own durable queue of every finalized block, and releases an entry only on an observed store and mirror watermark past it. Either state-syncs at its first installation only, recording its sync anchor A durably with queue coverage from A + 1, permitted only while every height in (W, A] is durable elsewhere, and with a certificate-verified sync-target finalization |
| Proof service | The exoware operation-log service over the store, plus the Makechain checker | Serves membership inclusion at any location against the root at any size, and block-history ancestry proofs; the checker verifies each block's finalization certificate from its own trust anchor and then asserts the store's root at the ingested size against that verified header's membership_target and history_target, and a mismatch fails ingestion rather than a proof request |
| Gateway/admission | Admission, bounded fan-out/status, proof/query pass-through with query answers cached by (query, watermark height), service authentication | No consensus authority; Accepted is f+1 acknowledgement, never durability/finality |
| Indexer/query | A service over the store: the query API over the history tables, the event subscription, "not found" and dedup, the query facade, and command-inclusion proofs and publication receipts built from stored blocks. It owns no database of its own, and the exporter projects every block-derived row; its one writer is an object-fetching ingestion worker that fills the two non-reproducible tables, object metadata and publisher assertions, in a separate schema prefix, because neither object bytes nor off-chain signed messages travel in a block | Rebuildable; every answer is bounded by the published watermark and exposes it with its evidence class; the BMT is stateless, so a receipt needs the stored block and nothing else; the ingestion prefix is outside the rebuild-from-mirror equality check |
| Settlement relayer | Tempo evidence acquisition/verification and IMPORT_CREDITS submission |
No credit authority beyond exact proof accepted by execution |
| Availability layer (several per project) | mkit object verification/storage, chain-authorized writes, per-layer disclosure and retrieval policy | External content custody; no consensus fact or chain-credit debit; obligations are in availability profile |
| Native/wasm verifier | Canonical schema and proof verifier only | Caller-selected trust anchor; no node/cloud runtime, live storage handles, network I/O, status interpretation, or workflow journal; selected Commonware proof types remain dependencies |
| Later SDK/CLI | Client workflow plus generated transport bindings around the native/wasm verifier | Deferred until its consumed contracts pass their gates; it is not part of the first implementation slice |
Minimal development composition (fixed design): run each of the seven validator identities as a separate engine process, and co-locate its local admission adapter only. One exporter process may also host the store and the proof service for the first slice; gateway, indexer/query, settlement fixture tool, and the availability-layer test adapter remain separate processes only when their scenario is exercised. Preserve interfaces, separate durable partitions/watermarks, and proof trust boundaries. Use the real three-member database set and executor from the first end-to-end slice. Deterministic-runtime test adapters may replay captured authentic Tempo evidence and replace availability/object-storage transport, while using the real codec/verifier contracts. No adapter may synthesize a positive Tempo certificate or bypass import verification. Devnets/testnets require Moderato proofs; mainnet requires Tempo mainnet proofs. No genesis credit allocations or Makechain faucet bypass are permitted. The first funded slice imports credits before account initialization; see operations.
Co-location is a process-count choice only. Seven identities in one project, cluster, region, or administrative team are not claimed as independent trust or failure domains. SDK, CLI, explorer, website, and the separate consuming application are deferred consumers; no early scaffold is part of this architecture definition.
Production composition (fixed responsibilities, open placement): separate validator identities; one active exporter and one standby, failure-isolated from each other so their durable queues cannot be lost in one event; two failure-isolated store copies, the exoware store and the mirror bucket; independently scalable admission, export/query, and relayer processes, plus the availability layers described below. Low/medium/high workload and machine counts remain operating-envelope/P4 inputs; module boundaries do not change with size.
Availability is not one service. A project may be served by several availability layers, each with its own authentication, disclosure and retrieval policy, and the chain records no locator for any of them: a project's layers are announced off chain by messages signed with the project's keys. No protocol role depends on a particular layer, and object unavailability never changes the validity of a finalized publication. The nine obligations a layer takes on when it serves a Makechain project are in availability profile.
For the accepted first shared devnet, the mirror's per-block objects use the GCS adapter defined in operations. That decision excludes repository objects, does not authenticate protocol bytes, and does not select production replication or store placement.
5.1 Exporter, store, and proof service
An exporter is a real non-voting Stateful node on the same binary, not a file mirror. It executes
every finalized block with the production executor, uses the same three-member tuple and root
parameters, and prunes exactly as a validator does: validity state at active size, the membership
log windowed to [floor, size), and the block-history accumulator compact. It checks all three
targets plus canonical state_root at each certified cut. Its marshal block store prunes after
retained_marshal_blocks like a validator's; a block it has not yet published stays readable from
its durable queue, which releases an entry only after the store and the mirror both accepted it.
Proof workers receive read-only handles; they never mutate the live databases.
An exporter state-syncs at its first installation and never again; afterwards it follows marshal,
which is glue's own rule for a node whose sync has completed. Under R-17.21 state sync installs a
height range without running the finalized hook, so nothing that range contains reaches the
installing node's queue. On completion the node records its sync anchor A durably, and its own
queue coverage begins at A + 1. From the start of the installation until A is recorded, the
node re-evaluates at every retargeting of its sync that every height in (W, current target] is
durable elsewhere, W being the publication watermark observed in both the store and the mirror at
the start of the installation: either the publication watermark has reached the current target, or
the other roster member is healthy, meaning its queue coverage is at least the current target and
its publication watermark has advanced within the exporter publish-lag bound. If neither holds at
any evaluation, the node aborts the installation and alerts; an installation that completes anyway
loses (W, A] under the first cause of the history-range-loss risk in specification section 4.4
(R-17.21). Because that coverage carries the safety, the sync target is free,
which is what lets a fresh installation, including a replacement standby, sync to a recent target
and start with an empty queue. The finalization the plan pins as that target is read from the
store's finalized-by-height record and is certificate-verified before the plan adopts it, since the
store authenticates no writer. An exporter for which max(own queue coverage, publication watermark) lies below the validators' retained window has no source for the missing range, so it
stops and alerts instead of syncing. The same rule binds the standby.
Publication starts in the capture hook, which runs before apply, and continues in the
finalized hook, which runs after apply and before pruning (R-17.10). In capture the exporter
extracts the block's slices of the membership-log and block-history operation logs from the two
members' merkleized batches: the raw canonical operation bytes of each range together with that
range's Commonware operation-range proof and pinned nodes, which are exactly the inputs the exoware
writer authenticates against a caller-supplied root. It cannot do this later, because a compact
batch proves its operations only until it is applied and the compact member keeps no history
(storage/src/qmdb/keyless/compact.rs:1-8), and finalized receives readers but no batch
(glue/src/stateful/actor/processor/mod.rs:939-962). In finalized it appends the block, its
finalization certificate, and those two ranges to a local durable queue
(commonware_storage::queue) and returns. A drain task uploads each entry to the store and to the
mirror, retries forever, and acknowledges the queue position only after both have accepted it. An
outage grows the local queue; only at the queue bound does the hook wait, holding back the block's
marshal acknowledgement. Uploads are idempotent, so a retry repeats the
identical range rather than reconstructing one.
The drain commits block batches to the store strictly in height order, one at a time, because a query bounded by a published watermark H would otherwise miss the rows of a lower height whose batch had not landed yet. Concurrency is admitted in two places that cannot reorder a height: the qmdb node rows staged ahead of the ordered batch, and the mirror uploads, which are content-addressed per block and carry their own watermark advanced only after the per-block object and its height-index entry are both readable.
The store is two copies of the same history, and either is rebuildable from the other.
- The exoware store:
exoware-simplexfor blocks and certificates, keyed by digest for headers and blocks, by epoch and view forNotarizedandFinalizedrecords, with a finalized-by-height index;exoware-qmdbfor both operation logs, which persists Merkle nodes as ranges arrive and answers historical roots and proofs at any caller-chosen size; andexoware-sqlfor the history tables, which is the query tier described in history-query.md. The exporter projects each block's rows for those tables inside the same upload batch, so the rows land atomically with the block, its certificate, and both slices. All three sit behindexoware-server, a backend-agnostic ConnectRPC router over theSequence,Ingest,Query,Prune,Log, andRetentioncapability traits, on a RocksDB backend hardened from the simulator implementation and placed on a persistent disk with snapshots. exoware ships no replication, snapshot, or backup of its own, and its server carries no authentication, so the disk snapshots, the mirror, and the private-network placement in section 3.3 of operations.md are all Makechain's. - The mirror: one raw object per block, holding the block, the certificate, and both slices,
content-addressed by
block_hash, with a height index and a durable watermark, on an object-storage bucket under a retention lock, kept forever. A second provider may be added later by replaying the mirror.
The proof service is the exoware operation-log service plus the Makechain checker. exoware answers
membership inclusion for any location against the operation-log root at any size, and block-history
ancestry, from its persisted Merkle nodes; Makechain writes no prover. exoware verifies a proof's
internal consistency but never compares it against an external commitment, because the trusted root
is the caller's input. The checker supplies that comparison: at every ingested block it asserts that
the store's root at that size equals the header's membership_target and history_target, and a
mismatch fails ingestion loudly rather than failing a proof request later.
The checker takes those targets from certificate-verified headers only. It verifies each header's finalization certificate against its own trust anchor before it compares anything, because the store authenticates no writer and performs no certificate verification of its own, so a header and a log read back from the store are self-consistent whoever wrote them. Reading the comparand from the store would therefore compare the store against itself. The checker is detective rather than preventive, since proofs are servable from the moment a watermark is published, and its unique value is detecting a foreign write or corruption; the periodic replay job verifies certificates the same way. Stopping ingestion is a concrete act: the checker signals the active exporter's drain to halt, so nothing further enters the store, while the store keeps serving the range it already holds.
The proof service maps the header's compact (root, size) to the full target
Target { root, range: 0..size } from the store's published operation logs, requiring exact root
equality and a nonzero valid size. That reconstruction happens in the proof service alone, never in
a node's readiness check, and it must not use the library's compact-to-full convenience conversion,
which retains only the last operation.
Historical current-state proofs are not offered. The exporter serves current-state proofs, with activeness, and within-window membership proofs from its live databases behind a read lock. A later release may add proofs at an older height by batch replay of stored blocks into a fresh instance; the live current database is never rewound for a request. Validators join and restart by state sync from validators, so no bootstrap artifact is on the startup path and the tiled bootstrap format is deferred.
One active exporter publishes and one standby follower runs with publication off, resuming from the store watermark when it takes over. That is a Makechain simplicity choice, not an exoware constraint: uploads may come from several workers concurrently, and only the publication watermark has one logical owner. This closes A5's assembly choice while keeping an unmeasured interval out of the protocol.
Publication off does not mean queue off. Under R-17.22 the standby follows every finalized block and appends the same durable queue entry the active appends, holding the block, its certificate, both operation-log slices, and the projected rows. It uploads nothing; it releases an entry only when it observes the store and the mirror watermark past that height. Its queue therefore holds exactly the range the active exporter has not yet made durable in both copies, which is what makes it the third recovery source when an active exporter loses its disk with an undrained queue. That value exists only if the two fail separately, so R-17.15 requires the standby to be failure-isolated from the active, sharing no host, no disk, and no power or network failure domain.
6. Reset-bound fixed-committee profile
Accepted owner policy: disposable pre-mainnet networks may be reset explicitly to a new genesis
and chain_id; a reset makes no history or credit preservation promise.
Accepted by the owner, 2026-09-19: the first disposable devnet uses seven fixed validators in epoch zero with pre-generated threshold output/shares, threshold VRF, a constant signing-scheme provider, an effectively unbounded fixed epocher, and no continuous reshare actor. This follows the simpler fixed assembly demonstrated by Constantinople, while Commonware itself separates one-shot bootstrap from continuous reshare. The material is trusted-dealer custody, not trustless genesis. Removing reshare simplifies orchestration; it promises no intrinsic latency or throughput improvement. The constant scheme provider is not epoch authorization: engine, block-context, and proof checks must enforce epoch zero. See the source evidence (external site) for exact pinned mechanisms.
The specification binds FixedEpochZeroV1 or DynamicReshareV1 in the genesis chainspec
(specification section 5.4).
Canonical bytes remain to freeze. The fixed profile deterministically rejects validator-set
schedule commands and consensus-critical release scheduling in both admission and execution. It may
not accept them as no-ops, hide them behind a local feature flag, or claim all seventeen production
commands work. Epoch length and the group identity cannot be changed in place under the current
specification. The accepted development path moves from the fixed profile to DKG/reshare
qualification by an explicit reset/new chain_id, with no history or credit migration promise. It does
not require every pre-mainnet configuration change to reset.
Source basis, not qualification: Commonware glue/src/dkg/mod.rs:1-15,
bootstrap/mod.rs:1-10, and reshare/mod.rs:18-165 at the pinned tag separate one-shot bootstrap
from continuous reshare. Constantinople 42d0b3a demonstrates a fixed epoch-zero assembly with
pre-generated output/share; it does not establish production custody or Makechain conformance.
| Profile | Useful evidence | Cannot qualify |
|---|---|---|
| Fixed development | Codec/vectors, executor parity, three-member roots, crash/cancellation, receipts, native/wasm, admission contention, indicative latency | DKG bootstrap/reshare, membership scheduling, custody handoff/erasure, consensus-critical release handoff, late join, production recovery/topology |
| Fixed qualification testnet | Heavy-agent workload, exporter/store/proof service, window/retry behavior, service contracts at realistic history | Any gate depending on dynamic epochs or production failure domains |
| Dynamic fixed-committee staging | Continuous reshare across boundaries, carry-over, custody restart and sync while participants stay fixed | Validator set-change scheduling, joining/departing transport union, production membership governance |
| Dynamic qualification testnet | Complete production assembly, set change/carry-over, release activation, bootstrap/recovery and topology faults | Mainnet capacity until target environment/workload acceptance is met |
| Mainnet | Current dynamic specification after all gates and explicit readiness approval | No deferred surface is implied safe by earlier-profile results |
Every pre-mainnet reset gets a new genesis and chain identity. Stable-leader experiments are a separate consensus choice and gain no approval from this simplification; Random/rotating remains the production baseline until an explicit specification decision and qualification.
7. Workflow ownership
| Workflow | Required path and authority |
|---|---|
| Publication | Client stores closure separately → gateway admission/fan-out → executor orders PUSH → Commonware finalizes → database barrier makes progress durable → the exporter publishes the block, its certificate, and both slices to the store and the mirror → client verifies receipt; storage and chain outcomes stay separate |
| Authority change | Canonical intent → command authentication under parent/overlay state → executor changes counters/generations atomically → derived event; gateway dry-run is advisory |
| Proof serving | Membership and ancestry proofs come from the proof service over the store's operation logs, current-state and within-window membership proofs from the exporter's live databases, command proofs and receipts from the indexer's stored blocks → reference verifier authenticates from caller trust anchor; gateway only transports |
| Startup/recovery | Operator trust material → shared application SyncPlan verifies bulk target/tail → reconcile three targets and state_root → separate DKG sync/readiness gates → own live vote above journal ceiling |
| Export/query | capture extracts the slices and the finalized hook stages them in the durable queue → the drain takes each height from its queue entry → the block, the certificate, both slices and the projected history-table rows commit as one batch and advance the watermark → a query reads the watermark first, bounds itself to it, and discloses it |
8. Deferred work and validation gates
| Gate | Acceptance evidence | Blocked or deferred until it passes |
|---|---|---|
| G1 Canonical contract | Cross-release vectors; native/wasm decode, signing and proof parity | Public SDK/CLI and receipt compatibility |
| G2 Executor parity | Profile-applicable APP scenarios plus propose/verify/apply, rollback, cancellation, duplicate keys and deposit conservation; fixed profile runs APP-01–13, APP-15–19, APP-20's fixed-genesis cases, and APP-14's installer/state-sync portions, while its dynamic schedule case moves to G5a/G5b/G6 | Claiming executor conformance or allowing dependent product contracts to stabilize; integration tests themselves remain allowed |
| G3 Persistence/recovery | Crash at each member/barrier point by the crash-cut tests of validation 7.1, which stop the process or abort storage handles at the named barrier; glue::simulate reaches a barrier only through recorded seed sweeps over height windows or storage-fault injection (validation 7.1); root/target reconciliation; ordinary restart and deliberate rebootstrap |
Continuing testnet and production validator operation |
| G4 Fixed-profile conformance | Profile identity, APP-15–20 as applicable, and deterministic rejection of unavailable schedule transitions | Any claim that simplified networks implement their declared rules |
| G5a Continuous reshare | APP-20 dynamic-genesis payload projection, fixed-committee boundaries, DkgFailed carry-over, custody restart/handoff/erasure and DKG sync |
Set-change qualification and mainnet |
| G5b Dynamic membership | Schedule/lock, add/remove, transport union, late join, departure and carry-over under a changed set | Dynamic qualification testnet completion and mainnet |
| G6 Release handoff | Rules/installers and every consensus-critical surface across the handoff at the boundary into the first block of the activating epoch, with restart and catch-up | Continuing chain activation of a new ruleset |
| G7 Proof/export | API-01–20, checker root agreement at every ingested block, authenticated gap rebuild from either store copy, duplicate occurrences, receipt substitution failures | Public proof claims, store independence, query freshness claims |
| G8 Workload/topology | Heavy-agent expected/burst/stress mix; latency/backpressure/retry distributions; selected cost/latency placement failures | Numeric capacity/SLO claims and mainnet topology approval |
Deferred product SDK, CLI, explorer, website, and broad service work may begin only when the contract it consumes has the corresponding gate evidence. Fixed-profile evidence remains useful but is never credited to a blocked dynamic-profile gate.
9. Architecture decision disposition
| ID | Disposition | Remaining gate, if any |
|---|---|---|
| A1 | Accepted: fixed seven, epoch zero, real pre-generated test shares, explicit reset to the dynamic profile | P3 freezes bytes and G4 supplies vectors; G5a/G5b qualify real DKG/reshare before mainnet |
| A2 | Resolved for P2: canonical Rust schema/release types are the source; the executor consumes those owned types; the Stateful adapter alone maps them to pinned Commonware types. Transport schemas and TypeScript declarations are generated later from versioned schema inputs and are never authoritative. Native and wasm use the same Rust verifier. Client workflow owns journals and reconciliation, not proof interpretation | P3 freezes exact layouts/discriminants and vector manifests; P5 chooses physical crate/package paths. Coding may begin behind the semantic interfaces before vectors exist, but no conformance claim may |
| A3 | Resolved: admission receives a finalized immutable AdmissionSnapshot; proposal receives branch-scoped Commonware batches. Dry-run uses separate permits and is off by default; proposal attempt budget is never shared |
Contention/cancellation qualification is part of G2/G8, not an interface decision |
| A4 | Resolved: non-blocking lossy hint channel, single-reader Commonware durable staging queue as the only slice source, watermark-sequenced drain with a mirror height-index scan, create-if-absent artifacts, and watermark-after-verification | Queue sizes/retry timings are deployment calibration; C5-C8 and G7 qualify crash/redelivery behavior |
| A5 | Resolved: a non-voting Stateful exporter with validator-shaped retention publishes each finalized block, certificate, and operation-log slice from its finalized hook through a durable queue into two store copies, the exoware store and the mirror bucket; the exoware operation-log service plus the Makechain checker is the proof service, the exporter serves current-state proofs from its live head, and validators bootstrap by state sync from validators |
Store growth, ingestion lag, and recovery from either copy are measured at the platform-owned G8 recovery target; historical current-state proofs wait for a later release |
| A6 | Deferred: low/medium/high process sizing and scaling | Platform lead owns workload/topology/cost research at G8; no architecture split by machine size |
| A7 | Deferred: stable terms or coding marshal | Explicit later release decision and workload/failure qualification; neither is part of early simplification |
P2 now has an owner and implementable semantic interface for every responsibility and process. Review does not change protocol validity, authorize deployment, or mark any qualification result as measured. P3 byte freeze and runtime qualification are downstream gates, not reasons to leave module ownership undecided.
10. Commonware primitive map and storage decisions
This section holds material formerly stated in the specification and is not normative: the specification and its companions remain authoritative for every rule. It supports specification chapter 2 (system overview), chapter 6 (encoding and digests), chapter 9 (state and record lifecycle), chapter 11 (blocks and headers), and chapter 17 (node roles and the database set).
Section 4 of this document already carries an equivalent table at a different granularity: it maps each Commonware seam to what Makechain supplies, what Commonware owns, and the required check. The table in section 10.1 maps each component to the primitive it uses and that primitive's stability. The two are read together; neither replaces the other. The fixed production baseline paragraph in section 4 states the same assembly in prose.
10.1 Primitive map
Every component uses the Commonware primitive that fits, ALPHA included. Where no primitive exists,
the component is named in section 10.4 so that nothing is assumed. Stability is recorded at
v2026.9.0. Bumping an ALPHA dependency is presumed to need a release, classified by the
specification's storage-and-dependency-change rules, and re-runs the ALPHA parity set in section 11
of validation.md.
| Component | Primitive | Stability |
|---|---|---|
| Runtime | runtime::tokio in production, runtime::deterministic in tests |
BETA / ALPHA |
| Mesh | p2p::authenticated::lookup with one all-primary tracked peer set |
BETA |
| Consensus | consensus::simplex with elector::Random V1 and rotating terms; profile-selected fixed or orchestrated assembly |
BETA |
| Ordering and backfill | consensus::marshal::standard::Deferred, broadcast::buffered, resolver::p2p |
BETA (coding marshal ALPHA, unused) |
| Execution host | glue::stateful::Application with a three-tuple DatabaseSet, SyncPlan, db::p2p sync source |
ALPHA |
| Validity state | storage::qmdb::current::unordered::variable through glue's variable-value adapter, merkleized with Makechain's local Keccak256 implementation of the pinned commonware_cryptography::Hasher (follow-up F13) |
ALPHA |
| Membership log | storage::qmdb::keyless::fixed full variant, merkleized with Makechain's local Keccak256 implementation of the pinned commonware_cryptography::Hasher (follow-up F13) |
ALPHA |
| Block-history accumulator | storage::qmdb::keyless, compact on validators and the exporter, reconstructed in full by the proof service, merkleized with Makechain's local Keccak256 implementation of the pinned commonware_cryptography::Hasher (follow-up F13) |
ALPHA |
| Command root | storage::bmt with Makechain's local Keccak256 implementation of the pinned commonware_cryptography::Hasher (follow-up F13) |
ALPHA |
| Threshold identity | FixedEpochZeroV1: trusted feldman_desmedt::deal fixture, threshold-VRF signer validation, FixedEpocher, and ConstantProvider with independent epoch guards; DynamicReshareV1: glue::dkg bootstrap, reshare::Application wrapper, probe, DKG state-sync plan with fence and gate; both use AttributableReporter and cryptography::bls12381 |
ALPHA / BETA |
| Certificates and blocks | marshal::store::Certificates over storage::archive::prunable (validators and the exporter) |
BETA |
| Watermarks | storage::metadata |
BETA |
| Parallelism | one parallel::Rayon strategy injected everywhere |
ungated |
| Signatures | cryptography::ed25519 and bls12381 for consensus, DKG, and governance; account families use external standard verifiers over the same curve crates |
BETA |
| Encoding and vectors | commonware-codec, commonware-conformance |
BETA-equivalent |
| Assurance | glue::simulate, invariants::minifuzz, per-crate fuzz targets |
ALPHA |
| Browser verifier | the Rust verifier compiled to wasm32, as Commonware's own CI builds consensus, storage, and cryptography |
build target |
Two notes on the hasher rows:
- The proof-path members and the command root use Makechain's local
Keccak256implementation of the pinnedcommonware_cryptography::Hasher(follow-up F13). The proof path is everything a verifier hashes between a finalization certificate and a proven fact:block_hash, the merkleization of the three authenticated databases, the membership leaf, the block-history leaf, andcommand_root. The two leaves a contract must bind to an outside fact are Keccak256 over the fact itself: the command-tree leaf isKeccak256(canonical SignedCommand bytes)and the membership leaf isKeccak256(project_id || scheme_id || native_id), so neither binding needs a BLAKE3 step on the target chain. BLAKE3 remains everywhere else, including content identity,command_id,CommitmentId, state keys, and every domain-separated preimage. - The Keccak256 hasher is Makechain's own implementation of the pinned
commonware_cryptography::Hashertrait, follow-up F13, cross-checked against the library's post-tag implementation once that ships. Neither a Commonware pin move nor a git submodule is used: a main-branch pin would split the dependency graph from exoware2026.9.0, which pins thecommonware-*crates at2026.9.0. The Solidity verifiers for threshold certificates, QMDB, MMR, and BMT proofs are consumed separately from the pinned tag and do not require the Rust pin to move. Merkleization cost under Keccak256 at the target throughput is a calibration item, and the choice is final only when that measurement passes. The re-measurement is section 11.7 of validation.md. - Code structure keeps the choice reversible: each proof-path tree names its hasher through one
alias in
makechain-protocol(block hash, command root, membership log, block-history accumulator, validity state), every proof-path site imports the alias, conformance vectors are generated from it, and a lint forbids naming a concrete hasher on the proof path. Moving one tree or the whole path to BLAKE3 is then a type change plus regenerated vectors, decided atSB-15's decision point in implementation-second-batch.md (external site).
The block-history accumulator has its own row above so that all three merkleized members appear together; the specification's own primitive map states its primitive in the database-set subsection rather than in the map table.
10.2 Two scoped constraints
Two rules from the specification's design principles are scoped here so they are not misread.
Hash maps on consensus paths. The rule governs Makechain-owned code, where iteration order could reach an encoding or a root. Commonware's internal unordered collections never determine consensus order: where one is iterated, its indices are sorted and deduplicated before any ordered work, and the resulting roots are pinned by conformance vectors. Those internals are therefore compliant.
Truncated identifiers. The rule governs stored, routed, and hashed values. The index translator shortens keys only to choose a bucket, and every read compares the full key before returning. Translation is not truncation, and the per-key memory figures stand.
10.3 External projects
Two projects are depended on that are neither Commonware nor Makechain-owned.
| Project | Supplies | Pinning |
|---|---|---|
| mkit (alpha) | object identity, attestation format, transport protocol, wasm verifier | pinned by version in the chainspec format identifiers; bumped only by release |
| Tempo | settlement source; the configurable-account derivation and the configuration bounds of the TIP-1107 cluster, at a pinned specification commit | pinned by version in the chainspec format identifiers; bumped only by release |
Dependency and pin policy is owned by repository.md.
10.4 Makechain-owned, with no Commonware primitive
- secp256k1 signing and recovery, through an external crate.
- SSHSIG, WebAuthn, and P256-direct verification, through external standard verifiers, because Commonware's public verifiers always prepend a namespace.
- EIP-712 framing.
- The mempool, admission, and gateway fan-out.
- Write forwarding.
- Object-store publication.
- The tiled bootstrap artifact format, which is deferred.
- The deterministic projection of history-table rows, which exoware stores and queries but does not derive.
- Ethereum MPT proof verification and Tempo certificate decoding.
- The Ed25519 governance multisig.
- P256 batch verification, if it is ever wanted.
- The multi-consumer event outbox.
The owning modules for these are already named in section 2; this list is the negative view, stating which of them cannot be delegated to a primitive.
10.5 Three wiring choices
| Choice | Taken | Reason |
|---|---|---|
| Mesh mode | lookup, not discovery |
Every peer address is already chain state, genesis data, or chainspec data, so address gossip has nothing to solve |
| Exporter operator API | gRPC, not stream::encrypted |
The API needs service framing, reflection, and browser clients |
storage::queue |
Single-reader durable queue behind the exporter's publication drain and the event outbox | Per-consumer cursors are kept by consumers against the re-derivable stored log |
The queue choice is already covered in section 3.3 and section 4, which assign the byte bound, the certified gap choice, artifact idempotence, and the exporter watermark to the Finalized export module rather than to Commonware. The mesh choice is expanded, with the channel plan and peer-set ownership, in section 8 of operations.md.
10.6 Storage decision table
Each store was chosen for one job. This table is the decision; the specification carries the rules. It is new to this document: section 2 assigns module ownership and section 4 assigns Commonware seams, but neither records why a store was chosen or what was rejected.
| Store | Holds | Backing | Why this one | Rejected |
|---|---|---|---|---|
| Validity state | every record execution reads or writes: accounts, keys, projects, slug claims, refs, roles, offers, usernames, credit frontiers, schedules | QMDB current::unordered::variable, header state_root plus state_target |
authenticated key-value store with a bitmap of active operations, so current-state inclusion and activeness are provable; unordered because no validity rule needs an exclusion proof and the ordered variant adds a next-key link per active key; variable values avoid padding every small record to the largest metadata or schedule record | ordered QMDB, whose cost has no consumer; a fourth database for query indexes, because derived views do not enter a root |
| Permanent membership log | one leaf per commitment published to a project, forever | QMDB keyless::fixed full variant, header membership_target |
append-only, keyless, fixed 32-byte leaves, constant RAM at any size; a location in the log is a stable witness a client can cite; one committed floor, so all roles share one root while nodes prune bytes below it and the store keeps them | keeping memberships in validity state, because the index grows without bound; merging with block history, because the lifetimes and proof shapes differ |
| Block-history accumulator | one leaf per block, linking parent height and hash | QMDB keyless, compact on every node role, reconstructed in full in the store, header history_target |
gives a logarithmic ancestry proof, so a client can prove finality of any height from one later certificate; the compact form keeps every node's cost constant and moves the full log off the nodes entirely | header-chain walks, which have no worst-case bound under Simplex's transitive finalization |
| Command root | one leaf per command of one block, in execution order, each the Keccak256 of that command's canonical signed envelope | stateless BMT over Keccak256, header command_root |
inclusion proof for "these bytes were finalized at this height" with no database | folding commands into a persistent log, which the membership log and the store already cover |
| Blocks and certificates | bodies, profile-permitted DKG payloads, finalization certificates | marshal store::Certificates over archive::prunable on validators and the exporter, bounded by retained_marshal_blocks |
Commonware's own ordering store; every node keeps a bounded window, and the exporter's durable queue holds a block until the store and the mirror have it | validators as long-term block servers, whose disk use is unbounded |
| Watermarks and crash recovery | per-member commit metadata, publication watermarks | storage::metadata, glue's per-member durability barriers, and SyncPlan target reconciliation |
recovery is owned by glue, not by an application journal | an application-level commit journal |
| Stored blocks and operation logs | every finalized block, its certificate, and that block's slices of both operation logs | two copies written from the exporter's queue: the exoware store (exoware-simplex, exoware-qmdb, exoware-sql behind exoware-server on a RocksDB backend), and a mirror bucket of raw per-block objects content-addressed by block_hash with a height index, a watermark, and a retention lock |
exoware persists the Merkle nodes as ranges arrive and serves historical roots and proofs at any size, so no Makechain prover exists; either copy rebuilds the other; anyone can mirror | a Makechain-written prover over a bucket alone, which would have to reproduce the keyless operation encoding byte for byte |
| History query tier | append-only history tables of blocks, commands, events, publications, memberships, commits, refs, roles, keys, usernames, credits and the validator schedule | exoware-sql in the same store, keyed tables with native secondary indexes under a versioned schema prefix, written by the exporter in each block's upload batch |
one SQL surface over rows that arrive atomically with the block they describe, so an answer bounded by the published watermark is consistent without snapshot isolation; off the validators entirely, and rebuildable from the mirror by a deterministic projection | a bespoke read RPC surface on gateways; an indexer with its own database and pipeline, which would need a second durable copy and its own recovery; a columnar dataset built for analytics at launch, which is deferred until a product needs it |
| Objects | the bytes a commitment names | mkit stores behind a project's availability layers, addressed by (scheme_id, native_id) |
out of consensus by design: the chain commits identities and the layers hold bytes under the stated obligations of availability-profile.md; a project may have several layers, announced off chain | storing objects on chain |
Two consequences follow.
- A validator's history retention is bounded, while its footprint grows with funded resident state. Ordinary account-created records are paid for at creation, deposited families carry a deposit that deletion returns, state creation is rate-limited per block, the membership log is windowed, block bodies are pruned, and the accumulator is compact.
- Every service outside the core is a projection. Gateways, indexers, and query engines can be wrong or malicious without affecting what the chain attests, which is why the specification defines proof kinds for the facts that matter. This restates design rule 5 in section 1 from the storage side.
The selected object-store backend for the first shared devnet, and its create-if-absent adapter contract, remain in section 3.2 of operations.md; the table above states the store class, not the vendor.
10.7 Execution-host adapter contracts
Three contracts sit between the executor and the pinned Commonware adapters. They are implementation guidance in form, and the first two are root-affecting or liveness-affecting in effect, so they are recorded rather than left to reading the adapter source.
Staged current-state reads. CurrentStaged::expand neither deduplicates earlier keys nor
observes values computed for staged slots but not yet passed to merkleize. The Makechain overlay
therefore owns both deduplication and command-visible writes, and expands staging only for
previously unread keys. Its stable staged indices and final writes are submitted together at block
merkleization. Violating either half changes which operations reach merkleize, and therefore the
committed state_root. Sections 2 and 3 already assign typed reads and staged writes for validity
state to the state adapter, and section 4 names glue/src/stateful/db/current.rs; the expansion
rule itself is new to this document.
Lock discipline. No code holds a shared read guard across a batch operation that reacquires the same database, because the write-preferring lock deadlocks when finalization intervenes. New to this document.
Bootstrap handles. Bootstrap handles are dropped before the execution host opens the members. Already covered in section 3.1, whose genesis database handoff applies the batch, awaits the durability handle, drops the handle, and reopens the member before Stateful or a network actor starts.
10.8 Fixed-profile stack composition
Section 6 already covers the accepted fixed-profile decision, its epoch-zero guards, and the absence
of a continuous reshare actor. The component inventory below completes it by naming what
FixedEpochZeroV1 does not assemble.
FixedEpochZeroV1 retains Random V1 with rotating terms, marshal::standard::Deferred,
stateful::Stateful, the same three-member database set, executor, journal, durability barriers,
proofs, the exporter, and recovery paths. It has none of:
- a bootstrap DKG chain;
- an epoch orchestrator;
- a reshare wrapper or actor;
- a DKG probe or DKG state-sync plan;
- a dynamic registrar;
- a dealer or reveal channel;
- future-player activation;
- a DKG
SecretStore.
The DKG state-sync plan, the fence, and the gate exist only under DynamicReshareV1, where they are
separate from the application SyncPlan so that a state-synced node keeps certified epoch
information the application sync may skip. Already covered in section 4 (the reshare and DKG seam)
and section 5 (the DKG custody partition, via operations.md section 2.1). The
absence list is new to this document.