Client API and publication-receipt companion

Draft protocolSource at 169cbd2 ↗Source SHA-256 f1d4389…8e0
Status
Draft; not normative. The normative set is spec/; this document describes conforming client behaviour under it
Specification
Revision 14, 2026-09-22
Library baseline
Commonware v2026.9.0; exoware 2026.9.0

This companion refines SPECIFICATION.md, sections 5, 8, 12, and 14–18. The specification takes precedence. Method names below define semantic SDK/service operations; HTTP/protobuf bindings, integer representations, enum discriminants, and proof byte encodings are separate format artifacts. Transport JSON is never signed or hashed. The state-machine companion defines execution and required qualification scenarios.

1. Boundaries and identities

Makechain publishes and orders content-addressed commitments, with versioned code as the first workload. For that workload mkit verifies the primary scheme's commit identities and commit-to-file relationships. Native git schemes use their registered verifiers. Makechain does not fetch objects during execution, authenticate content authors from object metadata, or add a separate attestation command. A signed-message service receipt is a service assertion; an mkit sidecar attestation gains no chain inclusion merely by naming a published commit.

Keep these identifiers distinct:

Identifier Meaning and persistence
intent_digest Hash of canonical signed intent; stable across unsigned witness replacement and alternative valid signature envelopes
command_id Hash of complete canonical envelope; changes when witnesses or signature containers change
(key_id, authorization_generation, sequence) Replay slot and position; multiple conflicting intents can compete for a position, but cannot both execute
(scheme_id, native_id) Content identity; not a publication identifier or ownership claim
(chain_id, block_hash, command_index, commitment_index) One publication occurrence; duplicates have distinct occurrences
EventId Derived event identity for delivery deduplication; not a proof of the event's contents

For root commands the replay slot is the permanent account root slot and its root sequence, as in specification section 7.2; approval subsets do not create separate replay streams. Never key a retry journal only by command_id, and never deduplicate chain publications only by content identity.

Services expose the finalized checkpoint they used: chain id, height, block hash, timestamp, active release, and the relevant proof evidence. The existing common RPC metadata/capability response also exposes consensus_profile, chain_id, genesis_hash, the fixed threshold-output digest and ordered committee identity when applicable, and the active complete Tempo profile identity, including its source chain, contract and code identity. These are discoverability and compatibility claims. A client accepts the static profile, chain, genesis, output, and committee fields only after matching caller-provided GenesisChainSpec and genesis trust. It accepts the current Tempo profile, which may have changed by release, only after authenticating the release lineage and current SettlementAnchor state at the reported checkpoint. An RPC response cannot select either trust context. A height without an authenticated header is not a trust anchor. Clients supply their own trust anchor and maintain an independent freshness policy.

The two semantic profile variants are FixedEpochZeroV1 and DynamicReshareV1. Their canonical field layout and discriminants remain format-contract work. A client must reject an unknown profile and must not reinterpret it as either known variant. FixedEpochZeroV1 means exactly seven validators and epoch zero for the life of that disposable chain. DynamicReshareV1 retains the production bootstrap-DKG, finite-epoch, continuing-reshare, validator-schedule, and release behavior.

2. Minimum semantic service surface

Operation Request Response / owner
GetReleaseSchedule Trusted checkpoint or bounded page continuation Existing specification section 18.3 schedule evidence, section 18.8 supported range, and common profile/chain/genesis/Tempo capability metadata; gateway/exporter
GetCurrentState Bounded explicit record identities, requested checkpoint Inclusion/activeness proofs with parent authority dependencies, or explicit service outcomes; gateway/exporter, for current state and within-window membership only, because a historical current-state proof is not offered
PrepareCommand Intended action, actor/signing slot, bounded parameters, optional checkpoint Canonical unsigned intent draft and preparation context below; SDK with facade/proof-service assistance
SubmitCommand Exact canonical signed envelope Admission result plus intent/envelope identifiers; gateway fan-out
GetIntentStatus Chain, signing slot/generation, sequence, intent_digest Advisory status, observed checkpoint, evidence when available, stated retention/coverage; the gateway, or the indexer over the store's history tables
GetCommandProof Block hash and command index Exact included envelope, authenticated header, BMT proof and finality/release evidence; indexer via gateway
GetPublicationReceipt Publication occurrence tuple Receipt in section 5, or explicit service failure; indexer via gateway
GetFinalizedBlock Block hash/height within a bounded reconciliation range Complete canonical body and authenticated header/finality evidence for checking command-root completeness; the indexer, from stored blocks
SubscribeFinalized Height/event cursor At-least-once events with checkpoint/watermark; the indexer over the store's history tables; resume and deduplicate by EventId

GetCurrentState does not create general exclusion proofs for unordered QMDB. Missing records are reported as service observations unless a specifically supported cryptographic absence construction exists. Preparing a creation can therefore be optimistic; its signed absence/counter guard is ultimately enforced by execution. An indexer's empty response never proves a ref, key, or name free.

A current-state proof is offered at the head and never retroactively. "Ref R of project P pointed at commitment C at height H" is not a proof kind of this specification, per R-15.6: a command-inclusion proof shows the write at some height h but cannot show the absence of a later write before H, which is the completeness claim section 15.8 excludes. A client that may later need to evidence a decision it made from current state therefore archives the current-state proof it read while that height was the head, together with its authenticated header. Such a proof stays verifiable against that certified header forever. Release-tag audits, consumption records, and any "what did this point at when we used it" question are the cases this applies to; the query tier answers them as a service claim, not as evidence. A later release may add retroactive proofs by batch replay of stored blocks, which is why the archived proof, not the query answer, is the durable artifact today.

Query discovery, lists, analytics, and search remain in the facade/query tier. A proof endpoint takes explicit bounded identities or positions, not an arbitrary SQL query. Receipt lookup by occurrence is bounded; discovering all occurrences is query-tier work. Validators acquire no new public read surface. Services enforce ingress/egress byte limits, item/proof-depth bounds, work limits, and pagination; partial pages disclose coverage and never imply completeness of the whole history. Every query answer reports the watermark height it is bounded by, and pages by keyset from the last key returned rather than by offset.

Capabilities report VALIDATOR_SET_SCHEDULE.Append and .Cancel unavailable under FixedEpochZeroV1, and report consensus-critical release scheduling unavailable across activation, deferral, cancellation, and emergency paths. They continue to report ordinary non-critical release support according to the active rules and binary range. This metadata is advisory: preparation must apply the trusted profile rules locally and submission/execution must still reject a disabled variant. An older service that cannot express the trusted profile is incompatible, not evidence that the dynamic behavior is available.

3. Prepare, sign, and submit

A prepared command is an advisory plan, never a reservation of state or a promise of validity. The preparation result contains:

  • Canonical unsigned CommandIntent bytes and recomputed intent_digest; the SDK decodes them before displaying or signing, rather than trusting a server-provided summary.
  • Authenticated preparation checkpoint and release/price schedule; every fetched proof is tied to that checkpoint. A dry-run also identifies the hypothetical execution timestamp and Rules used; it cannot predict the eventual parent, timestamp, or effects of intervening commands.
  • Signing slot/generation and observed replay mark; client-selected next sequence, coordinated by the SDK's durable per-slot journal so independent callers do not accidentally allocate the same sequence. Separate keys are preferred for independent automation writers.
  • Project control/grant generation, applicable key scope, expected record revisions/incarnations, and any absence parent-counter guards. The client verifies positive state evidence and exposes service-only absence observations honestly.
  • Itemized expected non-refundable fees, deposit locks and stored-payer discharges; the selected gross max_charge, canonical byte/work/new-record reservations, and assumptions behind the estimate. Refunds never lower max_charge or gross creation reservations. No estimate guarantees a price or available balance at execution.
  • Unsigned membership witnesses associated with non-delete ref operations in order. The SDK checks their target binding and anchor eligibility at preparation; a relayer may later refresh them within the signed max_charge and active per-command limits without altering the intent.
  • Optional valid_before_timestamp_ms and known pending release boundaries. The deadline is exclusive and uses consensus time. Neither it nor a release warning promises inclusion before that boundary.

A signing SDK MUST verify intent fields against the caller's requested action and constraints, including actor, recipient, project, names, targets, scope, authority changes, and maximum debit. It recomputes canonical bytes, digest, and family-specific signing presentation locally. Ref names are the exact signed UTF-8 bytes; escaping for display never changes them. A gateway cannot provide one readable EIP-712 operation while another is signed. Proof failure or unavailable required authority evidence stops signing unless the application explicitly operates under a declared service trust policy allowed by specification section 15.8; it never silently downgrades verification.

Before producing canonical bytes or requesting a signature, PrepareCommand applies the profile from trusted genesis. Under FixedEpochZeroV1 it refuses both VALIDATOR_SET_SCHEDULE variants and any release action that would schedule, defer, cancel, or emergency-activate a consensus-critical entry. It does not rewrite one into a successful no-op or misreport the rejection as stale state, rate limiting, unsupported release, or missing authority. Submission returns the existing Invalid admission class with an explicit profile-disabled diagnostic for a well-formed command forbidden by the trusted profile; malformed, wrong-chain, not-ready, and unsupported-release inputs retain their existing outcomes. This introduces no new command, admission, or wire discriminant.

There are no genesis credits. On devnets and qualification testnets, preparation for the first funded account uses the complete trusted Tempo Moderato profile and builds IMPORT_CREDITS.Credit from an authentic certified header and beneficiary-cumulative storage proof. Mainnet uses the trusted Tempo Mainnet profile. The client verifies the source chain, threshold/certificate profile and installed source epoch floor, contract and code identity, accepted asset, beneficiary-slot derivation, cumulative width, checkpoint and time bounds before submission. It never targets the existing global-issuance contract, fabricates a proof, or offers a Makechain-side faucet, mint, or genesis-credit fallback. Accepted assets obtained from a Tempo Moderato faucet still require a normal deposit into the pinned contract and the resulting authentic proof. Only verified finalization of the first import establishes the ledger from which INIT_ACCOUNT can be prepared. The exact ABI and slot-zero uint256 mapping are defined in settlement interface. Deployment code hashes, addresses, and qualified evidence remain external inputs; until those inputs and a deployment are pinned, the client reports that credit import is unavailable for that incomplete settlement profile.

Before the first submission, persist the intent, signed envelope, preparation checkpoint, replay context, and operation/chunk intent in a local journal. This is client recovery state, not consensus state. Submit the envelope. Accepted requires f + 1 effective validators' acknowledgements under section 8.2 of operations.md but promises neither persistence nor finalization. Acknowledgements, rate limits, and pool replacement are local service information. A client can rebroadcast an identical envelope or an alternative envelope for the same signed intent, with valid refreshed evidence and the same sequence. It must recompute command_id for the new envelope and keep both associated with one intent. Changing a target, CAS, price, authority context, max_charge, or sequence requires a new signature.

A complete user push requires both a durable storage acknowledgement from an availability layer serving the project and verified chain finalization. Neither proves the other. For chunked publication, each intended chunk is tracked independently; finalization of the final ref move proves only that ref target's publication. The SDK reports full completion only after every required chunk and the ref operation are reconciled.

4. Reconciliation and retry

Admission results are those of section 8.2 of operations.md. Intent-status results are a separate surface and never become consensus predicates:

Status Meaning Safe automatic action
Pending A service currently retains a candidate for this intent Wait or rebroadcast same intent; no inclusion promise
Finalized Service locates an included envelope whose decoded intent matches Verify command proof; then record success and publication occurrences
Replaced One pool replaced it with a different intent at the same sequence Reconcile chain history; replacement does not establish a chain outcome
Superseded Service asserts non-inclusion and that finalized replay state prevents future execution Verify replay context and resolve historical inclusion; the mark alone is insufficient
StateConflict Advisory execution conflict at a stated checkpoint Resolve possible prior inclusion; then refresh state and re-evaluate the intended action before re-signing
Unknown Service has no reliable answer, has evicted status, lacks history coverage, or cannot resolve inclusion Preserve uncertainty; no automatic fresh-sequence retry

A command-inclusion proof of any valid envelope with the same intent_digest establishes success, including one whose witnesses differ from the submitted envelope. The client checks decoded intent bytes as well as identifiers. An event, matching current ref target, membership proof, or higher sequence does not establish that this particular intent executed.

An advanced sequence mark establishes only that an old sequence cannot execute again while that slot/generation applies; it does not distinguish "already executed" from "skipped." An expired or revoked key, moved authority context, or crossed release boundary likewise does not erase prior execution. A status absence cannot be turned into authenticated non-inclusion.

For automatic fresh-sequence retry, the SDK must first establish both:

  1. The old intent cannot execute in any future valid block (for example, verified replay state has overtaken its sequence, or the relevant generation has permanently lost authority).
  2. It did not already finalize. This can be determined by checking complete authenticated command bodies from the persisted preparation checkpoint, at which its sequence was still unused, through an authenticated cut that establishes condition 1. Check header ancestry, command count, and the command root for every body; inspect decoded intents, not just submitted command_ids. A root comparison against a complete command list provides completeness for that block; isolated row inclusion proofs do not. Bound each fetch and total reconciliation work and expose progress.

A declared trusted history service may supply the second conclusion under its explicit coverage and trust contract instead; the SDK must label this service trust, not cryptographic non-inclusion. If the necessary range is unavailable or exceeds the configured work budget, remain Unknown. There is no launch constant-size non-inclusion proof or new negative-lookup index. A user may explicitly choose to publish again despite uncertainty, accepting possible duplication and cost; the SDK must not call that an idempotent retry or silently choose it.

After safe reconciliation, refresh state/prices, rebuild CAS/authority context, confirm the action still expresses the caller's intent, and sign with a fresh sequence. Never automatically force a ref over a concurrent writer's new target. Root witness changes and unsigned witness replacements alone do not justify a fresh sequence. Local per-slot ordering is useful but cannot prevent a higher sequence from arriving at validators first.

5. Publication receipts

Semantic shape (not a wire-format declaration):

PublicationReceipt {
  header,
  finality_evidence,
  release_lineage,
  signed_command_bytes,
  command_index,
  command_inclusion_proof,
  commitment_index
}

All indices are zero-based, bounds-checked protocol integers. The format artifact pins their widths and canonical proof encoding. Limits cover the complete envelope and all finality/lineage material; services may share authenticated checkpoint material across a bounded response without changing the verification target. No trust key, chain identity, or format is selected from untrusted hints.

The reference verifier returns a typed verified-publication result only after:

  1. Authenticating the consensus_profile, header/finality and release lineage from caller-provided chain/genesis or release-checkpoint trust, using specification section 15. An unsupported profile or release is a service/capability outcome, not a fabricated absence or a partial success.
  2. Decoding the exact signed envelope under the authenticated format, with exact consumption and all structural bounds. Require its intent's chain and ruleset to match the authenticated context; recompute command_id from those canonical bytes.
  3. Verifying the Commonware BMT proof at command_index under header.command_root, with its leaf count equal to header.command_count and the index in range. The leaf is recomputed as Keccak256(canonical SignedCommand bytes), so a contract on an EVM chain can check inclusion of the exact command bytes with Keccak256 alone; the BLAKE3 command_id remains the envelope identity used for correlation and journalling, not the leaf. Do not accept a proof under a root supplied independently of the authenticated header.
  4. Requiring a PUSH with commitment_index in its commitments array. Derive the chain, height/hash, actor, key, generation, sequence, project, commitment, intent_digest, and ruleset from the header and command. Any duplicated summary fields must agree exactly or the response is rejected.

Output is the publication occurrence tuple and those derived fields. Execution was accepted under Makechain's BFT assumptions; this proof does not independently reconstruct historical role or key state. No additional membership proof, event root, or per-publication validity record is needed. A ref-only command returns ordinary command evidence, not an invented commitment occurrence.

Within one authenticated chain, order by (height, command_index, commitment_index). Two equal commitments at different indices remain two occurrences. A block timestamp is consensus time of publication, not evidence of the content's creation time. Do not compare cross-chain positions as one total order or infer "first ever" from a returned subset of history.

A reset creates a new trusted-genesis binding, including the consensus_profile, incarnation, chain_id, genesis block/hash and threshold output. Clients keep journals, checkpoints, receipts, and proofs partitioned by that full trust context. A pre-reset chain signature, certificate, receipt, inclusion/state proof, or bootstrap artifact cannot complete an operation on the new chain. A Tempo fact that remains externally valid may be submitted only in a new-chain canonical import envelope and must independently satisfy the new chain's pinned Tempo profile and normal strict-advance rules; it does not carry old ledger state across the reset. IMPORT_CREDITS remains permissionless; this re-envelope requirement adds no account signature.

The result proves publication of an identifier, not that the bytes are available, are a well-formed commit, name a truthful author, or substantiate a claim. mkit/native scheme verification handles content separately. Receipt responses may carry conveniences, but each keeps its evidence class:

Extra field Evidence needed beyond the receipt
Membership at a supplied log location Membership proof; an equal leaf alone does not connect that duplicate location to this occurrence
Current ref target or new revision Current-state proof plus context at a stated authenticated checkpoint; later writes may have superseded it. A receipt captures the target at publication time and never the target as of a later height, and no retroactive proof of the target at that later height exists at launch
Event payload, charge, or deposit result Bounded replay from certified state/commands, or explicitly service-trusted data
Commit metadata / file proof Scheme-specific object verification, supplied by mkit for the primary scheme
Author identity / review correctness Separately stated identity or application trust policy; never inferred from publication

A membership location is not included in the core receipt. If an API claims its exact correspondence to the occurrence, it must additionally authenticate the operation offset (parent log size and the counts of commitments in preceding commands), rather than relying on leaf equality.

6. Projection and event contract

Project each PUSH commitment entry into a publications row keyed by its occurrence order with the occurrence tuple as covered columns. Object metadata belongs to commits, keyed by full scheme/native id, and is never collapsed into a single publisher. A memberships row can reference an occurrence after deterministic block projection derives its actual log location. Signed-message assertions remain separate records keyed by message id and subject; conflicting assertions are preserved rather than overwriting object-derived metadata.

object_verified means decoded from identity-verified object bytes. It does not verify that an object's author field belongs to its signing key, or that either belongs to its on-chain publisher. publisher_asserted identifies a signed assertion and its source; it never upgrades it to a consensus fact. The native verifier's additional signature result, if exposed, is a separate field.

Events are re-derived from finalized execution and delivered at least once. Consumers persist their cursor with effects and deduplicate EventId. CommandFinalized is not the proof object in section 5. An indexer can report its event and status view immediately, but an untrusted client must obtain the corresponding evidence before reporting verified finalization. Proof materialization delay is a service outcome; it does not mean execution failed.

Project origin is a creator-asserted source-content reference. To show "source C was published in project P," fetch and verify that source publication's receipt. That still establishes neither creator endorsement of the new project nor content ancestry; native object verification handles the latter.

7. Qualification scenarios

These scenarios are requirements for the future executable SDK/verifier suite, not present test results. They extend the application scenarios in the state-machine companion.

ID Scenario Required outcome
API-01 Gateway substitutes displayed name, target, account, scope, or debit Locally rebuilt intent/signing presentation exposes mismatch; no signature
API-02 Ref names differ by case or Unicode spelling; controls/invalid UTF-8 supplied Exact valid bytes stay distinct; invalid names rejected, no silent normalization
API-03 Witness refresh changes command_id but not intent; alternative root approvals One intent tracked; proof of either valid included envelope completes it
API-04 Response lost after finalization; status later evicted Unknown until reconciled; no automatic duplicate publication under fresh sequence
API-05 Higher sequence finalized before lower chunk Higher mark alone proves no chunk completion; inspect complete bounded history before retry
API-06 Pool reports Replaced but original finalized elsewhere Original inclusion wins; pool report cannot override proof
API-07 Receipt substitutes actor, project, index, count, header, envelope, or release Verification fails; no copied summary field becomes authority
API-08 Same commitment published twice in one PUSH and by two accounts Distinct occurrence rows and receipts; no inferred singleton author/publisher
API-09 Direct certificate absent for publication height Accumulator-backed finality verifies under a certified descendant
API-10 Ref-only PUSH or index equal to commitments length No publication receipt; ordinary command proof or invalid request respectively
API-11 Receipt attaches fabricated charge/new ref revision Core receipt does not authenticate it; separate proof/replay or service label required
API-12 Incomplete history page or missing block during retry reconciliation Remain Unknown; never treat missing range as non-inclusion
API-13 Membership proof for another occurrence with identical leaf Membership succeeds but exact occurrence/location linkage is not claimed
API-14 Service returns stale authority proof, unauthenticated absence, or new release Apply freshness/trust policy, expose absence limitation, and refresh supported rules before signing
API-15 Objects stored but PUSH pending, or PUSH finalized but closure unavailable Separate storage and chain outcomes; do not report a complete user push
API-16 Separate mkit attestation names a published commit No automatic inclusion, timestamp, authorship, or correctness claim
API-17 RPC claims a static profile, chain/genesis, or fixed output/committee different from trusted genesis, or an active Tempo identity different from authenticated release/current state Client reports incompatible metadata and neither prepares nor verifies under the response's claim
API-18 Prepare either validator-schedule variant or any consensus-critical release path under FixedEpochZeroV1; prepare an ordinary non-critical release Disabled intent is refused before signing with a profile-specific explanation; ordinary release preparation retains its normal checks
API-19 Fresh fixed-profile chain has no ledger; client receives authentic Moderato cumulative proof, then a global-issuance, synthetic, wrong-contract, wrong-slot, or wrong-beneficiary substitute Authentic import is prepared and verified before INIT_ACCOUNT; substitutes fail under the production proof verifier; no fallback credits appear
API-20 Client switches to a reset chain and is given old signatures, receipts, proofs, status, and journal entries; a still-valid Tempo fact is separately available Pre-reset chain artifacts remain partitioned under the old trust context and fail new-chain verification; Tempo evidence requires a new-chain import envelope and full revalidation

API-07, API-08, and API-13 exercise INV-PROOF-08; API-04 through API-06 and API-12 exercise INV-AUTH-02 and INV-PROOF-07 without turning service status into a consensus predicate.

The receipt verifier uses Commonware storage::bmt and existing finality/lineage verification, compiled through the same Rust/wasm reference verifier. At v2026.9.0, BMT leaves are position-bound and the root includes the leaf count (storage/src/bmt/mod.rs); preserve both checks in vectors. The mkit boundary follows mkit's docs/specs/SPEC-ATTESTATIONS.md section 3.1 and its rust/crates/mkit-core/src/sign.rs::verify_commit: sidecar attestations are outside the object DAG, and a valid object signature does not establish the asserted author's identity.