Client API and publication-receipt companion
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:
- 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).
- 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:
- 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. - 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.
- 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 BLAKE3command_idremains 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. - 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.