State and proofs
Application state lives in an authenticated trie. Every finalized block produces one root that commits to all of it, identical on every member. A client can then verify an individual record against that root without trusting the node that served it.
The state root
Section titled “The state root”State is stored in an MPF (Merkle Patricia Forestry, Aiken-compatible) trie. After applying a block, the resulting root is:
- identical on every honest member — that is what members sign;
- anchorable — the same bytes go into a Cardano metadata or script anchor; and
- provable — it supports both inclusion and exclusion proofs.
Effect records are committed transitively through a count-bound effectsRoot
leaf, so an authorized-but-not-yet-executed external action is as provable as
ordinary state.
Proof subjects: application language, not trie internals
Section titled “Proof subjects: application language, not trie internals”Raw key/value proofs exist, but they force the caller to know the physical layout. A proof subject is a typed descriptor that resolves an application-level identity to its canonical state key and evaluates declared claims against the proof-carried value.
curl -s -X POST \ "http://127.0.0.1:7070/api/v1/app-chain/chains/orders-chain/proof-subjects/finalized-message-v1/proof" \ -H 'Content-Type: application/json' \ -d '{ "coordinates": {"message-id": "<id>"}, "view": "latest", "claim": {"claimId": "recorded", "operands": {}}, "includeEvidence": false }' | jqYou supply coordinates in your own vocabulary (message-id, an account, a
registry key, a document id). The subject derives the canonical key,
decodes only the value the verified proof carries, and evaluates only the
claims it declares.
Stock v1 subjects cover finalized block messages, finalized message records, balances, registry entries, document heads, basic approval outcomes, authenticated-map entries, actor roles, role approval outcomes, and composite profile markers. Composite subjects are automatically rebound to their component namespace and get component-qualified ids.
Read the result precisely
Section titled “Read the result precisely”This is the part people get wrong. Each of these statements means something different, and a proof response keeps them separate:
| Result | What it actually asserts |
|---|---|
| Message inclusion | The message id is a leaf under one app block’s messagesRoot. |
| Finality certificate | A caller-pinned membership threshold signed the block. |
| Authenticated block record | Finalized state contains [height, messagesRoot, messageCount]. |
| State recording | The application wrote a typed fact under its canonical state key. |
| Anchor binding | A trusted Cardano output commits the selected application identity or root. |
| Claim satisfied | The proof-carried canonical value satisfies the selected bounded predicate. |
| Locally retained | This node has the bytes now. Durable availability is separately NOT_PROVEN. |
Absence is harder than presence
Section titled “Absence is harder than presence”An exclusion proof shows a key is absent from the trie. It does not show that the underlying business fact never happened — the dataset might simply be incomplete.
Do not expose a business-level absence predicate unless a marker, a paired subject, or an authenticated snapshot descriptor proves dataset completeness.
Retention and pruning
Section titled “Retention and pruning”Proof history is retained, not infinite. Monitor oldestProvableHeight:
./yano.sh appchain state identity --url http://node:8080/api/v1 --chain registry./yano.sh appchain state oldest --url http://node:8080/api/v1 --chain registryA pruned proof is unavailable. It is not evidence that the fact was absent.
New chains enable the state-index:finalized-block-messages-v1 subject by
default. Its enabled flag and configuration digest are part of the application
identity, so an existing database with a different configuration fails startup
rather than silently drifting. Disable it only in a new genesis profile.
Rough raw growth for that index, before MPF/JMT node and RocksDB amplification — one 32-byte key plus a ~38–47 byte canonical CBOR value per block:
| Block interval | Records/day | Raw logical growth/day |
|---|---|---|
| 1 second | 86,400 | 5.8–6.5 MiB |
| 5 seconds | 17,280 | 1.15–1.30 MiB |
| 20 seconds | 4,320 | 0.29–0.33 MiB |
Capacity planning must measure backend amplification, compaction, snapshots, and retained proof history on the intended workload.
Verifying independently
Section titled “Verifying independently”Proofs are portable. The Java client SDK (yano-x-client) verifies them
client-side, and the composite client (yano-x-composite-client) additionally
verifies governed-profile finality, one-root MPF, epoch chains, and
authorization policy.
Offline verification from the CLI:
./yano.sh appchain state entry --url http://node:8080/api/v1 \ --chain registry --key 0123./yano.sh appchain state proof --url http://node:8080/api/v1 \ --chain registry --key 0123For custom subjects, implement ProofSubjectProvider next to the module that
owns the canonical key and value codec. Each descriptor is closed data —
coordinates, fact fields, claims, completeness, verification targets, retention
hints, and fixed bounds — and its descriptorDigest goes into the capability
manifest. The runtime activates the provider only when subject id, version,
component id, and digest all match. Resolution must be deterministic and
side-effect free.
Deeper reading
Section titled “Deeper reading”- Proof Lab — message, typed-state, imported, and on-chain proof workflows, plus the independent-verifier and Cardano-validator guides.
- Tutorial 2 — retrieve and read a proof.
- Tutorial 7 — bind a root to L1.
- Authenticated snapshots — archiving and proving large immutable period datasets.