# Yano documentation --- # Build with AI Give coding agents compact, accurate, versioned Yano context. Canonical URL: https://getyano.dev/ai/overview/ Give your coding assistant the same documentation you use. Yano publishes plain Markdown and structured inventories so an agent can find current APIs and configuration without scraping navigation. ## Choose an artifact | Artifact | Use | | --- | --- | | [llms.txt](/llms.txt) | Compact index and key boundaries | | [llms-full.txt](/llms-full.txt) | Full public documentation in one text file | | [Agent instructions](/ai/agent-instructions.md) | Ready-to-use Yano context for a coding agent | | [Documentation manifest](/ai/manifest.json) | Source revision, version, page URLs, checksums | | [Configuration JSON](/ai/configuration.json) | Packaged active values by source/profile, plus declared keys | | [Route inventory](/ai/routes.json) | REST annotation paths extracted from source | Each documentation page has a Markdown copy under `/ai/pages/`, with the same path and a `.md` extension. For example: [the quickstart as Markdown](/ai/pages/start/quickstart.md). ## A useful starting prompt ```text Use https://getyano.dev/llms.txt to discover the Yano documentation. Read the Java testkit guide and the source/version manifest. Help me write a JUnit test that starts an isolated devnet, funds a wallet, and checks the balance. Use org.yanoproject imports and matching artifact versions. State prerequisites and distinguish tested APIs from assumptions. ``` ## For API client generation Get `/q/openapi?format=json` from the actual node you will call. The static route inventory only records source paths; it does not encode full request schemas, permissions, or feature availability. ## Keep the trust boundaries Yano is pre-release. Do not describe it as a fully validating production replacement. Yano's built-in app state machine is `ordered-log`; additional stock extensions belong to Yano X. Submission acceptance, member finality, and Cardano confirmation are different events. AI-generated code should preserve these distinctions. These are static context artifacts. The documentation site does not offer a live node, remote signing, or an MCP server. --- # Consensus & chain configuration Member consensus, execution, commitments, and anchoring. Canonical URL: https://getyano.dev/app-chains/consensus/ Code pointers use class names — start at `runtime/.../appchain/AppChainEngine.java` (consensus core), `core-api/.../appchain/` (SPI + codecs), and `runtime/.../OrderedLog.java` (the one built-in state machine). --- ## 1. Architecture map ``` one Yano node ┌───────────────────────────────────────────────────────┐ │ Cardano L1 (sync / relay / block production) │ │ │ L1 events: blocks, slots, rollbacks │ │ ▼ │ │ AppChainManager ── one per node ────────────────────│ │ │ shared INBOUND transport (appmsg, protocol 100) │ │ │ shared catch-up server (protocol 103) │ │ │ per-chain dispatch by chain-id │ │ ▼ │ │ AppChainSubsystem ── one per chain ──────────────────│ │ ├─ message pool (admission, backpressure) │ │ ├─ AppChainEngine (consensus, single-threaded) │ │ ├─ AppLedgerStore (RocksDB: blocks, MPF, indexes) │ │ ├─ AppStateMachine (your application logic) │ │ ├─ MemberGroup (static or governed membership) │ │ └─ anchor services, observers, sinks (optional) │ └───────────────────────────────────────────────────────┘ ▲ outbound app-peer connections: per chain (transport.mode=shared rides the L1 upstream session — one TCP connection per peer pair — with dedicated dials as fallback) ``` - **One inbound front, many chains.** All app-message agents share protocol id 100, so a session cannot carry one agent per chain. The manager installs ONE gossip agent scoped to the union of hosted chain ids and dispatches every verified envelope to the owning chain. Outbound connections are per-chain (each chain has its own peer list). The union transport is permissive (max of all chains' size/TTL limits); each chain's own limits are re-applied per message — with one carve-out: bodies on reserved `~` topics may be as large as `block.max-bytes`, because a consensus proposal body IS a whole serialized block. - **Everything per-chain is independent**: ledger, state machine, member set, sequencer mode, block cadence, anchor policy. Chains share only the node's networking and its L1 view. - **The engine is a serial event loop.** All consensus entry points — the propose tick, inbound `~consensus/*` messages, catch-up batches — hop onto one single-threaded executor per chain. There is no locking inside the consensus logic; ordering is the concurrency model. ## 2. Message lifecycle ``` submit (REST / SDK / gossip) → envelope auth: member Ed25519 signature, message-id integrity → transport limits: size (chain max-message-bytes), TTL cap → state machine validate() ← application admission (reject = 400) → pool (backpressure: full pool = 429 + counted gossip drops) → gossip to app peers (dedup by message-id) → proposer selects into a block (drops: finalized dupes, stale sender-seqs, machine-rejected; ~system topics bypass validate) → consensus round (§4) ← the only place messages become canonical → finalized: indexed by id/topic/sender, applied to state, streamed to SSE/webhooks/Kafka, provable via MPF, eventually anchored to L1 ``` Two distinct rejection tiers exist by design (§8.2): *admission* keeps junk out of blocks (node-local, fast, can be re-tried), while *apply* is consensus-enforced and never rejects — a finalized message that violates a business rule is a **deterministic no-op** on every member. ## 3. Block anatomy `AppBlock` fields (CBOR, `AppBlockCodec`): | Field | Notes | |---|---| | `version` | block format version (3) | | `chainId` | chain identity | | `height` | genesis = 1 | | `consensusContextDigest` | commits genesis, membership, quorum, and consensus/observer profiles | | `view` | certified-consensus view at this height | | `prevHash` | 32 zero bytes at height 1 | | `l1Slot`, `l1BlockHash` | the stable L1 reference (§6), 0/empty when disabled | | `timestamp` | proposer wall-clock millis — the ONLY time a state machine may use | | `messagesRoot` | binary blake2b-256 merkle root over the ordered **message ids** (odd width duplicates the last node; empty list = 32 zero bytes) | | `stateRoot` | MPF root AFTER applying this block (§7) | | `messages` | the full ordered message envelopes | | `proposer` | Ed25519 public key | | `justification` | empty in view 0; canonical `NewViewCertificate` in higher views | | `cert` | finality certificate: `(scheme=ed25519, [(signer, signature)...])` | **The block hash covers the header only** — blake2b-256 over the v3 consensus identity, value fields, proposer, and justification digest. Messages are bound via `messagesRoot`, history via `prevHash`; only the cert is outside the hash, so it can be attached with `withCert`. ## 4. The consensus round Consensus messages are ordinary signed envelopes on `propose`, `prepare`, `prepared`, `commit`, `timeout`, `new-view`, and `cert` topics under `~consensus/`. Votes and certificates use canonical, bounded CBOR and domain-separated signatures. ### 4.1 Proposing Every member ticks every `block.interval-ms`; the framework computes the same leader from committed context on every node, so only that member proceeds: 1. If a round times out, broadcast a durable signed timeout for the next view. 2. In view 0, the configured policy selects the initial leader. In higher views, the framework selects one deterministic leader from the certified context. 3. Select the mandatory durable L1 prefix, then ordinary messages: cap by `block.max-bytes` (primary; the serialized block is trimmed to fit) and `block.max-messages` (backstop); drop already-finalized ids, stale per-sender seqs, and messages the state machine's `validate()` rejects. Reserved `~` topics bypass application admission — a state machine cannot veto governance or consensus traffic. 4. Build and **apply locally** to compute the real `stateRoot`, persist the `(height, view, blockHash)` prepare lock, broadcast the proposal and PREPARE. No pending messages → no block. An idle chain produces nothing. ### 4.2 Follower validation — every check, in order A member votes for a proposal only after ALL of: 1. decodes as a block; height not already finalized; 2. height is exactly `tip+1` (a block from the future is *deferred* and retried, not dropped — the transport never re-delivers); 3. `block.proposer == envelope sender` (proposer authenticity); 4. the framework accepts this leader for the block's height and view; 5. `prevHash` equals the local tip hash; 6. `messagesRoot` recomputes from the message list; 7. serialized size ≤ `block.max-bytes` (counted + rejected loudly); 8. the L1 ref is monotonic and consistent with the node's OWN L1 view (§6.3) — mismatch rejects, "proposer slightly ahead" defers; 9. every `~l1/*` observation message re-derives from the node's own L1 stream (fail-closed); 10. every message: valid message-id, not expired, member-signed (membership evaluated **at this height**), body ≤ `max-message-bytes`; 11. per-sender seqs strictly increasing above the finalized floor (when `message.enforce-sender-seq` is on); 12. no conflicting prepare lock in this view and no newer durable lock; 13. **independent re-execution**: the follower applies the block itself and requires byte-identical `stateRoot`. Then and only then: persist the prepare lock and broadcast a domain-separated PREPARE. Check 13 is the heart of the system — *state is never trusted, always recomputed*. A nondeterministic state machine stalls its chain right here. ### 4.3 Finality Any member holding the round aggregates PREPARE votes. At `threshold`, it persists and broadcasts a `PreparedQC`; members then sign COMMIT. At a COMMIT quorum, the holder assembles `FinalityCert`, commits, and broadcasts `cert`. Cert verification never trusts the sender: scheme must be Ed25519, each signer must be a member at that height, duplicates are ignored, every signature is verified over the commit-domain digest, and the count must reach the threshold at that height. A block is **APP_FINAL** once committed with a threshold cert — via own votes, a received cert notice, or catch-up (§8.1). The ledger is append-only; there is no rollback path below finality. ### 4.4 Timeouts and partial rounds A timeout never deletes a lock. A quorum of signed timeout records forms a `NewViewCertificate`. The next leader must carry the highest valid PreparedQC value byte-for-byte; without one it may build a fresh deterministic value. Timeouts back off exponentially within configured bounds. ## 5. Vote locks — the safety core Before PREPARE, a member persists `(height, view, blockHash)`. It never prepares two values in one view or moves a lock backwards. PreparedQCs and their complete values are also durable across restart and must cross every higher view. There is no stale-lock deletion endpoint. Expired proposals recover only via a quorum-certified higher view; L1-invalidated prepared values quarantine rather than unlock. ## 6. Sequencer modes and membership ### 6.1 Fixed `sequencer.proposer` selects the view-0 leader. If it cannot complete the round, a quorum-certified timeout advances the view and the next member in canonical member order becomes leader. No configuration change or lock deletion is needed. ### 6.2 Rotating The view-0 leader is derived from the committed consensus context and parent block hash. Higher views advance through the canonically sorted member list. This gives every member the same leader even when their newest L1 tips differ. The L1 reference remains proposal data and is verified independently (§6.3); it is not the leader clock. Both fixed and rotating policies require a quorum to advance a failed round. Choose a threshold that matches the stated fault model (§4), not merely the number of normally-online nodes. ### 6.3 The L1 reference (both modes) With `l1.stability-depth > 0`, every block carries an `(l1Slot, l1BlockHash)` reference at least that many blocks below the L1 tip. Followers verify it against their **own** L1 view: a fabricated ref is rejected fail-closed, a ref slightly ahead of the local view is deferred and retried, and slots must be monotonic across blocks. This pins app-chain history to L1 time — it is what makes observation stability (user guide §5.5) and anchor recency meaningful. ### 6.4 Proposer vs anchor leader (don't conflate them) Rotation applies to the **proposer** only. The **anchor leader** — the node with `anchor.enabled` that builds and pays for L1 anchor txs — is a separate, fixed role that does not rotate (one thread UTxO, one fee wallet), and it is not a trust point: script-mode advances require threshold member co-signatures verified against each member's own ledger (user guide §5.1). A rotating chain with anchoring therefore has a rotating proposer AND a fixed anchor leader at the same time. ### 6.5 Membership epochs Membership is either static config or **governed**: membership changes are themselves finalized chain transactions requiring threshold-many identical member commands. Internally the group keeps *member epochs* — every consensus check above says "member **at height h**", so blocks from before a rotation verify against the membership that was current then. Governance commands ride reserved `~governance/*` topics (which bypass state-machine admission) and their effects persist atomically with the block that finalizes them. ## 7. State, the MPF trie, and proofs Each chain's ledger is one RocksDB instance (`//`) holding column families for blocks, framework metadata, the message index, query indexes, and the **MPF trie nodes** (Merkle Patricia Forestry — the same construction as Aiken's `merkle-patricia-forestry`, so proofs are on-chain-verifiable). The split of responsibilities: - **The state machine and framework-authenticated keys share the trie.** Every `writer.put(key, value)` / `delete(key)` in `apply()` is an MPF entry — individually provable against the resulting root. The framework additionally writes reserved observation cursor keys before commit, so delivery progress is covered by the same root. - **The framework owns everything else**: tip metadata, per-message and topic/sender indexes, per-sender replay floors, vote locks, governance epochs — all in RocksDB CFs *outside* the state commitment. - **One atomic batch per block.** `apply()` stages trie writes into a RocksDB WriteBatch; `commitBlock` writes block + tip + indexes + trie nodes + new root in a single atomic commit. A crash mid-block leaves the previous height fully intact. `stateRoot` is therefore a pure, reproducible commitment to the state machine's data — recomputed and byte-compared by every member on every block (§4.2 check 13), committed on-chain by anchoring (user guide §5), and queryable per key: `GET .../state/proof/{keyHex}` returns the value and an MPF inclusion proof any independent implementation can verify against an anchored root. ## 8. Catch-up, restart, snapshots ### 8.1 Catch-up (sync protocol 103) Gossip never re-delivers: a missed proposal is gone from the transport. A lagging member recovers through the dedicated block-range sync protocol — every 5s it asks one connected peer for `tip+1 .. tip+50` and applies what comes back through the same verification gauntlet as live consensus, minus what no longer applies: - height/prev-hash chain intact, messages-root recomputed; - proposer must have been the deterministic built-in leader at that height and view (custom extension modes retain their own view-0 validation); - L1 ref monotonic + consistent with the local L1 window (a ref ahead of the local view pauses the batch, it does not fail it); - observations re-verified fail-closed; message signatures re-verified (expiry is NOT re-checked — the messages were finalized before expiry); - **the threshold cert is fully verified**, and the block is re-applied with a required byte-identical state root. A certified block at a height with a local in-flight round supersedes the round. If a peer is ahead but local progress stalls, an `AppChainStalledEvent` fires (visible in status as `stalled`). ### 8.2 Restart semantics Persisted (RocksDB, all atomic with their block): blocks + certs, tip metadata, MPF trie + root, message/query indexes, per-sender floors, per-view prepare locks, prepared certificates + canonical proposal envelopes, membership epochs and pending governance. In-memory (lost on restart, by design): the pending pool, active round aggregation (reconstructed from persisted evidence), gossip dedup/counters, pending anchor rounds. Startup re-verifies rather than trusts: 1. snapshot manifest signatures/hashes (if restoring) — before RocksDB touches anything; 2. ledger integrity: committed root equals the tip block's recorded root; 3. persisted membership epochs override static config; 4. **tip-cert verification**: the tip block re-hashes to the stored tip hash AND its cert carries ≥ threshold valid member-at-height signatures — a snapshot whose contents merely self-agree is rejected; 5. own sender-seq floor restored so a restarted member never reuses a finalized sequence number. ### 8.3 Snapshots `GET .../snapshot` produces a RocksDB checkpoint (hard links — cheap) with a member-signed manifest binding tip height/hash, state root and member epochs. New-member onboarding = copy checkpoint, verify manifest, start, catch up the delta (user guide §14.3). ## 9. State machines — the SPI `org.yanoproject.api.appchain.AppStateMachine`: | Method | When | Contract | |---|---|---| | `id()` | — | stable identifier, matched against `state-machine` config | | `init(reader, info)` | once at start | read-only warm-up; `info` = (chainId, own member key, member count) | | `validate(msg)` | admission (pool + block selection) | fast, side-effect-free, MAY run concurrently; envelope auth already done; reject keeps the message out of blocks. `~` system topics bypass it | | `apply(block, writer)` | exactly once per finalized block, in height order, on EVERY member | the deterministic transition; all writes via `writer.put/delete` (= MPF entries) | | `query(path, params)` | committed reads | invoked through the chain-scoped REST `/query/{path}` route; state proofs remain available through `state/proof/{keyHex}` | **Determinism is the contract.** Inside `apply()`: no wall clock (use `block.timestamp()`), no randomness, no I/O, no environment reads, no iteration over unordered collections, no locale-dependent serialization. Violations don't corrupt anything — they *stall the chain*, because followers reject the proposer's state root (§4.2). Test with `StateMachineConformance` (runtime testkit): it applies an identical seeded block corpus in N independent runs plus a kill-and-reopen replay and asserts byte-identical roots at every height. **The two-tier rule** (worth internalizing): `validate` rejects; `apply` never does. Anything that reaches `apply` is already consensus-final, so a business-rule violation there must be a silent, deterministic no-op — re-check every rule you checked at admission. Every conforming state machine must follow this pattern. Convenience: `TypedAppStateMachine` + `MessageCodec` decode bodies at the edge (undecodable → reject at admission / skip at apply); `JacksonCborCodec.of(Class)` is the batteries-included codec. ## 10. Built-in state machine | id | Module | One-liner | |---|---|---| | `ordered-log` | runtime (built-in) | append-only log of opaque messages; per-message inclusion proofs | ### 10.1 `ordered-log` The default. Bodies are fully opaque — nothing is validated, everything finalizes. Per message it writes `sha256("~yano/finalized-message/v1/" || messageId) → cbor([schemaVersion, height, index, topic, sender])` and maintains a namespaced tip record. Resolve the public message ID through the `finalized-message-v1` typed proof subject; `state/proof/{keyHex}` is the lower-level route for the resolved physical key. The record format is shared (`FinalizedMessageIndex` in core-api) with opt-in indexes so proofs never diverge. Additional state machines are ServiceLoader plugins maintained in [Yano X](https://github.com/bloxbean/yano-x/tree/main/state-machines). Their wire formats, typed clients, proofs, and operational guidance live with those plugins so Yano's core documentation does not imply that they are built in. ## 11. Writing your own state machine 1. Implement `AppStateMachine` (or extend `TypedAppStateMachine`), obey §9's determinism rules and the two-tier validate/no-op pattern. 2. Implement `AppStateMachineProvider` — `id()` matches the `state-machine` config value; override `create(AppStateMachineContext)` if you need settings (`context.settings()` is the `yano.app-chain.*` map with the stem stripped, e.g. `machines.my-machine.foo`). 3. **Plugin mode** (default distribution, no rebuild): register the provider in `META-INF/services/org.yanoproject.api.appchain.AppStateMachineProvider`, and add `META-INF/yano/plugins/.json`. The manifest declares an `app-state-machine` contribution whose `name` equals the provider `id()` and whose `provider` is the same fully-qualified class as the ServiceLoader entry. For example: ```json { "schemaVersion": 1, "id": "com.example.my-machine", "version": "1.0.0", "yanoApi": { "min": 1, "max": 1, "minLevel": 1 }, "dependencies": [], "contributions": [ { "kind": "app-state-machine", "name": "my-machine", "provider": "com.example.MyMachineProvider" } ] } ``` Package one self-contained bundle JAR, drop it into the JVM node's `plugins/` directory, and set `state-machine: my-machine`. An unknown id fails fast listing available ids. Native images cannot load directory JARs; include and map the manifested bundle at application build time so catalog and reflection metadata are generated before the native executable. 4. **Library mode**: pass the machine instance straight to the `AppChainSubsystem` constructor — no provider or services file needed. 5. Start from `scaffolds/plugin-template/` (a complete counter machine + provider + ServiceLoader entry + bundle manifest), and gate your machine with `StateMachineConformance` before trusting it with a multi-node chain. The full walkthrough is user guide §6 / tutorial Part 2. ## 12. Where to go deeper - **Anchoring internals** (thread NFT, co-sign rounds, on-chain validator, independent verification): user guide §5 and the implementation. - **Rotation & governed membership design**: the implementation, `008.3-*`. - **Wire ABIs** (anchor datum, evidence bundle, observations): `core-api/src/main/cddl/appchain/*.cddl`. - **Live regressions** that exercise everything in this guide on a real devnet: the `test-app-chain-*` skills under `.claude/skills/`. --- # State machines & extensions Extend Yano through deterministic state machines and public plugin contracts. Canonical URL: https://getyano.dev/app-chains/extensions/ Use `ordered-log` when applications interpret a shared event history. Use an `AppStateMachine` plugin when members must enforce application state transitions together. ## Deterministic application rules Every voting member must compute the same state from the same inputs. Keep network requests, filesystem operations, randomness, and local wall-clock reads out of deterministic application logic. Admission validation gives callers early feedback; deterministic application remains the authority for finalized commands. Expose your machine through `AppStateMachineProvider`, ServiceLoader metadata, and a Yano plugin manifest. Install the same compatible bundle on all voting members of a JVM cluster. Select its ID with the chain's `state-machine` setting. Start from the [Yano X plugin template](https://github.com/bloxbean/yano-x/tree/main/scaffolds/plugin-template). Read [plugin operations](/operate/plugins/) and the [query/domain API contract](/reference/plugin-contract/) before deploying. ## Effects, observations, and queries - **Effects** separate deterministic intent from external work and its reported outcome. Executors must support bounded execution, recovery, and the documented idempotency contract. - **Observations** bring certified source reports into application state. They are preview functionality, disabled by default, and do not establish objective truth merely by reaching a reporter threshold. - **Committed queries** read against a fixed committed root; domain APIs may provide convenient decoded projections with a different trust boundary. [Yano X](https://github.com/bloxbean/yano-x) owns additional stock implementations and integrations. Yano retains the host and extension contracts. ## Native boundary Dynamic plugin JAR loading is a JVM feature. Native distributions retain the core providers; copying a JVM plugin beside a native executable does not install it. ## Test your plugin Use [the app-chain testkit](/develop/app-chain-testkit/) for embedded clusters, plus the conformance suites for the SPI you implement. Test actual failure/recovery behavior, not just a successful submit. --- # Certified observations Understand the preview observation framework and its trust model. Canonical URL: https://getyano.dev/app-chains/observations/ Certified observations bring bounded reports from configured sources or reporters into an app chain. They are **preview functionality, disabled by default**. Agreement on reports does not establish that a source is correct. ## Configure an explicit profile `observations.profile-cbor-hex` supplies a canonical observation profile. Its bytes are committed separately from the consensus profile. Omitting it selects the disabled profile. A retained chain rejects mismatched profiles; this is not an in-place feature toggle for arbitrary existing ledgers. The framework supports one-shot and recurring exact-value observations and complete-source numeric aggregation. Scheduling uses app heights, or verified L1 slots when the selected logical-time version and stable L1 feed support it. Height cadence is chain progress, not elapsed minutes. ## Separate collection from consensus Application callbacks emit deterministic intents. Bounded workers acquire data outside deterministic execution. Reports and certificates are made durable and are consumed under the configured rules. Status reports acquisition failures, queue pressure, journal usage, active subscriptions, and readiness. `observations.workers` defaults to `4` and supports `1–64`. The signing journal has bounded entry and byte limits. Never copy a signing journal across member identities or erase it to bypass a recovery failure. ## Audit and recovery Committed observation queries use the reserved `yano/observations/` prefix and return a height/root. They read authenticated state, not the node-local worker's current view. For certificate verification, use the domain-separated commit digest and independently trusted height-specific consensus context. Older incomplete certified headers must not be treated as equivalent evidence. Keep provider/plugin API levels matched to the host. Recovery must preserve finalized history, original profiles, membership, and signing locks. Offline replay tools verify every reconstructed root before installation. A missing journal or a divergent replay is not repaired by starting an empty ledger with the same signing key. Provider implementation contracts live under `org.yanoproject.api.appchain`; extension packages and their qualification remain separately versioned. See [extension development](/app-chains/extensions/) for the plugin boundary. --- # Ordered log in depth Record events, query positions, and verify ordered-log state. Canonical URL: https://getyano.dev/app-chains/ordered-log/ `ordered-log` is Yano's built-in append-only state machine for opaque events. It gives applications one threshold-finalized order, replicated history, provable message positions, and an optionally Cardano-anchored state root. It does not parse payloads or implement business rules. The configured id is exactly `ordered-log`. Names such as `orders-chain` are chain ids chosen by the operator; they do not create an order-specific data model. ## When to use it Use `ordered-log` when participants need to agree that an event was finalized at a particular position, while applications interpret the event body: - cross-organization event and audit logs; - order, shipment, case, or compliance histories; - immutable evidence and document-hash journals; - a shared event stream consumed by external services; and - notarization of arbitrary application bytes. Choose another state machine when the chain must maintain or enforce business state. `ordered-log` does not enforce schemas, unique order ids, lifecycle transitions, balances, ownership, approvals, or application-level authorization. ## Mental model: chain, topic, and payload These are independent concepts: | Concept | Example | Meaning | |---|---|---| | Chain id | `orders-chain` | Independent ledger, ordering, finality, state root, proofs, and optional anchor | | State-machine id | `ordered-log` | Deterministic logic applied to finalized blocks | | Topic | `order-created` | Caller-supplied label used for routing and filtering | | Payload | `{"orderId":"A-1001"}` | Opaque bytes owned and interpreted by the application | One chain can carry many topics: ```text orders-chain ├── order-created ├── order-paid ├── order-shipped └── order-cancelled ``` The state machine does not assign meaning to those topic names. Ordinary topics may be any valid UTF-8 value within the framework limit; names starting with `~` are reserved for Yano. ## Start the out-of-the-box demo From an extracted **JVM release** directory (recommended for app chains): ```bash ./yano.sh start:devnet,appchain ``` This starts Yano's built-in `orders-chain` as a single-member ordered log. The identity in `config/application-appchain.yml` is deterministic and intended only for local testing. For the multi-node commands in the rest of this walkthrough, use an extracted [Yano X](https://github.com/bloxbean/yano-x) showcase distribution: ```bash ./yano.sh appchain cluster start 3 ``` The showcase also hosts `orders-chain` as an `ordered-log` chain. Submit through member 1: ```bash ./yano.sh appchain cluster submit orders-chain order-created \ '{"orderId":"A-1001","quantity":4}' \ --node 1 ``` The command shape is: ```text ./yano.sh appchain cluster submit [--node ] ``` `--node 1` selects the ingress node. That member authenticates and signs the envelope, then gossips it to the cluster. It does not make node 1 the sequencer, state owner, or business-event processor. The payload need not be JSON: ```bash ./yano.sh appchain cluster submit orders-chain notes \ 'order A-1001 was checked manually' ``` The launcher submits command-line payloads as UTF-8 text. Use the REST `bodyHex` field or the Java client's byte-array method for arbitrary binary payloads. ## Configuration A single configured chain can select the machine directly: ```yaml yano: app-chain: enabled: true chain-id: orders-chain state-machine: ordered-log ``` The multi-chain form is: ```yaml yano: app-chain: chains[0]: chain-id: orders-chain state-machine: ordered-log membership: mode: governed chains[1]: chain-id: shipments-chain state-machine: ordered-log membership: mode: governed ``` Both chains use the same implementation but remain independent. Each has its own blocks, pending pool, finality certificates, state root, membership, sequencing policy, storage, proofs, and optional L1 anchor. Use multiple topics in one chain when the events share the same membership, finality, retention, anchoring, and operational lifecycle. Use separate chains when any of those boundaries should differ. Direct Yano startup reads `config/application-appchain.yml` as a complete single-node configuration. The Yano X showcase launcher reads its packaged `yano/config/application-appchain.yml` and injects node-specific member keys, peer addresses, threshold, and proposer. Production deployments must supply those values through their generated per-node configuration and secret-management flow. ### Operational tuning `ordered-log` has no machine-specific settings. It uses the common chain settings for capacity, latency, expiry, retention, sequencing, membership, and anchoring. For example: ```yaml yano: app-chain: chains[0]: chain-id: orders-chain state-machine: ordered-log max-message-bytes: 65536 default-ttl-seconds: 600 max-ttl-seconds: 3600 block: interval-ms: 1000 max-bytes: 4194304 max-messages: 5000 pool: max-messages: 10000 retention: enabled: true keep-blocks: 1000 ``` Keep consensus-affecting settings identical across members. When retention is enabled, eligible old message bodies below the confirmed anchor horizon may be stripped, while headers, ids, roots, and certificates remain so inclusion evidence is preserved. Archive bodies or evidence bundles separately when the original content must remain independently verifiable. ## Submit through REST In the default local cluster, node indices 0, 1, and 2 use HTTP ports 7070, 7071, and 7072. This request is equivalent to CLI submission with `--node 1`: ```bash RESPONSE=$(curl -sS -X POST \ http://127.0.0.1:7071/api/v1/app-chain/chains/orders-chain/messages \ -H 'Content-Type: application/json' \ -d '{ "topic":"order-created", "body":"{\"orderId\":\"A-1001\",\"quantity\":4}" }') echo "$RESPONSE" | jq . MESSAGE_ID=$(echo "$RESPONSE" | jq -r .messageId) ``` A successful submission returns HTTP `202`: ```json { "messageId": "<64 lowercase hex characters>", "chainId": "orders-chain", "topic": "order-created" } ``` For arbitrary bytes, send hexadecimal data instead: ```bash curl -sS -X POST \ http://127.0.0.1:7071/api/v1/app-chain/chains/orders-chain/messages \ -H 'Content-Type: application/json' \ -d '{"topic":"binary-event","bodyHex":"010203ff"}' | jq . ``` When REST authentication is enabled, also send `X-API-Key`. A topic-scoped API key can restrict which topics a caller may submit to, but that is an ingress policy rather than an `ordered-log` consensus rule. ## Submit from Java with Yano X The following client belongs to Yano X and is installed separately from the Yano host. Use the lightweight `yano-appchain-client` artifact with the same version as the Yano nodes: ```groovy implementation "org.yanoproject:yano-appchain-client:${yanoVersion}" ``` ```java import org.yanoproject.x.client.AppChainClient; AppChainClient client = AppChainClient .builder("http://127.0.0.1:7071/api/v1") .chainId("orders-chain") // .apiKey("secret") // when REST authentication is enabled .build(); String payload = """ {"event":"order-created","orderId":"A-1001","quantity":4} """.strip(); var submitted = client.submitText("orders", payload); System.out.println(submitted.messageId()); ``` Use `client.submit(topic, byte[])` for arbitrary bytes or `client.submitTyped(topic, value, encoder)` with an application-owned JSON, CBOR, or protobuf encoder. Submitting identical payload bytes twice normally creates two messages. The signed envelope also contains sender sequence and expiry data, so each submission has its own message id. Business-level idempotency, such as uniqueness by `orderId`, requires application logic or a custom state machine. ## What happens after submission 1. The ingress node checks framework bounds, signs the envelope with its member key, retains it in the pending pool, and gossips it. 2. The current proposer orders pending messages into an app block. 3. Every voting member deterministically executes `ordered-log` and derives the same state root. 4. The configured threshold certifies the block. 5. Members commit the block, message index, state, and finality certificate. 6. If anchoring is enabled, a later anchor commits the certified application root to Cardano. HTTP `202` means the ingress node accepted the envelope; it does not mean the message is finalized. Confirm finalization before treating the event as committed: ```bash until curl -sf \ "http://127.0.0.1:7070/api/v1/app-chain/chains/orders-chain/messages/$MESSAGE_ID" \ | jq .; do sleep 1 done ``` Applications can also follow finalized messages through the SSE endpoint: ```text GET /api/v1/app-chain/chains/orders-chain/stream?fromHeight=0&topic=orders ``` The Java client exposes the same behavior through `subscribe(...)` and `subscribeTyped(...)`. ## Committed state and proofs For each finalized message, `ordered-log` writes: ```text sha256("~yano/finalized-message/v1/" || message-id) -> cbor([schema-version, block-height, message-index, topic, sender]) ``` It also maintains: ```text ~tip -> cbor(block-height) ``` The message body remains in finalized block history and the message index; it is not duplicated in the state value. Use the typed proof subject to resolve a public message ID to the reserved physical state key and request its MPF proof: ```bash 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 "$(jq -nc --arg id "$MESSAGE_ID" ' {coordinates:{"message-id":$id}, view:"latest", claim:{claimId:"recorded",operands:{}}, includeEvidence:false}')" \ | jq '{stateRoot:.proof.stateRoot,presence:.proof.presence,position:.fact.fields,claim:.claimResult.satisfied}' ``` The lower-level `state/proof/{keyHex}` route accepts the resolved physical key, not the public message ID. The separate `messages/{messageId}/proof` route proves membership in the finalized block's compact `messagesRoot`. That proof binds the message's finalized position, topic, and sender to the returned committed state root. For audit-grade verification, verify it against an independently trusted root, such as the matching Cardano anchor, rather than trusting a root supplied by the same node. Other useful endpoints are: ```text GET /chains/{chainId}/messages/{messageId} GET /chains/{chainId}/messages/by-topic/{topic}?fromHeight=0&limit=100 GET /chains/{chainId}/blocks?from=1&limit=100 GET /chains/{chainId}/evidence/{messageId} GET /chains/{chainId}/status ``` All paths above are relative to `/api/v1/app-chain`. ## Bounds and security behavior The public submission path and default framework profile apply these constraints: - a non-empty body on REST and CLI submission; - body size at most 65,536 bytes by default (`max-message-bytes`); - topic size at most 256 UTF-8 bytes with no NUL character; - topics starting with `~` are reserved; - envelope signature and current member authorization; - pending-pool capacity and message expiry; and - structural, replay, block-size, and finality checks. These checks protect the protocol. They do not validate a JSON schema or any business meaning. Do not put secrets or unnecessary personal data in payloads: every member receives the body and finalized history may be retained or exported. Encrypt application bodies before submission when confidentiality is required, and manage decryption keys outside consensus. ## Customization choices ### Use payload conventions when validation is external Applications may agree on a versioned envelope such as: ```json { "schemaVersion": 1, "event": "order-created", "eventId": "evt-9001", "orderId": "A-1001", "quantity": 4 } ``` Producers and consumers can validate this schema without changing Yano. The chain still accepts other bytes, so this is appropriate only when business validation is deliberately outside consensus. ### Use topics for routing, not enforcement Topics let consumers filter one ordered history. They are useful for event families such as `order-created`, `order-paid`, and `order-shipped`, but the machine does not enforce a topic allow-list or a topic-specific payload shape. ### Use separate chains for isolation Run multiple `ordered-log` chains when applications require distinct member sets, sequencing, retention, anchoring, or failure boundaries. Reusing the same state-machine implementation does not share state between chains. ### Write a custom state-machine plugin for business rules Use a custom `AppStateMachine` when consensus must enforce rules such as: - strict payload decoding and bounds; - unique order ids; - allowed transitions such as `CREATED -> PAID -> SHIPPED`; - sender- or role-based authorization; or - committed current state keyed by `orderId`. Package the implementation behind `AppStateMachineProvider`, a ServiceLoader entry, and a Yano plugin manifest. Deploy the identical bundle to every voting member of a JVM cluster and select its id with `state-machine`. Start with the [Yano X plugin template](https://github.com/bloxbean/yano-x/tree/main/scaffolds/plugin-template). Admission hooks improve feedback and keep malformed commands out of blocks built by honest proposers, but deterministic `apply()` logic remains the consensus authority. It must re-decode input and safely handle invalid or stale commands without external I/O, randomness, wall-clock reads, or other node-local behavior. Do not switch an existing `ordered-log` ledger to incompatible application semantics in place. Use a fresh chain id/storage or a deliberately designed, versioned migration and activation plan. ### Use consumers or effects for external actions `ordered-log` itself does not call webhooks, Kafka, an ERP, or another external system. A consumer can subscribe to finalized messages and perform idempotent off-chain work. When an external action and its outcome must participate in the committed workflow, use an effect-emitting stock/composite machine or a custom state-machine and executor plugin. Never perform network, database, filesystem, or other external I/O from a state machine's deterministic `apply()` method. ## Related documentation - [Yano X tutorials](https://github.com/bloxbean/yano-x/tree/main/docs/appchain/tutorials) - [Yano X stock state machines](https://github.com/bloxbean/yano-x/tree/main/state-machines) - [Consensus and state-machine internals](/app-chains/consensus/) - [Yano X Java app-chain client](https://github.com/bloxbean/yano-x/tree/main/sdk/client) --- # What is an app chain? Understand application ledgers, member finality, proofs, and Cardano anchoring. Canonical URL: https://getyano.dev/app-chains/overview/ An app chain is a shared application ledger hosted by Yano. A configured group of members orders messages, executes the same deterministic logic, and certifies the resulting state. For example, several organizations can agree on the order of shipment events and later prove that a particular message was recorded. Yano's built-in `ordered-log` provides this shared history without interpreting the payload. ## From message to evidence 1. Your application submits a topic and payload to a member. 2. The member validates and signs an envelope, then gossips it. 3. A proposer orders messages into an app block. 4. Members execute the state machine and certify the block at the configured threshold. 5. The committed state root supports queries and proofs. 6. Optional anchoring records a commitment on Cardano. **Accepted, finalized, and anchored are separate states.** HTTP `202` means accepted at ingress. Finality requires the member certificate. L1 anchoring happens later and needs confirmation. ## What is included? Yano includes the host, networking, consensus/finality, commitment and proof surfaces, plugin contracts, L1 anchoring, and one built-in machine: `ordered-log`. [Yano X](https://github.com/bloxbean/yano-x) supplies additional stock state machines, capabilities, connectors, application products, SDKs, and JVM tooling. They are separately installed extensions; a plain Yano distribution does not automatically include them. ## Independent chains A node may host multiple chains. Each has its own identity, member set, sequencing policy, state, storage, and optional anchor policy. Use topics within one chain for event categories; use separate chains for different trust or operational boundaries. ## Trust model Member finality relies on configured keys, quorum rules, and consensus context. A state proof establishes a mathematical relationship to a root. You still need a trusted source for that root and the chain identity. Cardano anchoring does not make an opaque business claim true. [Start a local app chain](/app-chains/quickstart/) or [learn about proofs](/app-chains/proofs/). --- # Finality, proofs & L1 anchors Distinguish message inclusion, state proofs, certificates, and Cardano confirmation. Canonical URL: https://getyano.dev/app-chains/proofs/ Yano exposes different evidence for different questions. Choose the proof that matches the claim you want to make. | Evidence | What it establishes | | --- | --- | | Finality certificate | The configured member threshold certified an app block | | Message proof | A message ID occurs in a finalized block's `messagesRoot` | | State proof | A key/value or absence relates to an expected Merkle Patricia Forestry root | | Evidence bundle | Related message, block, certificate, and available anchor evidence for verification | | Cardano anchor | An app-chain commitment was recorded on L1 under the selected anchor scheme | ## Query a proof Message inclusion uses: ```text GET /api/v1/app-chain/chains/{chainId}/messages/{messageId}/proof ``` The lower-level state route is `state/proof/{keyHex}`. **A public message ID is not the physical state key.** For ordered-log message positions, use the `finalized-message-v1` typed proof subject. See the [ordered-log walkthrough](/app-chains/ordered-log/). ## Anchor modes Metadata anchoring records a commitment in transaction metadata. Script anchoring uses the script-anchor protocol and thread NFT. These have different verification and operating requirements. Script anchoring requires bootstrap, signing configuration, funding, and a matching script identity; setting a boolean alone is insufficient. App finality and L1 stability are distinct. Confirm the anchor transaction and bind it to the exact app block/root. A node reporting an anchor is not an independent Cardano lookup. ## Verification checklist 1. Pin the expected chain and genesis identity. 2. Obtain the expected root independently, or explicitly accept the trust in its provider. 3. Verify the proof using a release-matched verifier. 4. Verify the finality certificate against trusted membership and height-specific consensus context. 5. If relying on L1, verify the matching transaction, datum or metadata, script identity, and confirmation policy through an independent Cardano source. `org.yanoproject:yano-appchain-proof-verifier` supplies the retained portable proof-verification module. Hashing payload bytes alone does not verify finality or anchoring. For consensus configuration and anchor lifecycle detail, see [the consensus guide](/app-chains/consensus/). --- # Your first app chain Start the built-in ordered log and submit your first event. Canonical URL: https://getyano.dev/app-chains/quickstart/ Download and extract the **[JVM distribution](/start/installation/)**, recommended for app chains for now. From its extracted directory: ```bash ./yano.sh start:devnet,appchain ``` This activates the bundled app-chain profile, including `orders-chain`, a **single-member** `ordered-log` demo. Pre12 also includes a `registry-chain`; the current-source profile keeps `orders-chain` only. Its deterministic key is for local testing only. It is not a multi-member security demonstration and does not turn on L1 anchoring by itself. ## 1. Submit an event ```bash curl -fsS -X POST \ http://localhost:7070/api/v1/app-chain/chains/orders-chain/messages \ -H 'Content-Type: application/json' \ -d '{"topic":"order-created","body":"order A-1001"}' ``` Save the returned `messageId`. The response is HTTP `202`: the event has been accepted, but may not yet be finalized. ## 2. Read the finalized message Replace `` with the returned value. Query again after the proposer has had time to create a block: ```bash curl -fsS 'http://localhost:7070/api/v1/app-chain/chains/orders-chain/messages/' curl -fsS \ http://localhost:7070/api/v1/app-chain/chains/orders-chain/status ``` ## 3. Follow the event stream ```bash curl -N 'http://localhost:7070/api/v1/app-chain/chains/orders-chain/stream?fromHeight=0&topic=order-created' ``` SSE streams finalized messages. Design consumers for reconnects and duplicate delivery using durable application checkpoints. ## What the chain guarantees `ordered-log` records opaque bytes in a shared finalized order. It does not enforce unique order IDs, a JSON schema, balances, or an order lifecycle. Two submissions of the same payload can produce different message IDs because their envelopes include sender sequence and expiry. Use a custom state machine when consensus must enforce business rules. See [ordered-log in depth](/app-chains/ordered-log/) and [extension development](/app-chains/extensions/). --- # Building & testing Run the build tiers and packaging checks. Canonical URL: https://getyano.dev/contribute/build-and-test/ Yano uses layered verification so the normal development build keeps complete L1 and retained app-chain host/OrderedLog coverage without also running every multi-node, packaged-runtime, or native-image acceptance test. Optional state machines, connectors, products, and their cryptographic suites are built in [Yano X](https://github.com/bloxbean/yano-x). ## Requirements - JDK 25 - The repository Gradle wrapper (`./gradlew`) - `bash`, `curl`, `jq`, and `unzip` for packaged distribution acceptance ## Verification tiers | Tier | Source location | Purpose | Included in `build` | |---|---|---|---| | Core | `src/test` | L1 tests plus retained app-chain host, OrderedLog, proof, and plugin-boundary tests | Yes | | Extended | `src/integrationTest` | Retained multi-node sequencing, catch-up, membership, anchoring, and host integration | No | | Distribution | packaged process contracts | Tests the lean JVM/native archives and plugin-directory boundary | No | Tests are classified by behavior, not by class name. A test that starts several app-chain nodes belongs in `src/integrationTest` even when it validates retained host behavior. ## Normal development build Run the default build for normal changes: ```bash ./gradlew build -PskipSigning=true ``` This compiles every retained module and runs ordinary L1 and app-chain core tests. It intentionally does not run extended multi-node or distribution suites. For a faster module-level iteration: ```bash ./gradlew :runtime:test ./gradlew :appchain-testkit:test ./gradlew :appchain-proof-verifier:test ``` Run one ordinary test or method with Gradle's standard filter: ```bash ./gradlew :runtime:test \ --tests 'org.yanoproject.runtime.appchain.AppChainTwoNodeSmokeTest' ./gradlew :runtime:test \ --tests 'org.yanoproject.runtime.appchain.AppChainTwoNodeSmokeTest.twoNodes_exchangeAuthenticatedMessages_bothDirections' ``` ## Extended tests Run every retained integration suite, or select a module/test: ```bash ./gradlew extendedTest ./gradlew :runtime:integrationTest ./gradlew :runtime:integrationTest \ --tests 'org.yanoproject.runtime.appchain.GovernedMembershipIntegrationTest' ``` ## Distribution verification Run the lean JVM distribution and directory-plugin boundary gate: ```bash ./gradlew distributionCheck -PskipSigning=true ``` It builds the ordinary Yano JVM ZIP and verifies: - the core distribution manifest and OrderedLog-only built-in boundary; - packaged catalog integrity and directory-loaded conformance plugins; - absence of Yano X bundles and downstream source/task dependencies; and - packaged launchers, config, plugin tools, and executable bits. The gate uses temporary devnet data. It does not connect to Preview, Preprod, or Mainnet and does not publish an artifact. Individual distribution checks remain available: ```bash ./gradlew :app:verifyCoreJvmDistribution -PskipSigning=true ./gradlew :app:packagedJvmPluginCatalogSmoke -PskipSigning=true ``` Create archives without running the acceptance gate: ```bash ./gradlew :app:yanoDistZip -PskipSigning=true ./gradlew :app:yanoNativeDistZip -PskipSigning=true ``` ## Full optional build Run all retained repository core, integration, and JVM distribution tiers with one command: ```bash ./gradlew fullBuild -PskipSigning=true ``` For a from-scratch release-style check: ```bash ./gradlew clean fullBuild -PskipSigning=true ``` `fullBuild` is intentionally not the normal inner-loop command. Network-specific Haskell synchronization, native-image, and public-network acceptance workflows retain their dedicated commands and opt-in requirements. Yano X owns connector, product, showcase, and eUTxO/ZK gates. ## Continuous integration Pull-request verification runs the retained core build, integration tests, distribution checks, and GraalVM gates independently. Yano X has its own JVM-only extension and release-acceptance workflow. Release workflows use the corresponding repository-owned gates before publishing. ## Performance and diagnostics Gradle build caching is enabled in `gradle.properties`. Use the standard repository-wide build command so dependency-alignment gates retain the Gradle project locks they require: ```bash ./gradlew build -PskipSigning=true ``` Do not add Gradle's `--parallel` flag to a repository-wide build. The existing Module alignment gates deliberately inspect several projects' dependency graphs and Gradle 9 rejects that cross-project resolution under project parallelism. CI obtains safe concurrency by running the core, extended, crypto, and distribution tiers as independent jobs. Test output defaults to failures and skips. Enable the former per-test and standard-stream logging when diagnosing a failure: ```bash ./gradlew :runtime:test -PverboseTests=true ``` Use profiling when a tier becomes unexpectedly slow: ```bash ./gradlew build --profile --console=plain -PskipSigning=true ``` The HTML report is written under `build/reports/profile/`. --- # Build from source Optional source builds for contributors and advanced users. Canonical URL: https://getyano.dev/contribute/build-from-source/ Most users should [download a release](/start/installation/). This page is for contributors, custom artifacts, and unreleased changes. ## Get the source and build the application Install JDK 25, then: ```bash git clone https://github.com/bloxbean/yano.git cd yano ./gradlew :app:quarkusBuild -PskipSigning=true cd app ./yano.sh start:devnet ``` Choose the branch or tag containing the changes you want before building. For current namespace examples, use a checkout containing `org.yanoproject`; pre12 predates that rename. The remaining commands build release-style distributions from the checked-out source tree. Run commands from the repository root. ## Prerequisites - JDK 25. - Docker with the Compose plugin for Docker distributions and container native builds. - GraalVM 25 with `native-image` support for host native builds. Use `-PskipSigning=true` for local builds that do not publish artifacts. ## Artifact API Prefix The public REST prefix is fixed during Quarkus augmentation. The sole supported build input is `-PyanoApiPrefix=`; omitting it uses `/api/v1`. The value is at most 256 characters and must be `/` or a canonical absolute path made of unescaped `[A-Za-z0-9._~-]+` segments, with no empty, `.` or `..` segment or trailing slash. For example, build a JVM distribution whose API is rooted at `/bf`: ```bash ./gradlew :app:yanoDistZip -PyanoApiPrefix=/bf -PskipSigning=true ``` The build generates literal REST configuration, reserves `quarkus.http.root-path=/`, and emits both the raw `META-INF/yano-api-prefix-v1` marker and immutable `/ui/plugins/api-prefix.json` from that value. Do not change `yano.api-prefix`, `quarkus.resteasy.path`, or `quarkus.http.root-path` in launch configuration. Runtime-style system properties or environment variables for those keys are rejected during a build, and launch-time drift aborts before node or plugin initialization. Changing the prefix always means building a new JVM, native, or container artifact. The packaged contract gate must pass independently for the default, a custom prefix, and the canonical root: ```bash ./gradlew :app:packagedApiPrefixContractSmoke -PskipSigning=true ./gradlew :app:packagedApiPrefixContractSmoke -PyanoApiPrefix=/bf \ -PskipSigning=true ./gradlew :app:packagedApiPrefixContractSmoke -PyanoApiPrefix=/ \ -PskipSigning=true ``` Each invocation builds and tests the matching artifact. The gate verifies its raw marker, dashboard discovery JSON, positive route, and fail-fast behavior for launch-time drift in either prefix property and the reserved HTTP root. ## JVM Zip Distribution Build the JVM zip: ```bash ./gradlew :app:yanoDistZip -PskipSigning=true ``` Output: ```text app/build/distributions/yano-.zip ``` The zip contains `yano.jar`, `yano.sh`, config files, network genesis files, plugin directory scaffolding, and the JVM-only offline plugin catalog tool under `tools/yano-plugins/`. It also contains the repository `LICENSE` and a normalized CycloneDX 1.6 inventory at `sbom/yano.cdx.json`; packaging fails if an external Maven component lacks license metadata. After extracting the zip, start a network with the optional history archive by composing the bundled `projection` profile after the network profile: ```bash ./yano.sh start:preprod,projection # or ./yano.sh start:mainnet,projection ``` The profile writes to DuckLake. The archive is fresh-sync only: it is built from genesis and there is no partial-coverage mode, so it needs an empty storage directory rather than an existing node's. History is not supported by the native-image distribution. After extracting the JVM zip, validate or inspect one or more plugin JARs without loading provider code: ```bash ./tools/yano-plugins/bin/yano-plugins validate plugins/example.jar ./tools/yano-plugins/bin/yano-plugins inspect --format table plugins/example.jar ./tools/yano-plugins/bin/yano-plugins inspect --format json plugins/example.jar # Windows: tools\yano-plugins\bin\yano-plugins.bat validate plugins\example.jar ``` The CLI is also available as a standalone application distribution: ```bash ./gradlew :plugin-catalog:distZip -PskipSigning=true unzip plugin-catalog/build/distributions/yano-plugins-.zip \ -d /tmp/yano-plugins /tmp/yano-plugins/yano-plugins-/bin/yano-plugins validate plugin.jar ``` See [`plugin-catalog/README.md`](https://github.com/bloxbean/yano/blob/fd7fe406e9364689a3e829b79f82707488cebf1e/plugin-catalog/README.md) for policy options and stable exit codes, and [`PLUGIN_OPERATIONS.md`](/operate/plugins/) for deployment authentication, health, metrics, and dashboard guidance. Verify the final uber-JAR, its merged catalog/manifests, and JVM directory loading with the build-only conformance bundle: ```bash ./gradlew :app:packagedJvmPluginCatalogSmoke -PskipSigning=true ``` This task intentionally uses the default `includeNativePluginConformanceFixture=false`: the fixture must be absent from the application index so startup can prove it was selected from the external plugin directory. The task starts an isolated one-member app chain and asserts all ten catalog contribution kinds (`NodePlugin` plus nine typed SPIs), protected operations REST, the plugin health group, Prometheus metrics, and dashboard assets. The fixture's adversarial TCCL handoff also proves plugin callbacks crossed catalog facades. ## Native Zip Distribution Build a native zip for the current host platform: ```bash ./gradlew :app:yanoNativeDistZip \ -Dquarkus.native.enabled=true \ -Dquarkus.package.jar.enabled=false \ -PskipSigning=true ``` Output: ```text app/build/distributions/yano-native--.zip ``` Examples: ```text yano-native-0.1.0-pre4-macos-arm64.zip yano-native-0.1.0-pre4-linux-x64.zip yano-native-0.1.0-pre4-linux-arm64.zip ``` The zip contains the native `yano` executable, `yano.sh`, config files, and network genesis files, plus the same `LICENSE` and release SBOM. It deliberately has no plugin directory or `yano-plugins` JVM runtime: native images cannot load JARs dynamically. Run the standalone JVM CLI on a JDK 25 operator/build host when offline validation is needed. Native Yano embeds only retained core providers. Optional Yano X state machines, connectors, and products are supported by the JVM distribution, not by augmenting the native build. After the native zip task finishes, verify the final executable that it copied into the distribution: ```bash ./gradlew :app:nativePluginCatalogSmoke -PskipSigning=true ``` The smoke task regenerates the current packaged-JVM index and compares both its byte SHA-256 and selected-catalog fingerprint with the native executable's startup provenance record. This makes an executable built with different plugin catalog inputs fail even if it starts and reports healthy; it is not a digest of unrelated application code. The same rule applies to `-PyanoApiPrefix`: pass the identical value to the native build, distribution, and smoke commands. A native prefix cannot be changed after image generation. Use `-PyanoNativeBinary=` only to verify another executable built from the same catalog inputs. Release workflows always package first, smoke the resulting `app/build/yano` (or `yano.exe`), and only then upload the zip; this prevents a distribution-triggered native rebuild from replacing an executable that was already tested. Maintainers can additionally exercise native reachability for every typed app-chain plugin SPI with the non-published conformance fixture: ```bash ./gradlew :app:quarkusBuild \ -PincludeNativePluginConformanceFixture=true \ -Dquarkus.native.enabled=true \ -Dquarkus.package.jar.enabled=false \ -PskipSigning=true ./gradlew :app:nativePluginCatalogSmoke \ -PincludeNativePluginConformanceFixture=true \ -PskipSigning=true ``` This property is a verification-only build input. Do not use the resulting binary as a release artifact; the dedicated CI job neither publishes nor packages it. The smoke starts an isolated one-member, no-peer app chain and asserts all ten catalog contribution kinds (`NodePlugin` plus nine typed SPIs) through structured status, protected operations REST, the plugin health group, Prometheus metrics, and dashboard assets. It also retains the catalog-provenance and ignored-directory-JAR checks. ## Linux Native Zip From macOS For a Linux native binary from macOS, use Quarkus container native build. This is useful when preparing a Linux Docker native context locally: ```bash ./gradlew :app:yanoNativeDistZip \ -Dquarkus.native.enabled=true \ -Dquarkus.package.jar.enabled=false \ -Dquarkus.native.container-build=true \ -Dquarkus.native.builder-image=container-registry.oracle.com/graalvm/native-image:25i3 \ -PskipSigning=true ``` When Oracle's `native-image` builder is selected, Gradle adds `--gc=G1` and sets the container workdir to `/project`. ## Docker Compose Zip Distribution Build the Docker compose zip: ```bash ./gradlew :app:yanoDockerDistZip \ -PyanoDockerReleaseVersion=0.1.0-pre4 \ -PyanoDockerImageTag=0.1.0-pre4 \ -PskipSigning=true ``` Output: ```text app/build/distributions/yano-docker-0.1.0-pre4.zip ``` For local Docker image testing, use a local image tag: ```bash ./gradlew :app:yanoDockerDistZip \ -PyanoDockerReleaseVersion=0.1.0-pre4 \ -PyanoDockerImageTag=local \ -PskipSigning=true ``` The compose zip contains `yano.sh`, `yano.bat`, compose files, editable `config/application.yml`, editable `config/network`, `logs`, and `plugins`. Network-specific chainstate directories are created by the launcher on `start` or `restart`. ## Docker Images Docker images are built from Gradle-prepared artifact contexts: ```bash ./gradlew :app:prepareYanoDockerJvmContext -PskipSigning=true ``` ```bash ./gradlew :app:prepareYanoDockerNativeContext \ -Dquarkus.native.enabled=true \ -Dquarkus.package.jar.enabled=false \ -Dquarkus.native.container-build=true \ -Dquarkus.native.builder-image=container-registry.oracle.com/graalvm/native-image:25 \ -PskipSigning=true ``` See `docker/BUILD_FROM_SOURCE.md` for full Docker image build and smoke-test commands. ## Smoke Tests Check zip contents: ```bash unzip -l app/build/distributions/yano-*.zip | head unzip -l app/build/distributions/yano-native-*.zip | head unzip -l app/build/distributions/yano-docker-*.zip | head ``` Run JVM distribution: ```bash unzip app/build/distributions/yano-.zip -d /tmp/yano-jvm cd /tmp/yano-jvm/yano- YANO_AUTO_SYNC_START=false ./yano.sh start ``` Run native distribution: ```bash unzip app/build/distributions/yano-native--.zip -d /tmp/yano-native cd /tmp/yano-native/yano-native-- YANO_AUTO_SYNC_START=false ./yano.sh start ``` Run Docker compose distribution with local images: ```bash unzip app/build/distributions/yano-docker-.zip -d /tmp/yano-docker cd /tmp/yano-docker/yano-docker- ./yano.sh config ./yano.sh start curl -fsS http://localhost:7070/q/health/ready ./yano.sh stop ``` ## Standalone console artifact The normal node distribution includes its own UI where supported by the release. For a separately hosted console built from current source: ```bash ./gradlew :console-ui:consoleZip ``` Extract `console-ui/build/distributions/yano-console-ui-.zip` under the static server's `/ui` path. See [console hosting](/operate/console/) for API routing and CORS. --- # Contribute to Yano Build, test, and improve Yano through its public modules and contracts. Canonical URL: https://getyano.dev/contribute/guide/ To run Yano, [download a release](/start/installation/). To change the node itself, follow [build from source](/contribute/build-from-source/) for the checkout, Java prerequisites, and build commands. [Building and testing](/contribute/build-and-test/) explains the test tiers and extended checks. Use the suite that exercises your change; native packaging and multi-node behavior have additional gates. ## Repository boundaries | Area | Responsibility | | --- | --- | | `core-api` | Public contracts, role interfaces, plugin SPI | | `runtime` | Node assembly and runtime implementation | | `ledger-state`, `ledger-rules` | Ledger state and validation contracts | | `p2p`, `consensus` | Networking and consensus components | | `devnet-toolkit`, `testkit`, `testkit-ccl` | Local development and Java testing | | `app` | Quarkus API and runnable distribution | | `appchain/` | Retained app-chain configuration, test, and proof modules | | `archive-modules/` | Optional history projection and DuckLake backend | | `www/` | This documentation site | Yano owns `org.yanoproject.*`. Use domain names for new top-level packages; product names are reserved for sibling repositories. Yano X uses `org.yanoproject.x.*`. Keep implementation imports readable and use simple class names unless a collision requires qualification. ## Extension contributions Put additional stock app-chain machines, connectors, products, and JVM tooling in [Yano X](https://github.com/bloxbean/yano-x). Extend the public SPI without coupling the host to product-specific source paths or dependencies. ## Documentation contributions Edit Markdown under `www/src/content/docs/`, then run from `www/`: ```bash npm ci npm run check npm run build ``` The build refreshes AI artifacts and configuration references from this checkout and checks local links. Keep commands, units, defaults, and source ownership accurate. Describe user-facing behavior and include meaningful validation in your PR. --- # App-chain testkit Test your state machine with an embedded cluster of Yano members. Canonical URL: https://getyano.dev/develop/app-chain-testkit/ `org.yanoproject:yano-appchain-testkit` provides a JUnit 5 extension with generated member keys, temporary ledgers, and real socket connections between embedded nodes. ```java import org.yanoproject.appchain.testkit.AppChainCluster; import org.yanoproject.appchain.testkit.AppChainClusterHandle; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; @AppChainCluster(nodes = 3, stateMachine = "ordered-log") class SharedLogTest { @Test void finalizesOnEveryMember(AppChainClusterHandle cluster) throws Exception { String id = cluster.node(1).submit( "orders", "order-1".getBytes(StandardCharsets.UTF_8)); cluster.awaitFinalized(id); } } ``` `awaitFinalized` waits for the message on all nodes and fails on timeout. Node 0 is the sequencer. The default `threshold = 0` selects all members; `blockIntervalMs` defaults to `300`. You can set the chain ID, state-machine ID, member count, threshold, and proposer tick interval on the annotation. This is an integration fixture, not a simulation of Byzantine peers, adversarial timing, L1 rollback, or production load. Add workload-specific assertions and separate tests for those concerns. For effect executor plugins, the module also supplies provider-neutral conformance suites and an `EffectRuntimeHarness`. See the module source and contracts in the [repository](https://github.com/bloxbean/yano/tree/fd7fe406e9364689a3e829b79f82707488cebf1e/appchain/appchain-testkit). --- # Build with any SDK Connect your preferred Cardano SDK to Yano's Blockfrost-compatible transaction APIs. Canonical URL: https://getyano.dev/develop/blockfrost/ **Your dApp can use Yano from any language.** Keep building and signing transactions with your preferred off-chain SDK; use Yano's HTTP API for chain data, Plutus evaluation, and submission to the network. You do not need to write Java or embed Yano to use it as your dApp's backend. Cardano Client Lib (Java), MeshJS (JavaScript/TypeScript), and Evolution SDK's Lucid interface can connect through their Blockfrost providers. Other languages and SDKs can use the same supported endpoints through a configurable provider or a small HTTP adapter. ## How the pieces fit ```text Your dApp + wallet + favorite SDK │ query UTxOs and protocol parameters │ build a transaction and evaluate its Plutus scripts │ sign locally, then submit signed CBOR ▼ Yano's Blockfrost-compatible HTTP API │ admit the transaction and hand it to network submission ▼ Configured Cardano upstream peers → network propagation → block inclusion ``` Yano handles the node-side submission and propagation path. Your SDK constructs the transaction; your wallet or application holds the signing keys. On a devnet, transactions go to the local block-producing chain instead of a public network. ## 1. Start a release [Download and extract Yano](/start/installation/), then start it on the network your dApp uses: ```bash ./yano.sh start:preprod ``` For isolated local tests, use `./yano.sh start:devnet`. Match your SDK's network/address settings to the node, and wait for the relevant ledger state to be available. Public-network submission needs reachable upstream peers and forwarding enabled. The default API base is: ```text http://localhost:7070/api/v1 ``` Supply that **whole base path**, rather than the hosted Blockfrost `/api/v0` URL. `/q/swagger-ui` shows the endpoints supported by your installed release. ## 2. Configure your provider These snippets configure providers, not complete wallet or transaction applications. Keep the usual wallet setup, coin selection, change address, signing, and fee calculation in your SDK. ### Cardano Client Lib — Java In an application with CCL's Blockfrost backend dependency: ```java import com.bloxbean.cardano.client.backend.api.BackendService; import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService; BackendService backend = new BFBackendService( "http://localhost:7070/api/v1/", "yano-local"); var parameters = backend.getEpochService().getProtocolParameters(); ``` Pass `backend` to your CCL transaction-building workflow. CCL dependency imports remain `com.bloxbean.cardano.client.*`; the Yano namespace refactor does not rename CCL. For embedded Java tests, the separate [CCL testkit adapter](/develop/ccl/) supplies a backend without HTTP. ### MeshJS — JavaScript / TypeScript ```js import { BlockfrostProvider } from '@meshsdk/core'; const provider = new BlockfrostProvider('http://localhost:7070/api/v1'); const parameters = await provider.fetchProtocolParameters(); ``` Use this provider as your wallet/builder's fetcher, submitter, and evaluator where supported. Once your application has prepared the transaction: ```js // txCborHex is your prepared Plutus transaction; signedTxHex is wallet-signed. const budgets = await provider.evaluateTx(txCborHex); const txHash = await provider.submitTx(signedTxHex); ``` The [Mesh provider reference](https://docs.meshjs.dev/providers/classes/BlockfrostProvider) documents custom base URLs and these provider methods. Apply the evaluation budgets and rebuild/balance as required before the final signing and submission step. ### Evolution SDK — Lucid interface Yano's repository compatibility examples use `@evolution-sdk/lucid`: ```js import { Lucid, Blockfrost } from '@evolution-sdk/lucid'; const lucid = await Lucid( new Blockfrost('http://localhost:7070/api/v1', 'yano-local'), 'Preprod', ); ``` This example connects to the `preprod` node started above. Select your wallet and use the normal Lucid transaction workflow. The newer Evolution client API has its own configuration shape; use its [provider configuration documentation](https://intersectmbo.github.io/evolution-sdk/docs/modules/sdk/client/Client/) for the SDK version you install. The placeholder project IDs above satisfy providers that expect a Blockfrost project ID. They are not credentials or a substitute for access control. Configure any authentication at your Yano deployment or proxy explicitly. ## 3. Query, evaluate, and submit | Task | Method and path, relative to `/api/v1` | | --- | --- | | Read address UTxOs | `GET /addresses/{address}/utxos` | | Read current protocol parameters | `GET /epochs/latest/parameters` | | Read the latest block | `GET /blocks/latest` | | Inspect transaction inputs/outputs | `GET /txs/{txHash}/utxos` | | Evaluate Plutus execution units | `POST /utils/txs/evaluate` | | Submit a signed transaction | `POST /tx/submit` | Direct HTTP works from any language. To evaluate prepared transaction CBOR: ```bash curl -fsS -X POST http://localhost:7070/api/v1/utils/txs/evaluate \ -H 'Content-Type: application/cbor' --data-binary @tx-to-evaluate.cbor ``` The response uses the Blockfrost/Ogmios-style `result.EvaluationResult` mapping from redeemer identifiers to `memory` and `steps`. **Inspect the response body:** evaluation failures can return HTTP `200` with `result.EvaluationFailure`. Evaluation estimates execution units; it neither signs nor submits the transaction. After applying budgets, balancing, and signing: ```bash curl -fsS -X POST http://localhost:7070/api/v1/tx/submit \ -H 'Content-Type: application/cbor' --data-binary @signed-tx.cbor ``` Both routes also accept hex-encoded CBOR as `text/plain`. Evaluation requires available input state, protocol parameters, and an initialized evaluator. See [transaction workflows](/develop/transactions/) for evaluator options. ## Submission and confirmation are different Accepted submissions enter Yano's local transaction flow and are handed to the configured upstream forwarding/diffusion path. Network availability, peer policy, ledger validity, and expiry still affect propagation and inclusion. A returned transaction hash is **not confirmation** and cannot guarantee that a block producer will include it. Follow confirmed chain state and handle rollback. Inspect `/api/v1/status` and the [upstream settings](/node/upstream/) when submissions are not progressing. A local devnet never broadcasts its transactions onto preprod or mainnet. ## Compatibility boundaries Yano implements the Blockfrost-compatible surface needed for supported transaction workflows, not every hosted Blockfrost service. SDKs may make additional calls for history, polling, scripts, or chained transactions. Check the installed release's OpenAPI document and match SDK versions to the node. The current repository compatibility suite records two specific limitations: MeshJS can re-query an unconfirmed parent through canonical-only transaction routes, and the Evolution Lucid confirmation helper can request `/txs/{hash}/cbor`, which is not implemented. Do not assume that a working build/submit provider makes every SDK confirmation helper work. Use an available confirmed-state query appropriate to your transaction, or an SDK adapter, and handle rollbacks. For browser dApps on another origin, explicitly configure CORS for that origin or use a same-origin application backend. CORS is not authentication. The [console guide](/operate/console/) explains Yano's CORS settings. --- # Cardano Client Lib adapter Use an embedded CCL BackendService in integration tests. Canonical URL: https://getyano.dev/develop/ccl/ `testkit-ccl` adapts `YanoDevnetTestKit` to Cardano Client Lib's `BackendService` API. It is useful for Java tests where application code already depends on CCL abstractions and should run against an in-process Yano devnet. This module is intentionally separate from `testkit` so the base testkit does not pull in `cardano-client-backend`. ## Add the adapter In a JUnit 5 project already using the [Java testkit](/develop/java-testkit/), add the matching adapter version: ```groovy testImplementation "org.yanoproject:yano-testkit-ccl:${yanoVersion}" ``` ## What It Provides - `YanoBackendService.from(kit)`: creates a CCL `BackendService` backed by a `YanoDevnetTestKit`. Implemented service areas include the pieces needed for common devnet transaction workflows: - `UtxoService` - `TransactionService` - `EpochService` - `BlockService` Unsupported CCL services fail loudly instead of returning misleading empty results. ## Basic Usage ```java import com.bloxbean.cardano.client.backend.api.BackendService; import org.yanoproject.testkit.ccl.YanoBackendService; import org.yanoproject.testkit.devnet.YanoDevnetExtension; import org.yanoproject.testkit.devnet.YanoDevnetTestKit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; class CclIntegrationTest { @RegisterExtension static YanoDevnetExtension yano = YanoDevnetExtension.devnet().startNode(); @Test void usesCclBackendService(YanoDevnetTestKit kit) throws Exception { BackendService backendService = YanoBackendService.from(kit); var tip = backendService.getBlockService().getLatestBlock().getValue(); var params = backendService.getEpochService().getProtocolParameters().getValue(); // Pass backendService to application code that expects CCL. } } ``` ## Embedded Only `YanoBackendService` is embedded and in-process. It reads and writes through the public Yano testkit roles. It does not start a Yano app process and it does not use HTTP. For tests that need the production HTTP surface, use `testkit`'s `YanoAppProcess` and a normal HTTP-backed CCL backend such as `BFBackendService`. ## Protocol Parameters Protocol parameters are mapped from Yano's runtime snapshots when available. The fallback JSON mapper supports both Cardano node-style camel-case protocol params and Blockfrost-compatible snake-case protocol params. ## Boundaries This adapter does not expose runtime internals and does not attempt to implement the full CCL backend surface. Add support only when a real Yano devnet workflow needs it, and prefer failing loudly for unsupported methods. --- # Yano in use Use Yano as part of a complete local Cardano development environment. Canonical URL: https://getyano.dev/develop/devkit/ Yano is used in **[Yaci DevKit](https://github.com/bloxbean/yaci-devkit)** as a local Cardano devnet node. This is a practical use of Yano's block production and node-to-node protocol support: a broader development environment can run around the chain it produces. ## Yaci DevKit ### When to use DevKit Choose Yaci DevKit when you want its integrated local Cardano environment and tooling. Follow the setup and configuration instructions for your chosen DevKit version; the services and node choices are owned by that project. Choose [standalone Yano](/start/quickstart/) when you want to run and configure the node directly. Choose [Yano testkit](/develop/java-testkit/) when each test should own an isolated node lifecycle. ## Connecting a downstream consumer A Yano devnet serves blocks on its configured node-to-node port (normally `13337`) with devnet network magic `42`. An indexer or compatible downstream node must use the same network identity and genesis. Container hostnames and exposed ports depend on the DevKit deployment; `localhost` inside one container does not refer to a different container. Yano's faucet and time controls belong to an isolated development network. Use the DevKit workflow for the services it manages, rather than independently modifying a database that its node already owns. ## UVerify Sandbox UVerify Sandbox uses Yano as its local Cardano devnet node, together with Yaci Store for indexing. It starts from a prepared chain snapshot with UVerify contracts already deployed, making it useful for template development and SDK integration tests. See the [official UVerify Sandbox guide](https://docs.uverify.io/sandbox) for setup and supported workflows. Yano supplies local block production and snapshot/time controls; UVerify owns its application, sandbox orchestration, and funding workflow. The [UVerify engineering walkthrough](https://uverify.io/blog/sandbox-yano-yaci-store) explains how the services fit together. --- # Embed Yano in Java Use public node roles and the runtime assembly API in your application. Canonical URL: https://getyano.dev/develop/embed/ Yano is also a library. `YanoAssembly` composes the runtime and returns a `Yano` handle. The public role interfaces live under `org.yanoproject.api`; callers do not need raw runtime nodes or RocksDB handles. For devnet mutation controls, add `yano-devnet-toolkit` and use `YanoDevnetAssembly`: ```java import org.yanoproject.api.DevnetControl; import org.yanoproject.api.config.YanoConfig; import org.yanoproject.devnet.YanoDevnetAssembly; import org.yanoproject.runtime.assembly.Yano; YanoConfig config = YanoConfig.devnetDefault(0); try (Yano yano = YanoDevnetAssembly.devnet(config).build()) { yano.start(); DevnetControl devnet = yano.devnetControl().orElseThrow(); devnet.advanceTimeBySlots(10); devnet.createDevnetSnapshot("after-ten-slots"); } ``` Use `YanoAssembly.relay(config)` for a relay recipe. `YanoAssembly.fromConfig(config)` selects a recipe from configuration; explicit recipes make the intended role easier to review. The devnet toolkit supplies rollback, snapshot, faucet, and time controls. Normal relay recipes do not expose `DevnetControl`. Always close the node handle to release storage and network resources. ## Dependency coordinates Choose an actually published version with the `org.yanoproject` namespace, or publish this checkout locally. The current source version is in the [build manifest](/ai/manifest.json). ```groovy repositories { mavenCentral() mavenLocal() // if you built and published this checkout locally } dependencies { implementation "org.yanoproject:yano-runtime:${yanoVersion}" implementation "org.yanoproject:yano-devnet-toolkit:${yanoVersion}" } ``` Set `yanoVersion` explicitly and use matching versions across Yano modules. For tests, prefer the managed [Java testkit](/develop/java-testkit/) over assembling and cleaning up every resource yourself. --- # Java testkit Run isolated Cardano devnets in JUnit and plain Java. Canonical URL: https://getyano.dev/develop/java-testkit/ `testkit` provides JVM integration-test helpers for running Yano devnets from JUnit and plain Java tests. It builds on `devnet-toolkit` and keeps tests on the same public role APIs used by embedders. ## Add the dependency Use JDK 25 and set `yanoVersion` to a matching published release or your locally published checkout version. Add this to an existing JUnit 5 project: ```groovy dependencies { testImplementation "org.yanoproject:yano-testkit:${yanoVersion}" } test { useJUnitPlatform() } ``` Keep your JUnit Jupiter and test engine dependencies in the test project. See [embedding](/develop/embed/) for dependency repository setup. Building Yano itself is optional; contributors can use the separate [source-build guide](/contribute/build-from-source/). ## What It Provides - `YanoDevnetTestKit`: managed in-process devnet fixture. - `YanoDevnetExtension`: JUnit 5 extension with parameter injection. - `YanoDevnetTestConfig`: test-focused configuration builder. - Helper facades for common workflows: - `YanoQueries` - `YanoAwait` - `YanoWallets` - `YanoFaucet` - `YanoSnapshots` - `YanoTime` - `YanoTransactions` - `YanoAssertions` - `YanoWalletAssertions` - External-process helpers for heavier compatibility tests: - `YanoAppProcess` - `HaskellCardanoNodeProcess` - `YanoGenesisFiles` - `YanoExternalSyncAssertions` - `YanoAdaPotComparator` ## Storage And Lifecycle The default test configuration uses real RocksDB-backed chain storage in a test-owned temporary directory. The directory is removed when the fixture closes. This keeps test behavior close to production storage while still making each test isolated. Supported storage modes: - `temporaryRocksDbStorage()`: default, real RocksDB, test-owned cleanup. - `persistentRocksDbStorage(path)`: real RocksDB at a caller-owned path. Testkit devnet intentionally does not expose the runtime's in-memory storage mode. RocksDB is required for the same ledger-state, epoch-parameter tracking, snapshot, and restore behavior used by the regular devnet profile. Always close `YanoDevnetTestKit` directly or let `YanoDevnetExtension` do it for the test. ## JUnit Usage ```java import org.yanoproject.testkit.devnet.YanoDevnetExtension; import org.yanoproject.testkit.devnet.YanoDevnetTestKit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; class MyDevnetTest { @RegisterExtension static YanoDevnetExtension yano = YanoDevnetExtension.devnet() .startNode() .blockTimeMillis(200) .epochLength(120); @Test void fundsWallet(YanoDevnetTestKit kit) { var wallet = kit.wallets().newWallet(); kit.faucet().fund(wallet.address(), 10_000_000L); kit.time().advanceSlots(1); kit.assertions().nodeIsRunning() .wallet(wallet) .hasAtLeast(10_000_000L); } } ``` ## Plain Java Usage ```java import org.yanoproject.testkit.devnet.YanoDevnetTestConfig; import org.yanoproject.testkit.devnet.YanoDevnetTestKit; try (YanoDevnetTestConfig config = YanoDevnetTestConfig.builder() .temporaryRocksDbStorage() .blockTimeMillis(200) .build(); YanoDevnetTestKit kit = YanoDevnetTestKit.devnet(config)) { kit.start(); var snapshot = kit.snapshots().create("initial"); kit.time().advanceSlots(10); kit.snapshots().restore(snapshot.name()); } ``` ## External Process Helpers The `external` package is for slower compatibility tests that need process boundaries: - `YanoAppProcess` starts the Quarkus app jar and talks to its HTTP API. - `HaskellCardanoNodeProcess` starts a Haskell `cardano-node` against a Yano devnet n2n port. - `YanoGenesisFiles` copies or adapts genesis files for app/Haskell process tests. These helpers are opt-in. Normal JUnit integration tests should prefer the in-process `YanoDevnetTestKit`. ## Boundaries The testkit does not expose `RuntimeNode`, raw `ChainState`, RocksDB handles, or maintenance gates. Tests interact through the public `Yano` role interfaces and focused helper facades. Use `testkit-ccl` when a test needs a Cardano Client Lib `BackendService`. --- # JavaScript & TypeScript testkit Manage a native devnet process from JavaScript tests. Canonical URL: https://getyano.dev/develop/javascript-testkit/ `@bloxbean/yano-testkit` starts a local native Yano devnet, waits for HTTP readiness, and returns connection details and helpers. Its npm scope is unchanged by the Java namespace rename. ```bash npm install --save-dev @bloxbean/yano-testkit ``` Use Node.js 20.8 or later for the testkit. Platform packages cover Linux x64/arm64, macOS arm64, and Windows x64. A matching published platform package or a locally built native binary is required. ## Own the lifecycle ```js import { startYanoDevnet } from '@bloxbean/yano-testkit'; const yano = await startYanoDevnet(); try { const response = await fetch(new URL('node/tip', yano.apiBaseUrl)); if (!response.ok) throw new Error(`Tip query failed: ${response.status}`); console.log(await response.json()); } finally { await yano.stop(); } ``` ## Vitest ```js import { describe, expect, test } from 'vitest'; import { yanoDevnet } from '@bloxbean/yano-testkit/vitest'; const yano = yanoDevnet(); describe('Cardano integration', () => { test('reads the chain tip', async () => { const response = await fetch(new URL('node/tip', yano.apiBaseUrl)); expect(response.ok).toBe(true); }); }); ``` The helper starts the process in `beforeAll` and stops it in `afterAll`. ## Downloaded binary and storage ```js const yano = await startYanoDevnet({ binaryPath: '/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64/yano', cwd: '/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64', blockTimeMillis: 200, httpPort: 0, n2nPort: 0, }); ``` The example uses the downloaded macOS arm64 archive; substitute your own platform directory and use `yano.exe` on Windows. Match the testkit version to the binary; newer helpers can require newer endpoints. Port `0` allocates an available port. The default temporary RocksDB mode isolates each run; persistent mode is available when a test needs to retain state. Set `YANO_TESTKIT_BINARY` to override binary discovery. The fixture can fund and query addresses, but wallet creation and signing stay in your application. See [the complete JavaScript guide](/reference/javascript/) for faucet units, snapshots, time controls, and transaction helpers. --- # Snapshots, rollback & time Make local Cardano history reproducible in development and tests. Canonical URL: https://getyano.dev/develop/time-travel/ Yano's devnet controls let tests create history, rewind it, and explore epoch boundaries. They require a devnet-capable recipe and are not general public-network administration tools. ## Snapshots in a Java test ```java import org.yanoproject.testkit.devnet.YanoDevnetTestConfig; import org.yanoproject.testkit.devnet.YanoDevnetTestKit; try (YanoDevnetTestConfig config = YanoDevnetTestConfig.builder() .temporaryRocksDbStorage().blockTimeMillis(200).build(); YanoDevnetTestKit kit = YanoDevnetTestKit.devnet(config)) { kit.start(); var baseline = kit.snapshots().create("baseline"); kit.time().advanceSlots(10); kit.snapshots().restore(baseline.name()); } ``` ## Start in the past From your extracted [JVM release](/start/installation/) directory: ```bash java -Dquarkus.profile=devnet \ -Dyano.block-producer.past-time-travel-mode=true \ -jar yano.jar ``` Production is deferred until you shift the genesis: ```bash curl -fsS -X POST http://localhost:7070/api/v1/devnet/epochs/shift \ -H 'Content-Type: application/json' -d '{"epochs":4}' curl -fsS -X POST http://localhost:7070/api/v1/devnet/epochs/catch-up ``` The shift starts the chain in the past; catch-up moves toward wall-clock and live production. Use fresh isolated storage for a new scenario. Timing derives from genesis; sparse backfill can be configured with `yano.block-producer.backfill-block-interval-slots`. Consult source configuration and compatibility tests before changing that interval. ## Trigger a rollback ```bash curl -fsS -X POST http://localhost:7070/api/v1/devnet/rollback \ -H 'Content-Type: application/json' -d '{"count":3}' ``` Specify exactly one of `count`, `slot`, or `blockNumber`. The operation uses the normal state rollback path and notifies downstream N2N clients. Use it to test whether an indexer or application reverses observations correctly. --- # Query and submit transactions Use the REST API with your existing Cardano transaction library. Canonical URL: https://getyano.dev/develop/transactions/ Yano supplies chain queries, transaction submission, and script evaluation. Your application or Cardano library owns address generation, keys, transaction construction, and signing. For provider setup with CCL, MeshJS, or Evolution SDK, start with [Build with any SDK](/develop/blockfrost/). ## Query spendable outputs Replace `
` with a real address on your selected network: ```bash curl -fsS 'http://localhost:7070/api/v1/addresses/
/utxos' curl -fsS http://localhost:7070/api/v1/epochs/latest/parameters ``` Preserve lovelace and native-asset quantities without floating-point rounding. Build and sign using the returned UTxOs and protocol parameters. ## Submit signed CBOR ```bash curl -fsS -X POST http://localhost:7070/api/v1/tx/submit \ -H 'Content-Type: application/cbor' \ --data-binary @tx.cbor ``` Hex-encoded CBOR is also accepted with `Content-Type: text/plain`. A submission response does not prove inclusion. Query or await confirmation and handle rollback in your application. ## Evaluation The evaluation route is `/api/v1/utils/txs/evaluate`. Use the running node's [OpenAPI document](/reference/http-api/) for its accepted request/response shapes. The launcher selects Aiken for the JVM and Scalus for native execution; `yano.block-producer.script-evaluator` can override the choice where supported. Aiken's JNA evaluator is not supported by native image. ## Integration choices - Java: use Cardano Client Lib; [the CCL testkit adapter](/develop/ccl/) provides an in-process backend for tests. - JavaScript and TypeScript: use the [JavaScript testkit](/develop/javascript-testkit/) alongside your transaction library. - Indexers: consume N2N blocks on the configured server port and handle rollbacks. Some REST shapes are Blockfrost-compatible. This does not mean every Blockfrost API is implemented. Inspect the endpoints and schemas of the artifact you run. --- # Historical archive Enable Yano's DuckLake archive and query its history with DuckDB. Canonical URL: https://getyano.dev/node/history/ Yano can project finalized chain history into **DuckLake** for analytics, reporting, and application-specific indexes. DuckLake keeps its metadata in SQLite and its table data in Parquet, so you can query the archive directly with DuckDB without adding another database service. The archive is optional and runs outside authoritative block application. Yano first records a canonical projection outbox in RocksDB, then drains eligible data into DuckLake after the configured finality gate. ## Enable the archive at fresh sync From a JVM distribution, start a new node with the `projection` profile: ```bash ./yano.sh start:preprod,projection ``` History is disabled by default and is not supported by the native distribution. Enable it with empty node storage so Yano can project from genesis. Turning it on for an already populated ordinary node does not backfill the missing past. The `wallet` profile enables wallet indexes but does not enable the archive. Use `projection` when you need historical data. Block sections cover transactions, UTxO history, account events, and address transactions. Section selection becomes part of the archive identity and cannot be changed retroactively on a populated archive. Epoch artifacts cover rewards, epoch stake, DRep distribution, Ada pots, and governance proposal status. Newly enabled epoch artifacts collect data prospectively from their enrollment point. ## Find the history files The packaged profile writes to `./history` by default. Set `YANO_HISTORY_DIR` before starting Yano to use another location: ```bash export YANO_HISTORY_DIR=/var/lib/yano/history ./yano.sh start:preprod,projection ``` The directory contains: ```text history/ ├── ducklake-catalog.sqlite ├── ducklake-catalog.sqlite.tx-locator.sqlite ├── ducklake-data/ └── tmp/ ``` `ducklake-catalog.sqlite` holds DuckLake metadata, while `ducklake-data/` contains the managed Parquet data files. The transaction locator is a rebuildable Yano sidecar. It is not the history database. ## Connect with DuckDB Install the [DuckDB CLI](https://duckdb.org/docs/stable/clients/cli/overview.html), start an in-memory session, and install the DuckLake and SQLite extensions. `INSTALL` downloads each extension once for the installed DuckDB version; later sessions only need `LOAD`. ```bash duckdb ``` ```sql INSTALL ducklake; INSTALL sqlite; LOAD ducklake; LOAD sqlite; ``` Attach the Yano archive in read-only mode. Replace both paths with absolute paths to the same history directory: ```sql ATTACH 'ducklake:sqlite:/var/lib/yano/history/ducklake-catalog.sqlite' AS history_lake ( DATA_PATH '/var/lib/yano/history/ducklake-data', DATA_INLINING_ROW_LIMIT 0, READ_ONLY ); ``` `READ_ONLY` keeps Yano as the only writer. A separate DuckDB process can query the archive while projection continues; a query may briefly wait if SQLite is committing catalog metadata. Do not use `read_parquet('ducklake-data/**/*.parquet')`. DuckLake uses its catalog to select the files that belong to a snapshot and to exclude obsolete files. Query through `history_lake` instead. ## Discover tables and coverage List the relations that are present and inspect their columns: ```sql SHOW TABLES FROM history_lake; DESCRIBE history_lake.transactions; DESCRIBE history_lake.unspent_outputs; ``` The configured projection sections determine which data tables exist. Common physical tables and derived views include: | Data | Relation | | ------------------------------------ | ------------------------------ | | Transactions | `transactions` | | Output creation and spending | `output_lifecycle` | | Current unspent outputs | `unspent_outputs` | | Address asset movement | `address_asset_flow` | | Stake and delegation events | `account_events` | | Address participation by transaction | `address_transactions` | | Epoch rewards | `rewards` | | Epoch stake | `epoch_stakes` | | DRep distribution | `drep_distributions` | | Ada pots | `ada_pots` | | Governance proposal status | `governance_proposal_statuses` | Check the last committed block before treating query results as complete: ```sql SELECT last_block, last_slot, lower(hex(last_block_hash)) AS last_block_hash, committed_at FROM history_lake.projection_receipts ORDER BY last_block DESC LIMIT 1; ``` The archive normally trails the node tip because it projects only data eligible under its finality policy. Epoch datasets also have their own enrollment and coverage records. Inspect `projection_artifact_enrollment`, `projection_epoch_coverage`, and `projection_epoch_gap_interval` before assuming every historical epoch is present. ## Query chain history Hashes and raw credentials are stored as blobs. Use `lower(hex(column))` for their familiar hexadecimal form. Block times are Unix timestamps in seconds, and monetary amounts are lovelace. Find the latest archived transactions: ```sql SELECT lower(hex(tx_hash)) AS tx_hash, block_number, slot, epoch, to_timestamp(block_time) AS block_time, valid, fee FROM history_lake.transactions ORDER BY block_number DESC, tx_index DESC LIMIT 20; ``` Find unspent outputs at an address as of the attached archive snapshot: ```sql SELECT lower(hex(tx_hash)) AS tx_hash, output_index, lovelace, block_number, slot FROM history_lake.unspent_outputs WHERE address = 'addr1...' ORDER BY block_number DESC, output_index; ``` Follow received and spent assets for an address: ```sql SELECT direction, lower(hex(tx_hash)) AS tx_hash, output_index, quantity, is_lovelace, lower(hex(policy_id)) AS policy_id, lower(hex(asset_name)) AS asset_name, block_number, slot FROM history_lake.address_asset_flow WHERE address = 'addr1...' ORDER BY block_number, tx_hash, output_index; ``` Read reward history for a stake address when the `reward:v1` epoch artifact is enabled: ```sql SELECT epoch, reward_type, amount, spendable_epoch, lower(hex(pool_hash)) AS pool_hash FROM history_lake.rewards WHERE stake_address = 'stake1...' ORDER BY epoch DESC, reward_type; ``` Add `epoch`, `block_number`, or `slot` predicates to large queries. Historical tables with an `epoch` column are partitioned by epoch, so bounded queries scan less data. ## Pin a consistent snapshot A normal read-only attachment follows the archive as Yano commits new projection batches. For a multi-query report that must use one immutable view, record the current snapshot and reattach at that version: ```sql SELECT id FROM history_lake.current_snapshot(); -- Suppose the returned snapshot ID is 42. DETACH history_lake; ATTACH 'ducklake:sqlite:/var/lib/yano/history/ducklake-catalog.sqlite' AS history_lake ( DATA_PATH '/var/lib/yano/history/ducklake-data', DATA_INLINING_ROW_LIMIT 0, SNAPSHOT_VERSION 42, READ_ONLY ); ``` Retained snapshots are finite; the packaged profile keeps them for 168 hours by default. Finish long-running analysis before its pinned snapshot is cleaned up, or increase `YANO_PROJECTION_SNAPSHOT_RETENTION_HOURS` before starting the node. ## Export query results DuckDB can materialize a bounded result without modifying the Yano archive: ```sql COPY ( SELECT * FROM history_lake.transactions WHERE epoch BETWEEN 500 AND 510 ) TO 'transactions-500-510.parquet' (FORMAT PARQUET); ``` Use `FORMAT CSV, HEADER` instead when a downstream tool needs CSV. Detach when the session is complete: ```sql DETACH history_lake; ``` For the complete relation and column contract, see the [DuckLake projection schema](https://github.com/bloxbean/yano/blob/main/docs/archive/DUCKLAKE_PROJECTION_SCHEMA.md). For archive selection, retention, maintenance, and disk thresholds, see the [configuration catalog](/reference/configuration-catalog/) and Yano's `/q/openapi-history` endpoint. When moving or backing up an archive, keep the catalog and `ducklake-data/` together from the same consistent point. A catalog alone contains metadata, not the historical rows. --- # Ledger state and storage Understand local state, rollback, filtering, pruning, and partial bootstrap. Canonical URL: https://getyano.dev/node/ledger/ Yano uses **RocksDB** for authoritative node state. Depending on configuration, it tracks UTxOs, accounts, stakes, delegations, rewards, epoch snapshots, protocol parameters, and Conway governance state. ## Rollback is part of the model A chain can reorganize. State and derived indexes need to follow the canonical chain backward as well as forward. Keep consumers aware of rollback; a previously seen block or transaction is not automatically a permanent application fact. Read [account state and rollback](/reference/account-state/) for the state lifecycle and epoch-boundary recovery behavior. ## Storage controls solve different problems | Control | Effect | Consequence | | --- | --- | --- | | `yano.storage.path` | Node RocksDB directory | One node process per database | | `yano.app-chain.storage.path` | Separate app-chain RocksDB root | Back up app-chain identity and state together | | `yano.filters.utxo.*` | Persist selected UTxOs | Queries describe the selected subset, not complete network coverage | | `yano.chain.block-body-prune-depth` | Remove older block bodies | Old body-dependent queries and scans may be unavailable | | `yano.rollback-retention-epochs` | Bound retained rollback material | Must match your recovery requirements | Pruning bodies and retaining rollback data are separate policies. Do not assume preserved headers mean all historical bodies are still available to downstream clients. ## Partial bootstrap The optional bootstrap mode can obtain selected initial UTxOs through providers such as Blockfrost or Koios instead of replaying all history. This is a **partial-state** workflow. The runtime disables derived state that requires full history in partial bootstrap mode: account state, stake balance indexes, epoch parameter tracking, rewards, Ada pots, governance, and epoch snapshots. UTxO bootstrap remains available. It is not a shortcut to a complete historical node. See the [configuration catalog](/reference/configuration-catalog/) for `yano.bootstrap.*` settings. Use fresh storage when changing the network or completeness model. --- # Mempool-aware UTxOs Understand unconfirmed transactions and spendable-output views. Canonical URL: https://getyano.dev/node/mempool/ The following endpoints accept `include_mempool=true` (default: `false`): - `/api/v1/addresses/{address}/utxos` - `/api/v1/addresses/{address}/utxos/{asset}` - `/api/v1/credentials/{paymentCredential}/utxos` - `/api/v1/utxos/{txHash}/{index}` (existing single-output lookup) For example: ```sh curl 'http://localhost:7070/api/v1/addresses/ADDRESS/utxos?include_mempool=true&page=1&count=20&order=asc' ``` Listings exclude outputs consumed by pending transactions and include matching unspent pending outputs (including change). Intermediate outputs already spent by pending children are excluded. Address listings also support the existing `use_payment_credential=true` selector. Credential queries accept a payment hash or an address, as before. Asset matching, deduplication and ordering happen before pagination, so excluded outputs do not leave holes in pages. Ascending overlay order is confirmed outputs by slot, transaction hash, output index, followed by pending outputs ordered by hash and index. `order=desc` reverses that order. Pending outputs have `block: null`, using the existing single-output mempool DTO representation; they are not confirmed balances. Requests without the flag retain existing confirmed-only behavior, except that all three listing endpoints now reject `count > 100` with HTTP 400. Overlay queries walk one snapshot-backed iterator (forward or reverse), stopping as soon as the page is full. Point lookups for deduplication use the same snapshot. No re-paging or whole-subject materialization occurs. Memory holds one decoded confirmed output, at most 100 result outputs, and a bounded mempool snapshot. Deep pages and selective filters still scan earlier rows, but never re-scan them within a request. There is no new persistent index or sync-write overhead. Resource limits are deliberately conservative: - At most two concurrent overlay queries and two open storage read views per store. - At most 100,000 scanned confirmed index rows and a five-second cooperative read deadline. - Mempool capture refuses pools with more than 100,000 produced outputs or spent entries. - At most 1,000 matching pending outputs and 8 MiB of their source transaction bytes (counted conservatively per selected output); this is not a measurement of Java heap bytes. Subject hashes are computed at transaction projection, not per query. Payloads are copied only for matching outputs and only after releasing the admission lock. Busy admission, query saturation, time/work/snapshot limits, unavailable storage, or a canonical-hash change before response completion return HTTP 503, never a silently truncated successful page. Retry with backoff; use smaller pages where applicable. A subject exceeding snapshot limits needs confirmation/eviction before overlay queries can succeed. These bounds limit query amplification; they do not constitute a measured whole-node native 1.5 GB heap guarantee. Results are transient, not a reservation; chain and mempool changes can shift pagination between requests. Wallets must still reserve their own in-flight inputs and handle submission conflicts. Unsupported overlay listings return HTTP 503 rather than silently falling back to confirmed-only results. Eviction and relay behavior are unchanged. --- # Connect to Cardano Configure public-network profiles and upstream peers. Canonical URL: https://getyano.dev/node/networks/ Yano defaults to Cardano **preprod**, with the upstream host `preprod-node.world.dev.cardano.org`, port `30000`, and network magic `1` in the bundled application configuration. From an extracted distribution: ```bash ./yano.sh start:preprod ``` Other packaged network profiles include `mainnet`, `preview`, and `sanchonet`. Use a **separate storage directory for each network**. Do not point a new network profile at another network's chain state. ## Configure the node Place overrides in `config/application.yml` relative to the working directory. For example: ```yaml yano: storage: path: ./chainstate-preprod remote: host: preprod-node.world.dev.cardano.org port: 30000 ``` Profiles compose with a comma-separated list. Network profiles set genesis/network values; additional profiles enable optional behavior: ```bash ./yano.sh start:preprod,relay ./yano.sh start:preprod,wallet ``` The first enables the packaged relay profile. The second enables wallet discovery indexes and requires a fresh sync for complete history. ## Observe synchronization ```bash curl -fsS http://localhost:7070/api/v1/status curl -fsS http://localhost:7070/api/v1/node/tip ``` Readiness and sync completion are different. A healthy process can still be catching up. Compare the local tip, remote tip, peer state, and recent progress before relying on current data. The N2N server normally listens on `13337`. A downstream consumer must use matching network magic and genesis. For a local devnet, that magic is `42`; do not connect a public-network consumer using it. ## Upstream selection and validation Yano supports trusted-single, trusted-failover, static-multi, and p2p-relay modes. See [upstream configuration](/node/upstream/) for their tradeoffs and the current validation boundary. --- # Upstream peers and validation Choose peer selection and inspect validation without overstating trust. Canonical URL: https://getyano.dev/node/upstream/ Upstream mode decides where headers and block bodies come from. Configure it under `yano.upstream`. | Mode | Purpose | | --- | --- | | `trusted-single` | One selected upstream; simplest synchronization setup | | `trusted-failover` | A configured trusted set with failover | | `static-multi` | Multiple explicitly configured peers and header observations | | `p2p-relay` | Peer governor and discovery for relay experiments | Packaged profiles include `trusted-peers`, `static-multi`, and `relay`. Read their exact settings in the [configuration catalog](/reference/configuration-catalog/). A mode or profile is not a claim that all public-network consensus rules are enforced. ## Header validation The current bundled configuration describes these levels: | Level | Additional checks | | --- | --- | | `none` | Default; upstream header validation disabled | | `structural` | Header structure | | `header-signature` | Structure, KES, and operational-certificate signatures | | `praos-lite` | Shelley+ VRF checks when epoch nonce tracking is available | | `praos-ledger` | Ledger-view checks and persisted operational-certificate counters when available | `yano.upstream.validation.body-level` currently supports `none`. Do not interpret the header presets as complete transaction/body or Cardano consensus validation. Validation start settings can delay checks to an era or checkpoint. Packaged network profiles carry network-specific anchors. Preserve the profile's genesis and checkpoint identity when selecting a validation preset. ## Operational-certificate counters `yano.upstream.validation.opcert-counter-mode` supports `none`, `compat`, and `strict`. `compat` checks stored counters when present; `strict` applies the registered-issuer baseline when stored state is absent. Understand the coverage of the database before enabling stricter checks. ## Forwarding and diffusion Upstream transaction forwarding is configured separately from transaction diffusion. The current base config enables `yano.tx.diffusion.enabled` and bounds transactions, bytes, and peer cooldown. Use status and logs to confirm actual active peers and behavior; do not infer relay completeness from a single flag. ## Troubleshooting If progress stalls, inspect the selected peer, recovery reason, validation failures, and retained history. Check network identity and connectivity before changing validation or storage. See [troubleshooting](/operate/troubleshooting/). --- # Wallet discovery & scans Coverage-aware first-seen lookup and streaming wallet scans. Canonical URL: https://getyano.dev/node/wallet-indexes/ These indexes are preview functionality. The general node defaults keep both indexes disabled. The wallet profile enables both and no longer enables archival history projection. Production resource validation remains outstanding. ## Upgrading from the pre-contributor wallet index The contributor-based wallet index requires a **fresh sync database**. Existing wallet tables do not contain the new host-owned availability/rollback metadata; their presence does not prove complete index history. Upgrading the executable alone will leave these indexes unavailable, and later blocks will not repair them. Startup warns when existing wallet history is unavailable under the new gate. Stop the node, retain the old database as a backup, configure a new storage path, and sync with the wallet flags enabled from the beginning. Do not run two nodes against the same database. There is no migration or automatic backfill in preview. ## Configuration Enable the desired capabilities **before the first sync into a fresh database**: ```yaml yano: address-first-seen: enabled: true scan: index: enabled: true max-concurrent: 2 utxo: enabled: true filters: utxo: enabled: false chain: block-body-prune-depth: 0 ``` External indexes use `yano.utxo.index-contributors`, separately from the built-in wallet flags above. A JAR in `plugins/` is discovered but does not automatically activate its contributor: ```yaml yano: utxo: enabled: true index-contributors: - type: example.output-index enabled: true config: {} ``` `type` is the provider selector; `enabled` defaults to false; `config` contains plugin-specific scalar values. `wallet` is reserved and cannot appear in this list. Plugin allow/deny policy still applies. Restart after changing selection, and use a fresh sync for complete history. See the [external example](https://github.com/bloxbean/yano/blob/fd7fe406e9364689a3e829b79f82707488cebf1e/examples/utxo-output-index/README.md). Address-decoding failures retain one representative error per feature and block, not a complete list of bad addresses. Good addresses in that block are still indexed. Filter scans report incomplete blocks; first-seen queries cannot claim historical completeness while a relevant error remains. `yano.filters.utxo.enabled` controls selective UTxO storage, which must be disabled for these indexes. It is unrelated to the new per-block scan filter. Plugin UTxO filters are likewise incompatible. No archival/history backend is needed. The features are independent: first-seen needs its own continuous history, while scans need their filters and retained canonical block bodies. First-seen survives spending and ordinary body pruning. Scan queries that cross missing coverage or unavailable bodies fail; they do not silently scan all bodies as a fallback. Existing databases do not acquire complete coverage by enabling a flag. A fresh sync is required. Turning a flag off while canonical blocks advance breaks its continuity. Turning it off and back on without missing blocks can preserve the existing proof. There is no index backfill or automatic historical repair. ## Canonical coordinates and coverage Coordinates are JSON objects with `blockNumber`, `slot`, and a lowercase 64-digit hex `blockHash`. Origin is exactly: ```json {"blockNumber":-1,"slot":0,"blockHash":"0000000000000000000000000000000000000000000000000000000000000000"} ``` Slot zero is a real slot. Origin is distinguished by block number -1. Coverage contains `enabled`, `completeFromOrigin`, `from`, `indexedThrough`, `identity`, and `unavailableReason`. Both the indexed coordinate and live tip are reported so a client can detect lag. A query is authoritative only through its indexed point. ## Exact-address first-seen ```http GET /api/v1/addresses/{address}/first-seen ``` The response contains `firstSeenSlot`, `coverage`, and `liveTip`. A number, including zero, means the complete decoded address received an effective output at that slot. `null` means it has never received one through `indexedThrough`, only when origin-to-point completeness is established. Addresses sharing a payment credential remain distinct. Valid outputs, collateral returns and applicable genesis outputs count; ordinary outputs of phase-2-invalid transactions do not. Malformed addresses return 400. Disabled, missing, incompatible or incomplete indexes return 503, never an authoritative null or a misleading positive slot. A wallet must not count a null toward its discovery gap if the index is behind the live tip. Receive and change branches retain independent discovery counters. ## Streaming transaction scan ```http POST /api/v1/scan Content-Type: application/json Accept: application/x-ndjson ``` Example initial request; replace the credential hash with the account's stake hash: ```json { "version": 1, "credentials": [{"role":"stake","type":"key","hash":"01010101010101010101010101010101010101010101010101010101"}], "after": {"blockNumber":-1,"slot":0,"blockHash":"0000000000000000000000000000000000000000000000000000000000000000"}, "knownOutputs": [] } ``` `after` is required and exclusive. Optional `to` is inclusive and must be within available coverage; omitted `to` pins the current indexed end. Credentials contain `role` (`payment`, `stake`, or the scoped `drep` support below), `type` (`key` or `script`), and a 28-byte hex hash. Supply 1–200 credentials. The response is newline-delimited JSON, with these record types: - `ready`: the effective end in `point`, plus coverage and live tip. - `genesis`: applicable genesis outputs for an origin scan, without a transaction hash. - `transaction`: confirmed `txHash`, canonical `point`, `blockTime` (Unix seconds), `valid`, effective `inputs` and `outputs`. - `progress`: the most recently processed point. - `warning`: a block whose wallet indexing or scan extraction is incomplete, with its canonical `point`, diagnostic `error`, and `complete: false`. - `done`: successful completion at the exact pinned end, with `complete: true`. - `incomplete`: terminal partial results at the pinned end, with `complete: false`. No `done` follows this record. Do not advance a durable recovery cursor. - `rollback` or `error`: unsuccessful termination; do not advance durable state. Inputs are outpoints (`txHash`, `index`). Outputs include their outpoint, address, lovelace, assets (`policyId`, `assetName`, `quantity`), creation slot/block/hash, collateral-return flag, and available datum/reference-script fields. A matching transaction includes all its effective inputs and outputs, including unrelated addresses. Clients must independently check ownership before counting funds. Preserve asset quantities as arbitrary-precision integers. An origin scan seeds applicable genesis funds. A resumed scan must supply `knownOutputs`: the **complete relevant unspent output set at `after`**, using the same output objects from prior records. The maximum is 10,000 tracked outputs. Save the cursor and this state atomically, only after validating `done` with `complete: true`. The node checks structural validity, creation bounds, query relevance and duplicate outpoints; it cannot prove that a caller did not deliberately omit a relevant output. A cursor alone cannot establish outgoing attribution. Never resume from a progress record without the matching complete state. The server limits active scans with `yano.scan.max-concurrent` (default 2, range 1–16). Blocking streaming runs off the HTTP I/O thread; output backpressure slows the scan. Disconnect/cancellation releases request state. Invalid requests return 400, orphaned cursors 409, and unavailable coverage/bodies/capacity 503 when detected before streaming. Failures discovered after headers produce a terminal error or rollback record when possible. EOF, a timeout, or an incomplete final line is **not** successful completion, even if some transactions arrived. Known per-block extraction failures do not stop indexing other addresses or later blocks. The same atomic block batch stores successfully decoded first-seen rows, a partial credential filter, and a diagnostic in the `wallet_index_errors` RocksDB column family. Keys are index-kind plus block number; values contain the block hash and a representative failure reason. The repair unit is the full block, so this is not a separate queue entry for every failed output. Canonical block bodies retain the original transaction/output details. Errors are retained across restarts and undo pruning, and removed with reverted blocks in the atomic rollback batch. A scan crossing a recorded error emits a warning and reads that block regardless of filter matches, returning whatever confirmed matches it can extract. Affected streams finish with `incomplete`; ranges entirely before or after the error can finish with `done` (resumed scans still require complete `knownOutputs`). The `ready.coverage` coordinates describe structural index continuity; only the terminal record establishes whether the requested scan was complete. First-seen answers remain unavailable while their history contains unresolved first-seen errors. There is no automatic repair operation yet. Upgrading does not reconstruct filters or origin completeness already lost by older versions. Unknown gaps, missing block bodies, corrupt metadata and reorgs still fail closed rather than returning a successful partial scan. On reorg, discard uncommitted stream changes. Retry an earlier saved canonical cursor together with its matching history/outpoint snapshot. If none survives, restart at origin. Hash validation catches a reorg even when the client missed its notification. The wallet currently retains two durable boundaries and can restart at origin for deeper reorgs; it does not mix an earlier cursor with newer outpoints. Changing chain identity invalidates that saved state. ## Scope and resources Stake scanning is suitable for ordinary CIP-1852 base addresses. It does not cover enterprise addresses or resolve pointer stake credentials. A stake match does not prove payment ownership. DRep support covers the named certificate subjects, not all votes or governance events. Byron wallet recovery, epoch rewards, nontransaction refunds and arbitrary historical transaction-by-hash lookup are outside this API. No global transaction-location index is created. Address decoding and payment/stake credential extraction use Cardano Client Lib (CCL), including its `ByronAddress` decoder. Historical addresses may contain trailing bytes: these are retained for exact-address identity without imposing a separate strict address parser. Pointer addresses contribute their payment credential only; stake-pointer resolution remains unsupported and does not invalidate scan coverage. Planning estimates remain 2–3 GB for filters and roughly 100–200 bytes per distinct ever-seen address, plus undo/metadata and temporary WAL/compaction headroom. These costs are additive; retaining bodies also costs disk if switching from a pruning configuration. They are planning estimates, not measured production resource recommendations. Validate disk growth, retained bodies, and scan workload for your deployment. --- # Console & observability Explore the embedded console and persistent metrics. Canonical URL: https://getyano.dev/operate/console/ The Yano distribution embeds the unified console. Start Yano and open `http://127.0.0.1:7070/ui/`. The node, app-chain, plugin, and observability routes are real static paths, so `/ui/status/`, `/ui/app-chain/`, `/ui/plugins/`, and `/ui/observability/` can be bookmarked directly. The connection panel accepts a Yano API base and an optional API key. Keys remain in memory unless the operator explicitly opts into browser-local persistence. The plugin operations route is stricter: it ignores `?api=` and saved base overrides, trusts only `/ui/plugins/api-prefix.json`, and retains its privileged key in the current tab's `sessionStorage`. ## Standalone hosting Use the embedded console in your installed release where available. A separately hosted console is an advanced deployment; see [the source-build page](/contribute/build-from-source/#standalone-console-artifact) to produce its ZIP and extract it below `/ui` on a static server. The archive has no Node.js runtime and contains no credentials. For the plugin page, route `/api/v1` to the Yano node on the same origin (the archive's fixed plugin discovery document intentionally does not accept a query-controlled remote base). For the other console routes, a cross-origin node can be selected from the connection panel. CORS is disabled in Yano by default. Enable it only for the exact console origin, for example: ```yaml quarkus: http: cors: enabled: true origins: https://console.example.com methods: GET,POST,OPTIONS headers: Accept,Content-Type,X-API-Key ``` The equivalent environment settings are `YANO_HTTP_CORS_ENABLED=true`, `QUARKUS_HTTP_CORS_ORIGINS=https://console.example.com`, `QUARKUS_HTTP_CORS_METHODS=GET,POST,OPTIONS`, and `QUARKUS_HTTP_CORS_HEADERS=Accept,Content-Type,X-API-Key`. Never configure `origins: "*"` when API keys are used. CORS controls which browsers may call the node; it does not replace or weaken Yano's API-key checks. The fetch-based app-chain stream uses the same CORS and `X-API-Key` rules as normal JSON requests. ## Historical metrics Without extra services, the node and app-chain charts retain up to one hour of bounded history in the current browser tab. This short history survives a refresh but is not a monitoring database. For persistent local history, the JVM and native distributions include a pinned Prometheus companion: ```bash ./yano.sh observability start ./yano.sh observability status ./yano.sh observability stop ./yano.sh observability clean --yes ``` `start` discovers a running maintained local app-chain cluster, or defaults to `http://127.0.0.1:7070`. Repeat `--target ` to replace discovery. It prints a preconfigured `/ui/observability/?metrics=...` link. The default retention is 15 days / 2 GB; `stop` preserves history and only the explicit `clean --yes` command removes the marked state and labeled volume. Docker Compose v2 is required only for this optional mode. Production operators can set an existing Prometheus-compatible origin in the Connection panel. The console issues only its built-in read-only queries. Configure exact-origin CORS and access control on that endpoint. Its optional bearer credential is retained only in the current tab, is bound to the exact metrics origin, is never stored with or substituted for the Yano API key, and is never sent to the node. ## Capability panels The App chains page discovers optional capabilities from the selected chain's status and, when authorized, confirms first-party bundles against the plugin catalog. It does not assume that every deployment has effects or role-aware approvals. - An effects-enabled chain shows emitted effects, statistics, composed proofs, and confirmed requeue/cancel actions. Mutations require the appropriate operator API key. - A `role-approvals` or `role-evidence` chain queries the proposal through the root-fixed committed-query surface and adds the decoded domain projection when available. The committed result remains visible if that convenience projection is unavailable. - Every running app chain exposes **Operations**, **Capabilities**, and **Proofs**. Proofs is divided into **Message**, **State**, **Import and verify**, and **Advanced** workflows. The browser can SHA-256 the exact finalized message payload or included proof value and compare each with its own optional expected digest. A proof can be loaded at the current tip, loaded at the exact height of the latest anchor confirmed by this node, or pasted as JSON. **Verify proof** sends only the bounded key/value/proof/root fields to the connected node's release-matched MPF verifier and reports three facts independently: - whether the MPF path is mathematically valid for the expected root; - whether the proof envelope's root and optional height match that source; - where the expected root came from. The State workflow discovers typed proof subjects from the chain's immutable capability manifest and keeps physical state keys in Advanced. For extension-specific trust labels and proof workflows, see the [Yano X Proof Lab guide](https://github.com/bloxbean/yano-x/blob/main/docs/appchain/PROOF_LAB.md). `L1-confirmed by this node` means Yano observed the anchor transaction and bound its persisted confirmation back to the exact finalized app block. It is not an independent Cardano lookup. For an audit decision, resolve the shown transaction through an independent Cardano source, validate the metadata or script datum/output, and pin the expected chain, membership, threshold, and script identity. The browser digest remains a byte-integrity check rather than finality, anchor, or MPF verification. Custom component-specific panels remain data-only future catalog work; plugins cannot inject executable console code. ### EUTxO lifecycle An `eutxo-ledger` chain exposes a reviewed EUTxO route at `/ui/app-chain/eutxo/?chain=`. With the node-local `indexer:eutxo-lifecycle` capability, it provides bounded transaction, account, bridge, lineage, and optional validity views. A user can start from an address, L1 deposit outpoint, app-message ID, L2 transaction ID, or withdrawal claim and follow the canonical L1 deposit to L2 activity to L1 payout. The indexer is a derived SQLite projection. The console displays its coverage, checkpoint, finalized height, lag, and bridge-reconciliation diagnostics. If it is disabled or unavailable, the page falls back to the committed transaction-summary API; consensus and bridge execution are unaffected. L1 transaction details are loaded lazily from the connected Yano node and remain optional when retained L1 history is unavailable. The browser does not decode arbitrary Cardano or proof CBOR and never reads the disposable demo journal. The Bridge tab's server-built CIP-30 deposit may select a wallet input that contains ADA plus native assets. The vault output remains ADA-only and every native asset is returned to the depositor as change; signing and submission remain wallet-owned. Operational configuration, endpoints, metrics, rebuild safety, and the measured SQLite support envelope are documented in the [Yano X EUTxO indexer runbook](https://github.com/bloxbean/yano-x/blob/main/ledgers/eutxo/INDEXER_OPERATIONS.md). For local frontend development, run `npm run dev` in `console-ui/frontend`; Vite proxies `/api` and `/q` to `http://127.0.0.1:7070`. --- # Mempool administration Configure and use privileged local mempool eviction. Canonical URL: https://getyano.dev/operate/mempool/ Manual eviction is a recovery tool, not transaction cancellation. Check the chain and upstream node before evicting a transaction that appears stuck. The endpoint is disabled by default, including in the wallet profile. Enable it explicitly and configure a dedicated secret (prefer environment configuration): ```sh export YANO_MEMPOOL_ADMIN_ENABLED=true export YANO_MEMPOOL_ADMIN_API_KEY="$(openssl rand -hex 32)" export QUARKUS_HTTP_HOST=127.0.0.1 ``` The corresponding properties are `yano.mempool.admin.enabled` and `yano.mempool.admin.api-key`. Both enablement and a nonblank key are required; keys must be at least 32 characters; generate a random secret, not a password. Enabling without a sufficiently long key fails closed. The host setting above binds the whole HTTP server to loopback for a locally managed wallet node. The wallet launcher should set it explicitly. For remote administration use HTTPS; never transmit the key over unencrypted public HTTP. Do not expose this key to untrusted wallet dApps. ```sh curl -X DELETE \ -H "X-Admin-API-Key: $YANO_MEMPOOL_ADMIN_API_KEY" \ http://localhost:7070/api/v1/admin/mempool/transactions/TRANSACTION_HASH ``` A successful response contains `txHash`, `evictedTxHashes` (including pending descendants), and a `warning`. An absent transaction returns HTTP 200 with an empty list, so repeated requests are safe. Disabled: 404; missing/wrong key: 401; invalid hash: 400; missing/short configured key, busy admission, or unavailable runtime: 503. Eviction fails fast when either admission lock is busy; retry with backoff. Eviction atomically removes the transaction and its dependent pending transactions from the local mempool, releasing their input reservations and cleaning their output, reference-script, and dependency indexes. Successful administrative requests are logged with the requested hash, total count and up to ten evicted hashes, never the key. It does not alter canonical ledger state or wallet indexes. **This does not cancel a transaction on the network.** The upstream submission API has no cancellation hook: queued/in-flight submissions and transactions already held by peers may still propagate and confirm. A peer may announce an evicted transaction again and it can be readmitted. Eviction is not a blacklist. Eviction also cannot recall a transaction already selected for a locally produced block. Reusing released inputs creates a competing spend, not a guaranteed replacement. Automatic confirmation, conflict, expiry, and retention cleanup remain unchanged. --- # Plugin operations Validate, install, secure, and observe JVM plugins. Canonical URL: https://getyano.dev/operate/plugins/ Yano exposes a host-owned, read-only view of the selected plugin catalog and node-local plugin lifecycle, health, and metrics. The HTTP adapters read a bounded runtime cache; they do not invoke plugin callbacks while serving a request or metrics scrape. ## Protect the operations API The plugin operations endpoints are privileged even though they are `GET` requests. These examples use the default artifact prefix `/api/v1`: ```text GET /api/v1/plugin-operations GET /api/v1/plugin-operations/bundles?after=&limit=<1..100> GET /api/v1/plugin-operations/bundles/ ``` Configure at least one unscoped full key. Prefer environment variables or an external secret provider instead of putting production keys in a committed configuration file: ```bash export YANO_APP_CHAIN_API_KEYS='replace-with-a-long-random-full-key' ``` The equivalent Java properties are: ```properties yano.app-chain.api.keys=replace-with-a-long-random-full-key ``` The key list may also contain topic-scoped submit keys in the form `key=topic-a|topic-b`. Such keys receive `403` from plugin operations; only an unscoped full key is accepted. ```bash curl -H 'X-API-Key: replace-with-a-long-random-full-key' \ http://127.0.0.1:7070/api/v1/plugin-operations ``` The surface fails closed: a missing full-key configuration returns `503`, a missing or invalid request key returns `401`, a scoped key returns `403`, and an unscoped full key returns `200`. API keys are resolved at runtime for JVM/native parity, but the parsed key set is cached; restart the node after rotating keys. Set `YANO_APP_CHAIN_API_AUTH_ENABLED=true` too only when READ and SUBMIT routes must require keys; privileged plugin operations require a full key in either mode. ## Dashboard Open `/ui/plugins/`, enter an unscoped full key, and use **Forget key** when finished. The input is cleared after submit; the key is retained only in JavaScript memory or session storage and is never put in the URL or rendered back into the page. Before enabling credential entry, it discovers the artifact-baked API prefix through the immutable same-origin document `/ui/plugins/api-prefix.json`. It fails closed when that document is missing or invalid. A stored key is bound to the exact verified API prefix; URL query parameters cannot steer its destination. Reverse proxies must serve `/ui/plugins/api-prefix.json` from the same packaged Yano artifact and must not synthesize it from a request header, query parameter, or other client-controlled value. The canonical root prefix `/` is supported and maps the operations API to `/plugin-operations`. The prefix is fixed when the artifact is built. Its only supported input is `-PyanoApiPrefix=` (default `/api/v1`); do not put a public prefix in launch configuration. The path is limited to 256 characters and must be `/` or a canonical absolute path of unescaped `[A-Za-z0-9._~-]+` segments, with no empty, `.` or `..` segment or trailing slash. For a custom prefix, follow [the advanced source-build instructions](/contribute/build-from-source/#artifact-api-prefix). The normal release download uses `/api/v1`. The build generates literal REST configuration, the raw `META-INF/yano-api-prefix-v1` marker, and the immutable dashboard discovery document from that one input. It reserves `quarkus.http.root-path=/`. Runtime-style build inputs for `yano.api-prefix`, `quarkus.resteasy.path`, or `quarkus.http.root-path` are rejected. Launch-time drift in any of those values aborts before node or plugin initialization. The dashboard loads at most 500 bundles, in pages of 100, so one refresh uses at most five inventory requests. It displays an explicit cap indicator at that boundary. The REST API remains cursor-paginated across the full catalog (up to 4,096 bundles) for automation that needs complete inventory. The dashboard is an operator view, not an authenticated ledger proof. A contribution marked `CATALOG VALID · LIFECYCLE NOT OBSERVED` was accepted by the selected catalog but its callback lifecycle has not been observed by the operations registry. Compare `observedContributionCount` with `contributionCount` before treating the runtime view as complete. The summary and paginated inventory remain bounded and omit individual health checks. Expanding one bundle fetches its privileged detail response, including the activation-frozen check ids/descriptions and cached status. `UNKNOWN` means that no valid result has been observed; `stale` retains the last-good status after a timeout, callback failure, or invalid whole-source snapshot. The protected summary also publishes `pluginApiMajor`, the globally monotonic `pluginApiLevel`, and the selected catalog fingerprint. Compare all three when checking a multi-node rollout; a matching fingerprint already commits to the host major/level and each selected manifest's minimum required level. ## Health and metrics Plugin operator health is available at: ```text /q/health/group/plugins ``` It is deliberately separate from node liveness and readiness. An optional plugin can therefore be degraded or down without forcing `/q/health/ready` down. Alert on this health group independently. Prometheus-format metrics are available from `/q/metrics`. Standard families start with `yano.plugin`; custom plugin metrics are mapped into bounded host-owned families with exactly `plugin=` and `metric=` tags rather than request or error text. Health-check ids do not create Prometheus series. Counter and timer exports stay monotonic across runtime generations. Authoritatively absent series are unregistered; explicitly stale sources keep their last-good value and stale status. The app-chain API key does **not** protect `/q/health/group/plugins` or `/q/metrics`. Restrict management endpoints with the deployment listener, firewall, ingress, or authenticated reverse proxy. Do not expose them publicly by relying on the plugin operations credential. ## Validate a plugin before deployment The JVM distribution includes the offline, resource-only catalog inspector: ```bash ./tools/yano-plugins/bin/yano-plugins validate plugins/example.jar ./tools/yano-plugins/bin/yano-plugins inspect --format json plugins/example.jar ``` It validates manifests and service descriptors without loading providers. See [`plugin-catalog/README.md`](https://github.com/bloxbean/yano/blob/fd7fe406e9364689a3e829b79f82707488cebf1e/plugin-catalog/README.md) for policy flags and stable exit codes. Native distributions intentionally omit this JVM tool and cannot load dropped plugin JARs; validate artifacts on a JDK 25 host before including trusted providers at native build time. Each explicit JAR, or all regular files in one exploded artifact, is limited to an aggregate 1 GiB immutable scan snapshot. Inputs over that boundary are rejected before temporary capture and rechecked while streaming. ## Shutdown behavior Yano bounds plugin callbacks and reports stale or failed sources from cached state. Plugins are nevertheless trusted in-process Java code. A malicious or broken callback can ignore interruption indefinitely; the runtime does not use unsafe thread termination. If a provider prevents clean shutdown after the configured grace period, terminate the node process and replace or disable the bundle before restarting. --- # Troubleshooting Diagnose startup, synchronization, unavailable data, and app-chain progress. Canonical URL: https://getyano.dev/operate/troubleshooting/ ## The process will not start Check `java -version` (JDK 25), the working directory, configured storage ownership, and whether ports `7070` or `13337` are already in use. Start the launcher inside the complete extracted distribution; Java is needed only for the JVM ZIP. Do not run two processes against one RocksDB directory. Keep node and app-chain storage, network configuration, and member identity together when moving an installation. ## The node is healthy but still behind Read `/api/v1/status` and `/api/v1/node/tip`. Readiness means the application is ready to serve; public-network synchronization can still be in progress. Look at the local/remote tips, selected peer, recovery reason, rejected validation stage, and last progress in logs. Confirm the genesis/network magic and upstream reachability. Repeatedly restarting or deleting state can hide the original error; preserve logs and configuration first. ## A wallet query returns unavailable Wallet indexes must be enabled before a fresh sync. An existing database does not gain historical completeness when you flip a flag. Scan queries also need retained block bodies. A `503` is not an empty account or an unused address. See [wallet indexes](/node/wallet-indexes/). ## A message is accepted but not finalized `202` is ingress acceptance. Check chain status, configured proposer, member keys, threshold, peer connectivity, expiry, and backpressure. Make sure voting nodes agree on the chain configuration and commitment identity. An idle chain need not produce empty blocks. ## A proof is valid but the claim is still uncertain A valid proof only binds data to its expected root. Establish where that root came from, which chain it belongs to, and whether the associated finality and L1 evidence are independently trusted. See [proofs and anchors](/app-chains/proofs/). ## Report a problem Include the source revision or artifact version, JVM/native platform, enabled profiles, redacted configuration, steps to reproduce, expected behavior, and relevant logs. Do not include signing keys, API keys, or private payloads. Open an issue in [the Yano repository](https://github.com/bloxbean/yano/issues). --- # Versions & upgrades Keep source, artifacts, storage, and plugins compatible. Canonical URL: https://getyano.dev/operate/upgrades/ Yano is pre-release. Pin library, node, verifier, and plugin versions together. Check the [documentation build manifest](/ai/manifest.json) for the source revision and version used to generate these docs. The current Java namespace is `org.yanoproject.*`, and Maven artifacts use group `org.yanoproject`. Do not rename dependency packages such as `com.bloxbean.cardano.client.*` or `com.bloxbean.cardano.yaci.*`: those belong to separate projects. ## Before an upgrade 1. Record the current version, profile configuration, network/genesis identity, and plugin catalog. 2. Stop the process cleanly and keep a recoverable backup of the complete state and associated identities. 3. Check release-specific storage and commitment compatibility. 4. Validate the new artifact in an isolated environment before replacing the running installation. Wallet index upgrades require fresh sync where completeness metadata is absent. History archive sections are selected for a fresh archive. App-chain commitment/profile changes may require a fresh chain; they are not automatically in-place migrations. Never discard a signing journal or reuse an app-chain identity with a newly empty ledger as a recovery shortcut. It may lose persisted consensus or observation locks. See [release migration details](/reference/upgrading/) for concrete compatibility changes. --- # Account state & rollback Account state, rewards, governance, and rollback behavior. Canonical URL: https://getyano.dev/reference/account-state/ Yano's RocksDB account store is the authoritative implementation for stake accounts, stake-pool and DRep delegations, pool lifecycle state, rewards and epoch snapshots. The in-memory implementation exists only for deterministic unit tests; startup never falls back to it when a persistent store is incompatible or cannot be opened. ## Pool retirement at an epoch boundary The boundary order is rewards, SNAP, POOLREAP and governance. SNAP therefore captures the end-of-previous-epoch delegation state before an effective pool retirement changes live state. POOLREAP uses the exact deposit stored when the pool's current lifecycle was created. It credits a registered reward account once, or accounts for an unclaimed refund in the monetary result, and then removes the live pool, retirement, lifecycle-slot and stake-pool-delegation rows. Stake accounts, DRep delegations, pool-parameter history and certificate history remain. Large delegation scans and writes are split into bounded rollback-v1 chunks. A durable progress marker prevents a partial transition from being reported as a complete boundary or a ready chain tip. Startup finishes a valid interrupted transition before UTXO/account reconciliation and sync. ## Chainstate compatibility An empty account store atomically records the current epoch-boundary indexes and the `pool-lifecycle-state-v1` readiness marker. A populated store without that marker, or with an unknown marker value, is rejected without modification using `IncompatibleChainStateException`. There is intentionally no automatic repair or v2 promotion. Incorrect same-block certificate ordering in an older preview chainstate cannot be reconstructed exactly from its live rows. Keep the old directory as a backup if needed and sync into a new chainstate directory. ## Marker inventory These are the format/readiness guards and transient recovery cursors an operator may encounter for account-state lifecycle and its required UTXO index. A permanent marker says that stored data has a known semantic contract; a transient cursor says that a journaled operation must be resumed. | Key | Kind | Written when | If absent, malformed or incompatible | | --- | --- | --- | --- | | `meta.epoch_boundary_state_version` | Format version | Atomically when an empty account store is initialized | A populated pre-marker or unknown version is rejected without writes; retain a backup and resync. | | `meta.snapshot_dereg_index_version` | Permanent semantic-readiness guard | In the same empty-store initialization batch | Missing or incompatible means the credential-major SNAP deregistration input is not trustworthy; startup rejects the store and requires resync. | | `meta.reward_event_index_version` | Permanent semantic-readiness guard | In the same empty-store initialization batch | Missing or incompatible means bounded reward event input is not trustworthy; startup rejects the store and requires resync. | | `meta.pool_lifecycle_state_version` (`pool-lifecycle-state-v1`) | Permanent semantic-readiness guard | In the same empty-store initialization batch as the epoch-boundary markers | Missing on a populated store or any value other than v1 is rejected without writes; retain a backup and resync. | | `meta.utxo_pointer.ready.v1` | Permanent, coordinate-pinned UTXO pointer-index completeness marker | Updated atomically with pointer-index changes after the full index is available | When the pointer index is applicable, a missing, malformed, stale or wrong-coordinate marker makes it not ready and startup fails closed with resync guidance. | | `meta.genesis_staking_bootstrap` | Permanent idempotence and genesis-identity guard | Atomically with Shelley genesis pools, delegations, deposits and initial derived facts | Absence permits the one-time bootstrap. A marker for a different genesis identity fails closed; verify genesis configuration rather than overwriting it. | | `meta.rollback.v1.target-slot` | Transient crash-recovery cursor | Before the first bounded account rollback chunk; removed after all phases are reversed | Absence is normal. Presence makes startup resume rollback-v1. A malformed value fails startup and requires restoring a sound checkpoint or resyncing. | | `meta.reward.progress.v1` | Transient epoch-boundary crash cursor | With each committed bounded streaming-reward chunk; deleted by the final reward commit | Absence is normal. Presence makes reward processing resume after the last durable pool and prevents POOLREAP from starting. An epoch mismatch or malformed value fails the boundary closed. | | `meta.pool.reap.progress.v1` | Transient epoch-boundary crash cursor | Before the first POOLREAP chunk, advanced with each chunk and deleted by the final pool/refund commit | Absence is normal. Presence keeps the boundary and application unready; startup resumes a matching boundary or fails closed if its epoch, slot or step is inconsistent. | ## Manual debugging rollback A one-shot startup rollback can be requested with exactly one command-line system property: ```text -Dyano.debug.rollback-to-slot= -Dyano.debug.rollback-to-epoch= ``` Do not put these properties in `application.yml`; remove them from the next start so the rollback is not requested again. The runtime resolves a canonical stored block at or before the target and rolls back account state, UTXO state and chain state in dependency-safe order. For a rollback across an epoch boundary, account rollback includes every POOLREAP chunk: live pool lifecycle rows and pool delegations are restored, and the exact deposit refund and accumulated reward state are reversed. Replay of the boundary then applies the same retirement and refund once. The target must be at or above the common retained rollback floor. If required reward inputs or journals have been pruned, Yano fails closed; restore a suitable checkpoint or resync with a larger retention setting instead of forcing the rollback. POOLREAP uses the existing rollback-v1 boundary journal and progress marker. There is no rollback-v2 format. --- # Configuration catalog Active packaged configuration values and declared property keys, generated from source. Canonical URL: https://getyano.dev/reference/configuration-catalog/ This reference is generated from the checked-out source. It keeps **bundled defaults and profile overrides separate**. Values are not a merged runtime configuration. Environment expressions retain their exact syntax. Comments and commented examples are excluded. See [configuration layering](/reference/configuration/) before applying settings. Download [the JSON catalog](/ai/configuration.json). Secret-like populated fields are redacted; the local app-chain demo identity is intentionally not an operator credential. ## Bundled application defaults Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `quarkus.http.port` | `7070` | | `quarkus.http.cors.enabled` | `${YANO_HTTP_CORS_ENABLED:false}` | | `quarkus.http.test-port` | `0` | | `quarkus.http.filter.plugin-dashboard-security.matches` | `/ui/plugins/.*` | | `quarkus.http.filter.plugin-dashboard-security.header.Content-Security-Policy` | `connect-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'` | | `quarkus.http.filter.plugin-dashboard-security.header.X-Frame-Options` | `DENY` | | `quarkus.http.filter.plugin-dashboard-security.header.X-Content-Type-Options` | `nosniff` | | `quarkus.http.filter.plugin-dashboard-security.header.Referrer-Policy` | `no-referrer` | | `quarkus.http.filter.plugin-dashboard-security.header.Cache-Control` | `no-store` | | `quarkus.http.filter.console-security.matches` | `/ui(?!/plugins/).*` | | `quarkus.http.filter.console-security.header.Content-Security-Policy` | `connect-src 'self'${YANO_CONSOLE_CONNECT_SRC_EXTRA:}; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'` | | `quarkus.http.filter.console-security.header.X-Frame-Options` | `DENY` | | `quarkus.http.filter.console-security.header.X-Content-Type-Options` | `nosniff` | | `quarkus.http.filter.console-security.header.Referrer-Policy` | `no-referrer` | | `quarkus.http.filter.console-security.header.Cache-Control` | `no-store` | | `quarkus.swagger-ui.always-include` | `${YANO_SWAGGER_UI_ENABLED:true}` | | `quarkus.swagger-ui.path` | `/q/swagger-ui` | | `quarkus.swagger-ui.urls.Core API` | `/q/openapi-core` | | `quarkus.swagger-ui.urls.App Chain API` | `/q/openapi-app-chain` | | `quarkus.swagger-ui.urls.Devnet API` | `/q/openapi-devnet` | | `quarkus.swagger-ui.urls.Admin API` | `/q/openapi-admin` | | `quarkus.swagger-ui.urls.History API` | `/q/openapi-history` | | `quarkus.swagger-ui.urls.All APIs` | `/q/openapi` | | `quarkus.swagger-ui.urls-primary-name` | `Core API` | | `yano.mempool.admin.enabled` | `false` | | `yano.plugins.enabled` | `true` | | `yano.plugins.directory` | `plugins` | | `yano.plugins.allow-list` | `[]` | | `yano.plugins.deny-list` | `[]` | | `yano.plugins.auto-register-annotated` | `false` | | `yano.plugins.logging.enabled` | `false` | | `yano.network` | `preprod` | | `yano.auto-sync-start` | `true` | | `yano.client.enabled` | `true` | | `yano.remote.host` | `preprod-node.world.dev.cardano.org` | | `yano.remote.port` | `30000` | | `yano.remote.protocol-magic` | `1` | | `yano.upstream.validation.level` | `none` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `none` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `69638426` | | `yano.upstream.validation.start.hash` | `ecde79b23e343becca15618fc26281ba1aaea2eb1b66ab8828d7a127f5dbc30f` | | `yano.relay.auto-discovery` | `false` | | `yano.relay.advertised-host` | `auto` | | `yano.relay.advertised-port` | `0` | | `yano.relay.allow-private-addresses` | `false` | | `yano.relay.connection.max-inbound-connections` | `100` | | `yano.relay.connection.max-connections-per-ip` | `5` | | `yano.relay.connection.source-port-reuse` | `true` | | `yano.tx.mempool.max-txs` | `10000` | | `yano.tx.mempool.max-bytes` | `134217728` | | `yano.tx.mempool.max-utxo-index-entries` | `100000` | | `yano.tx.mempool.ttl-seconds` | `10800` | | `yano.tx.diffusion.enabled` | `true` | | `yano.tx.diffusion.limits.max-in-flight-txs-per-peer` | `100` | | `yano.tx.diffusion.limits.max-in-flight-bytes-per-peer` | `1048576` | | `yano.tx.diffusion.limits.peer-cooldown-ms` | `60000` | | `yano.app-chain.storage.path` | `${YANO_APP_CHAIN_STORAGE_PATH:appchain-chainstate}` | | `yano.dns.cache` | `null` | | `yano.server.enabled` | `true` | | `yano.server.port` | `13337` | | `yano.storage.rocksdb` | `true` | | `yano.storage.path` | `./chainstate` | | `yano.genesis.shelley-genesis-file` | `config/network/preprod/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `162d29c4e1cf6b8a84f2d692e67a3ac6bc7851bc3e6e4afe64d15778bed8bd86` | | `yano.genesis.byron-genesis-file` | `config/network/preprod/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/preprod/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/preprod/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/preprod/protocol-param.json` | | `yano.dev-mode` | `false` | | `yano.account-state.enabled` | `true` | | `yano.account.stake-balance-index-enabled` | `true` | | `yano.epoch-boundary.reward-mode` | `streaming` | | `yano.epoch-boundary.max-batch-operations` | `10000` | | `yano.epoch-boundary.max-batch-bytes` | `4194304` | | `yano.history.dir` | `./history` | | `yano.history.archive.engine` | `ducklake` | | `yano.history.archive.finality-blocks` | `auto` | | `yano.history.archive.wait-warn-seconds` | `30` | | `yano.history.archive.stuck-operation-seconds` | `300` | | `yano.history.archive.ducklake.target-file-size` | `4MB` | | `yano.history.archive.ducklake.row-group-size` | `100000` | | `yano.history.archive.ducklake.snapshot-retention-hours` | `168` | | `yano.history.archive.ducklake.cleanup-grace-hours` | `24` | | `yano.history.rollback.retention-blocks` | `auto` | | `yano.history.duckdb.max-total-memory` | `256MB` | | `yano.history.duckdb.max-concurrent-queries` | `2` | | `yano.history.duckdb.max-temp-directory-size` | `2GB` | | `yano.history.duckdb.steady-state.memory-limit` | `128MB` | | `yano.history.duckdb.steady-state.threads` | `1` | | `yano.history.duckdb.bulk-catch-up.memory-limit` | `128MB` | | `yano.history.duckdb.bulk-catch-up.threads` | `1` | | `yano.history.duckdb.bulk-catch-up.max-concurrent-jobs` | `1` | | `yano.epoch-snapshot.amounts-enabled` | `true` | | `yano.adapot.enabled` | `true` | | `yano.rewards.enabled` | `true` | | `yano.epoch-params.tracking-enabled` | `true` | | `yano.governance.enabled` | `true` | | `yano.chain.block-body-prune-depth` | `0` | | `yano.chain.block-prune-batch-size` | `500000` | | `yano.chain.block-prune-interval-seconds` | `300` | | `yano.address-first-seen.enabled` | `false` | | `yano.scan.index.enabled` | `false` | | `yano.scan.max-concurrent` | `2` | | `yano.filters.utxo.enabled` | `false` | | `yano.filters.utxo.addresses` | `[]` | | `yano.filters.utxo.payment-credentials` | `[]` | | `yano.metrics.enabled` | `true` | | `yano.metrics.sample.rocksdb.seconds` | `30` | | `yano.validation.default-validator-enabled` | `true` | | `yano.validation.supplementary-rules-enabled` | `false` | | `yano.utxo.rebuild-unmarked-from-genesis` | `false` | | `yano.utxo.prune.schedule.seconds` | `5` | | `yano.utxo.metrics.lag.logSeconds` | `10` | | `yano.utxo.lag.failIfAbove` | `-1` | | `yano.block-producer.enabled` | `false` | | `yano.block-producer.block-time-millis` | `0` | | `yano.block-producer.lazy` | `false` | | `yano.block-producer.genesis-timestamp` | `0` | | `yano.block-producer.slot-length-millis` | `0` | | `yano.block-producer.start-epoch` | `0` | | `yano.block-producer.past-time-travel-mode` | `false` | | `yano.block-producer.past-time-travel-slot-leader-mode` | `false` | | `yano.block-producer.tx-evaluation` | `true` | ## Bundled application defaults — devnet profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `devnet` | | `yano.auto-sync-start` | `true` | | `yano.dev-mode` | `true` | | `yano.client.enabled` | `false` | | `yano.remote.protocol-magic` | `42` | | `yano.genesis.shelley-genesis-file` | `config/network/devnet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `` | | `yano.genesis.byron-genesis-file` | `config/network/devnet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/devnet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/devnet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/devnet/protocol-param.json` | | `yano.account-state.enabled` | `true` | | `yano.history.projection.enabled` | `true` | | `yano.history.projection.sink` | `ducklake` | | `yano.epoch-snapshot.amounts-enabled` | `true` | | `yano.adapot.enabled` | `true` | | `yano.rewards.enabled` | `true` | | `yano.epoch-params.tracking-enabled` | `true` | | `yano.governance.enabled` | `true` | | `yano.utxo.enabled` | `true` | | `yano.block-producer.enabled` | `true` | | `yano.block-producer.block-time-millis` | `0` | | `yano.block-producer.lazy` | `false` | | `yano.block-producer.genesis-timestamp` | `0` | | `yano.block-producer.slot-length-millis` | `0` | | `yano.block-producer.vrf-skey-file` | `config/network/devnet/vrf.skey` | | `yano.block-producer.kes-skey-file` | `config/network/devnet/kes.skey` | | `yano.block-producer.opcert-file` | `config/network/devnet/opcert.cert` | | `yano.block-producer.past-time-travel-slot-leader-mode` | `false` | ## Bundled application defaults — devnet-slotleader profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `devnet` | | `yano.auto-sync-start` | `true` | | `yano.dev-mode` | `true` | | `yano.client.enabled` | `false` | | `yano.remote.protocol-magic` | `42` | | `yano.genesis.shelley-genesis-file` | `config/network/devnet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `` | | `yano.genesis.byron-genesis-file` | `config/network/devnet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/devnet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/devnet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/devnet/protocol-param.json` | | `yano.account-state.enabled` | `true` | | `yano.epoch-snapshot.amounts-enabled` | `true` | | `yano.adapot.enabled` | `true` | | `yano.rewards.enabled` | `true` | | `yano.epoch-params.tracking-enabled` | `true` | | `yano.governance.enabled` | `true` | | `yano.utxo.enabled` | `true` | | `yano.block-producer.enabled` | `true` | | `yano.block-producer.slot-leader-mode` | `true` | | `yano.block-producer.lazy` | `false` | | `yano.block-producer.genesis-timestamp` | `0` | | `yano.block-producer.slot-length-millis` | `0` | | `yano.block-producer.start-epoch` | `0` | | `yano.block-producer.vrf-skey-file` | `config/network/devnet/vrf.skey` | | `yano.block-producer.kes-skey-file` | `config/network/devnet/kes.skey` | | `yano.block-producer.opcert-file` | `config/network/devnet/opcert.cert` | | `yano.block-producer.past-time-travel-slot-leader-mode` | `false` | ## Bundled application defaults — mainnet profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `mainnet` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `133660855` | | `yano.upstream.validation.start.hash` | `9aa420cf998dbcceec1abaf83ab26294d278d25527e779050ab334c1fadab16c` | | `yano.remote.host` | `backbone.cardano.iog.io` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `764824073` | | `yano.genesis.shelley-genesis-file` | `config/network/mainnet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81` | | `yano.genesis.byron-genesis-file` | `config/network/mainnet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/mainnet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/mainnet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/mainnet/protocol-param.json` | ## Bundled application defaults — preview profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `preview` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `34905604` | | `yano.upstream.validation.start.hash` | `cbc88c5f633ace6671f6cbb54a0913e4d7c57697869e951bc032e093f6db5f46` | | `yano.remote.host` | `preview-node.play.dev.cardano.org` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `2` | | `yano.genesis.shelley-genesis-file` | `config/network/preview/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `363498d1024f84bb39d3fa9593ce391483cb40d479b87233f868d6e57c3a400d` | | `yano.genesis.byron-genesis-file` | `config/network/preview/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/preview/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/preview/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/preview/protocol-param.json` | ## Bundled application defaults — sanchonet profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `sanchonet` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.remote.host` | `sanchonet-node.play.dev.cardano.org` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `4` | | `yano.genesis.shelley-genesis-file` | `config/network/sanchonet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `f94457ec45a0c6773057a529533cf7ccf746cb44dabd56ae970e1dbfb55bfdb2` | | `yano.genesis.byron-genesis-file` | `config/network/sanchonet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/sanchonet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/sanchonet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/sanchonet/protocol-param.json` | ## Bundled application defaults — test profile Source: [app/src/main/resources/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/src/main/resources/application.yml) | Property | Packaged value | | --- | --- | | `yano.plugins.enabled` | `false` | | `yano.plugins.logging.enabled` | `false` | | `yano.auto-sync-start` | `false` | | `yano.client.enabled` | `false` | | `yano.server.enabled` | `true` | | `yano.storage.rocksdb` | `false` | | `yano.storage.path` | `./chainstate-test` | ## application-appchain.yml Source: [app/config/application-appchain.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-appchain.yml) | Property | Packaged value | | --- | --- | | `yano.app-chain.storage.path` | `${YANO_APP_CHAIN_STORAGE_PATH:appchain-chainstate}` | | `yano.app-chain.chains[0].chain-id` | `orders-chain` | | `yano.app-chain.chains[0].signing-key` | `` | | `yano.app-chain.chains[0].members` | `8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c` | | `yano.app-chain.chains[0].threshold` | `1` | | `yano.app-chain.chains[0].state-machine` | `ordered-log` | | `yano.app-chain.chains[0].state.commitment-profile` | `mpf-blake2b256-v1` | | `yano.app-chain.chains[0].state.format-fingerprint` | `91ee14091200f1e24659112d640e877e9177779dcc81dd06117f013e9190082b` | | `yano.app-chain.chains[0].state.genesis-id` | `c2b9c92a865dfa7c218a1a6e49f1dd88163372e40466009876458c01609d0d70` | | `yano.app-chain.chains[0].membership.mode` | `governed` | | `yano.app-chain.chains[0].block.interval-ms` | `1000` | | `yano.app-chain.chains[0].sequencer.proposer` | `8a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c` | ## application-header-signature.yml Source: [app/config/application-header-signature.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-header-signature.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.validation.level` | `header-signature` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `none` | ## application-mainnet.yml Source: [app/config/application-mainnet.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-mainnet.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `mainnet` | | `yano.remote.host` | `backbone.mainnet.cardanofoundation.org` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `764824073` | | `yano.genesis.shelley-genesis-file` | `config/network/mainnet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81` | | `yano.genesis.byron-genesis-file` | `config/network/mainnet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/mainnet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/mainnet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/mainnet/protocol-param.json` | | `yano.upstream.discovery.peer-snapshot-urls` | `["https://book.play.dev.cardano.org/environments/mainnet/peer-snapshot.json"]` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `133660855` | | `yano.upstream.validation.start.hash` | `9aa420cf998dbcceec1abaf83ab26294d278d25527e779050ab334c1fadab16c` | ## application-opcert-strict.yml Source: [app/config/application-opcert-strict.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-opcert-strict.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.validation.opcert-counter-mode` | `strict` | ## application-praos-ledger.yml Source: [app/config/application-praos-ledger.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-praos-ledger.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.validation.level` | `praos-ledger` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `compat` | ## application-praos-lite.yml Source: [app/config/application-praos-lite.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-praos-lite.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.validation.level` | `praos-lite` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `none` | ## application-preprod.yml Source: [app/config/application-preprod.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-preprod.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `preprod` | | `yano.remote.host` | `preprod-node.world.dev.cardano.org` | | `yano.remote.port` | `30000` | | `yano.remote.protocol-magic` | `1` | | `yano.genesis.shelley-genesis-file` | `config/network/preprod/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `162d29c4e1cf6b8a84f2d692e67a3ac6bc7851bc3e6e4afe64d15778bed8bd86` | | `yano.genesis.byron-genesis-file` | `config/network/preprod/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/preprod/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/preprod/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/preprod/protocol-param.json` | | `yano.upstream.discovery.peer-snapshot-urls` | `["https://book.play.dev.cardano.org/environments/preprod/peer-snapshot.json"]` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `69638426` | | `yano.upstream.validation.start.hash` | `ecde79b23e343becca15618fc26281ba1aaea2eb1b66ab8828d7a127f5dbc30f` | ## application-preview.yml Source: [app/config/application-preview.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-preview.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `preview` | | `yano.remote.host` | `preview-node.play.dev.cardano.org` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `2` | | `yano.genesis.shelley-genesis-file` | `config/network/preview/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `363498d1024f84bb39d3fa9593ce391483cb40d479b87233f868d6e57c3a400d` | | `yano.genesis.byron-genesis-file` | `config/network/preview/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/preview/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/preview/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/preview/protocol-param.json` | | `yano.upstream.discovery.peer-snapshot-urls` | `["https://book.play.dev.cardano.org/environments/preview/peer-snapshot.json"]` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | | `yano.upstream.validation.start.slot` | `34905604` | | `yano.upstream.validation.start.hash` | `cbc88c5f633ace6671f6cbb54a0913e4d7c57697869e951bc032e093f6db5f46` | ## application-projection.yml Source: [app/config/application-projection.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-projection.yml) | Property | Packaged value | | --- | --- | | `yano.history.dir` | `${YANO_HISTORY_DIR:./history}` | | `yano.history.projection.enabled` | `true` | | `yano.history.projection.sink` | `${YANO_PROJECTION_SINK:ducklake}` | | `yano.history.projection.drain-interval-millis` | `${YANO_PROJECTION_DRAIN_INTERVAL_MILLIS:250}` | | `yano.history.projection.maintenance.housekeeping-interval-minutes` | `${YANO_PROJECTION_HOUSEKEEPING_INTERVAL_MINUTES:30}` | | `yano.history.projection.maintenance.housekeeping-budget-seconds` | `${YANO_PROJECTION_HOUSEKEEPING_BUDGET_SECONDS:30}` | | `yano.history.projection.maintenance.compaction-interval-minutes` | `${YANO_PROJECTION_COMPACTION_INTERVAL_MINUTES:360}` | | `yano.history.projection.maintenance.compaction-budget-seconds` | `${YANO_PROJECTION_COMPACTION_BUDGET_SECONDS:300}` | | `yano.history.projection.maintenance.compaction-rewrite-bytes` | `${YANO_PROJECTION_COMPACTION_REWRITE_BYTES:8589934592}` | | `yano.history.projection.sink-options.target-file-size-bytes` | `${YANO_PROJECTION_TARGET_FILE_SIZE_BYTES:4194304}` | | `yano.history.projection.sink-options.snapshot-retention-hours` | `${YANO_PROJECTION_SNAPSHOT_RETENTION_HOURS:168}` | | `yano.history.projection.sink-options.cleanup-grace-hours` | `${YANO_PROJECTION_CLEANUP_GRACE_HOURS:24}` | | `yano.history.projection.disk.soft-bytes` | `${YANO_PROJECTION_DISK_SOFT_BYTES:8589934592}` | | `yano.history.projection.disk.hard-bytes` | `${YANO_PROJECTION_DISK_HARD_BYTES:34359738368}` | | `yano.history.projection.disk.low-water-bytes` | `${YANO_PROJECTION_DISK_LOW_WATER_BYTES:4294967296}` | | `yano.history.projection.disk.free-space-reserve-bytes` | `${YANO_PROJECTION_DISK_FREE_RESERVE_BYTES:17179869184}` | ## application-relay.yml Source: [app/config/application-relay.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-relay.yml) | Property | Packaged value | | --- | --- | | `yano.client.enabled` | `true` | | `yano.server.enabled` | `true` | | `yano.server.port` | `13337` | | `yano.relay.auto-discovery` | `true` | | `yano.relay.advertised-host` | `auto` | | `yano.relay.advertised-port` | `0` | | `yano.relay.allow-private-addresses` | `false` | | `yano.relay.connection.max-inbound-connections` | `100` | | `yano.relay.connection.max-connections-per-ip` | `5` | | `yano.relay.connection.source-port-reuse` | `true` | | `yano.upstream.mode` | `p2p-relay` | | `yano.upstream.sync.bulk-source` | `single-trusted` | | `yano.upstream.sync.fan-in-start` | `near-tip` | | `yano.upstream.discovery.enabled` | `true` | | `yano.upstream.discovery.peer-sharing` | `true` | | `yano.upstream.discovery.seeds` | `[]` | | `yano.upstream.discovery.topology-file` | `` | | `yano.upstream.discovery.peer-snapshot-limit` | `64` | | `yano.upstream.discovery.ledger-peers` | `false` | | `yano.upstream.discovery.use-ledger-after-slot` | `-1` | | `yano.upstream.discovery.allow-private-addresses` | `false` | | `yano.upstream.selection.policy` | `trusted-or-quorum-within-rollback-window` | | `yano.upstream.selection.quorum` | `2` | | `yano.upstream.selection.tie-break` | `deterministic` | | `yano.upstream.governor.enabled` | `true` | | `yano.upstream.governor.targets.cold` | `150` | | `yano.upstream.governor.targets.warm` | `8` | | `yano.upstream.governor.targets.hot` | `3` | | `yano.upstream.governor.max-concurrent-dials` | `4` | | `yano.tx.diffusion.enabled` | `true` | | `yano.tx.diffusion.limits.max-in-flight-txs-per-peer` | `100` | | `yano.tx.diffusion.limits.max-in-flight-bytes-per-peer` | `1048576` | | `yano.tx.diffusion.limits.peer-cooldown-ms` | `60000` | ## application-sanchonet.yml Source: [app/config/application-sanchonet.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-sanchonet.yml) | Property | Packaged value | | --- | --- | | `yano.network` | `sanchonet` | | `yano.remote.host` | `sanchonet-node.play.dev.cardano.org` | | `yano.remote.port` | `3001` | | `yano.remote.protocol-magic` | `4` | | `yano.genesis.shelley-genesis-file` | `config/network/sanchonet/shelley-genesis.json` | | `yano.genesis.shelley-genesis-hash` | `f94457ec45a0c6773057a529533cf7ccf746cb44dabd56ae970e1dbfb55bfdb2` | | `yano.genesis.byron-genesis-file` | `config/network/sanchonet/byron-genesis.json` | | `yano.genesis.alonzo-genesis-file` | `config/network/sanchonet/alonzo-genesis.json` | | `yano.genesis.conway-genesis-file` | `config/network/sanchonet/conway-genesis.json` | | `yano.genesis.protocol-parameters-file` | `config/network/sanchonet/protocol-param.json` | | `yano.upstream.validation.start.mode` | `era` | | `yano.upstream.validation.start.era` | `conway` | ## application-static-multi.yml Source: [app/config/application-static-multi.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-static-multi.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.mode` | `static-multi` | | `yano.upstream.sync.bulk-source` | `single-trusted` | | `yano.upstream.sync.fan-in-start` | `near-tip` | | `yano.upstream.discovery.enabled` | `false` | | `yano.upstream.discovery.peer-sharing` | `false` | | `yano.upstream.discovery.peer-snapshot-urls` | `[]` | | `yano.upstream.discovery.peer-snapshot-files` | `[]` | | `yano.upstream.discovery.seeds` | `[]` | | `yano.upstream.selection.policy` | `trusted-or-quorum-within-rollback-window` | | `yano.upstream.selection.quorum` | `2` | | `yano.upstream.selection.tie-break` | `deterministic` | | `yano.upstream.governor.enabled` | `true` | | `yano.upstream.governor.targets.cold` | `32` | | `yano.upstream.governor.targets.warm` | `4` | | `yano.upstream.governor.targets.hot` | `3` | | `yano.upstream.governor.max-concurrent-dials` | `2` | | `yano.tx.diffusion.enabled` | `true` | ## application-structural-validation.yml Source: [app/config/application-structural-validation.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-structural-validation.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.validation.level` | `structural` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `none` | ## application-trusted-peers.yml Source: [app/config/application-trusted-peers.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-trusted-peers.yml) | Property | Packaged value | | --- | --- | | `yano.upstream.mode` | `trusted-failover` | | `yano.upstream.sync.bulk-source` | `single-trusted` | | `yano.upstream.sync.fan-in-start` | `disabled` | | `yano.upstream.discovery.enabled` | `false` | | `yano.upstream.discovery.peer-sharing` | `false` | | `yano.upstream.discovery.peer-snapshot-urls` | `[]` | | `yano.upstream.discovery.peer-snapshot-files` | `[]` | | `yano.upstream.discovery.seeds` | `[]` | | `yano.upstream.governor.enabled` | `false` | | `yano.upstream.validation.level` | `none` | | `yano.upstream.validation.body-level` | `none` | | `yano.upstream.validation.opcert-counter-mode` | `none` | ## application-wallet.yml Source: [app/config/application-wallet.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application-wallet.yml) | Property | Packaged value | | --- | --- | | `yano.scan.index.enabled` | `true` | | `yano.address-first-seen.enabled` | `true` | | `yano.chain.block-body-prune-depth` | `0` | | `yano.utxo.enabled` | `true` | | `yano.filters.utxo.enabled` | `false` | ## application.yml Source: [app/config/application.yml](https://github.com/bloxbean/yano/blob/6e277757f7567cf35e45898d897f18fbeee37bd3/app/config/application.yml) | Property | Packaged value | | --- | --- | | `quarkus.http.port` | `7070` | | `yano.exit-on-epoch-calc-error` | `false` | | `yano.epoch-boundary.reward-mode` | `streaming` | | `yano.epoch-boundary.max-batch-operations` | `10000` | | `yano.epoch-boundary.max-batch-bytes` | `4194304` | ## Declared property keys These literal property names are declared by the public configuration contract. Presence here does not imply a default or support for arbitrary values. Consult the feature guide and runtime validation for constraints. - `yano.account-state.enabled` - `yano.account-state.epoch-block-data-retention-lag` - `yano.account-state.snapshot-retention-epochs` - `yano.account.stake-balance-index-enabled` - `yano.adapot.enabled` - `yano.address-first-seen.enabled` - `yano.api-prefix` - `yano.app-chain.anchor.enabled` - `yano.app-chain.anchor.every-blocks` - `yano.app-chain.anchor.fallback-fee-lovelace` - `yano.app-chain.anchor.max-interval-minutes` - `yano.app-chain.anchor.metadata-label` - `yano.app-chain.anchor.mode` - `yano.app-chain.anchor.script.thread-policy` - `yano.app-chain.anchor.script.validator` - `yano.app-chain.anchor.signing-key` - `yano.app-chain.anchor.validity-slots` - `yano.app-chain.api.auth.enabled` - `yano.app-chain.api.keys` - `yano.app-chain.api.snapshot-admin-keys` - `yano.app-chain.block.interval-ms` - `yano.app-chain.block.max-bytes` - `yano.app-chain.block.max-messages` - `yano.app-chain.chain-id` - `yano.app-chain.chains` - `yano.app-chain.default-ttl-seconds` - `yano.app-chain.dx.release-catalog-digest` - `yano.app-chain.dx.resolved-config-digest` - `yano.app-chain.enabled` - `yano.app-chain.l1.stability-depth` - `yano.app-chain.max-message-bytes` - `yano.app-chain.max-ttl-seconds` - `yano.app-chain.members` - `yano.app-chain.message.enforce-sender-seq` - `yano.app-chain.peers` - `yano.app-chain.pool.max-messages` - `yano.app-chain.retention.enabled` - `yano.app-chain.retention.keep-blocks` - `yano.app-chain.sequencer.proposer` - `yano.app-chain.signing-key` - `yano.app-chain.state-machine` - `yano.app-chain.state.l1-proof-consumption-required` - `yano.app-chain.state.proof-pruning.enabled` - `yano.app-chain.state.proof-pruning.interval-seconds` - `yano.app-chain.state.proof-pruning.retain-heights` - `yano.app-chain.storage.path` - `yano.app-chain.threshold` - `yano.app-chain.transport.mode` - `yano.app-chain.validation.strict` - `yano.app-chain.webhooks` - `yano.auto-checkpoint-interval` - `yano.auto-sync-start` - `yano.block-producer.backfill-block-interval-slots` - `yano.block-producer.block-time-millis` - `yano.block-producer.enabled` - `yano.block-producer.genesis-timestamp` - `yano.block-producer.initial-epoch` - `yano.block-producer.initial-epoch-nonce` - `yano.block-producer.kes-skey-file` - `yano.block-producer.lazy` - `yano.block-producer.opcert-file` - `yano.block-producer.past-time-travel-mode` - `yano.block-producer.past-time-travel-slot-leader-mode` - `yano.block-producer.process-skipped-epochs` - `yano.block-producer.script-evaluator` - `yano.block-producer.slot-leader-mode` - `yano.block-producer.slot-length-millis` - `yano.block-producer.stake-data-provider-url` - `yano.block-producer.start-epoch` - `yano.block-producer.tx-evaluation` - `yano.block-producer.vrf-skey-file` - `yano.bodyFetch.maxBatchSize` - `yano.bodyFetch.realtimeFallbackPollMs` - `yano.bodyFetch.slowEpochTransitionWarnMs` - `yano.bootstrap.addresses` - `yano.bootstrap.block-number` - `yano.bootstrap.blockfrost.api-key` - `yano.bootstrap.blockfrost.base-url` - `yano.bootstrap.enabled` - `yano.bootstrap.koios.base-url` - `yano.bootstrap.provider` - `yano.bootstrap.utxos` - `yano.chain.block-body-prune-depth` - `yano.chain.block-prune-batch-size` - `yano.chain.block-prune-interval-seconds` - `yano.chainstate.recoveryHeaderScanBlocks` - `yano.client.enabled` - `yano.debug.rollback-to-epoch` - `yano.debug.rollback-to-slot` - `yano.dev-mode` - `yano.dns.cache.negative.ttl` - `yano.dns.cache.ttl` - `yano.epoch-boundary.max-batch-bytes` - `yano.epoch-boundary.max-batch-operations` - `yano.epoch-boundary.reward-mode` - `yano.epoch-params.tracking-enabled` - `yano.epoch-snapshot.amounts-enabled` - `yano.epoch-snapshot.balance-mode` - `yano.exit-on-epoch-calc-error` - `yano.filters.utxo.addresses` - `yano.filters.utxo.enabled` - `yano.filters.utxo.payment-credentials` - `yano.genesis.alonzo-genesis-file` - `yano.genesis.byron-genesis-file` - `yano.genesis.conway-genesis-file` - `yano.genesis.protocol-parameters-file` - `yano.genesis.shelley-genesis-file` - `yano.genesis.shelley-genesis-hash` - `yano.governance.enabled` - `yano.headerAppliedEvent.queueCapacity` - `yano.history.` - `yano.ledger-apply.max-queued-decoded-bytes` - `yano.ledger-apply.max-queued-items` - `yano.ledger-apply.reserved-control-slots` - `yano.mempool.admin.api-key` - `yano.mempool.admin.enabled` - `yano.metrics.enabled` - `yano.metrics.sample.rocksdb.seconds` - `yano.network` - `yano.pipeline.epochBoundaryFallbackWaitMs` - `yano.pipeline.headerContinuityValidationBlocks` - `yano.pipeline.nonRecoveringRollbackWaitMs` - `yano.pipeline.slowBodyCallbackWarnMs` - `yano.plugins.allow-list` - `yano.plugins.auto-register-annotated` - `yano.plugins.deny-list` - `yano.plugins.directory` - `yano.plugins.enabled` - `yano.plugins.logging.enabled` - `yano.relay.advertised-host` - `yano.relay.advertised-port` - `yano.relay.allow-private-addresses` - `yano.relay.auto-discovery` - `yano.relay.connection.max-connections-per-ip` - `yano.relay.connection.max-inbound-connections` - `yano.relay.connection.source-port-reuse` - `yano.remote.host` - `yano.remote.port` - `yano.remote.protocol-magic` - `yano.resource-profile` - `yano.rewards.enabled` - `yano.rocksdb.atomic_flush` - `yano.rocksdb.block-cache-bytes` - `yano.rocksdb.max-background-jobs` - `yano.rocksdb.max-open-files` - `yano.rocksdb.pipelined_write` - `yano.rocksdb.target-file-size-bytes` - `yano.rocksdb.tuning.enabled` - `yano.rocksdb.write-buffer-allow-stall` - `yano.rocksdb.write-buffer-bytes` - `yano.rollback-retention-epochs` - `yano.scan.index.enabled` - `yano.server.enabled` - `yano.server.port` - `yano.storage.path` - `yano.storage.rocksdb` - `yano.tx.diffusion.enabled` - `yano.tx.diffusion.limits.max-in-flight-bytes-per-peer` - `yano.tx.diffusion.limits.max-in-flight-txs-per-peer` - `yano.tx.diffusion.limits.peer-cooldown-ms` - `yano.tx.diffusion.mode` - `yano.tx.mempool.max-bytes` - `yano.tx.mempool.max-txs` - `yano.tx.mempool.max-utxo-index-entries` - `yano.tx.mempool.ttl-seconds` - `yano.upstream.discovery.allow-private-addresses` - `yano.upstream.discovery.allowlist` - `yano.upstream.discovery.denylist` - `yano.upstream.discovery.enabled` - `yano.upstream.discovery.ledger-peers` - `yano.upstream.discovery.peer-sharing` - `yano.upstream.discovery.peer-snapshot-files` - `yano.upstream.discovery.peer-snapshot-limit` - `yano.upstream.discovery.peer-snapshot-urls` - `yano.upstream.discovery.seeds` - `yano.upstream.discovery.topology-file` - `yano.upstream.discovery.use-ledger-after-slot` - `yano.upstream.failover.cooldown-ms` - `yano.upstream.failover.max-failures-before-cooldown` - `yano.upstream.governor.enabled` - `yano.upstream.governor.max-concurrent-dials` - `yano.upstream.governor.targets.cold` - `yano.upstream.governor.targets.hot` - `yano.upstream.governor.targets.warm` - `yano.upstream.mode` - `yano.upstream.peers` - `yano.upstream.selection.policy` - `yano.upstream.selection.quorum` - `yano.upstream.selection.require-body-before-adoption` - `yano.upstream.selection.rollback-window-slots` - `yano.upstream.selection.tie-break` - `yano.upstream.selection.trust-policy` - `yano.upstream.sync.bulk-source` - `yano.upstream.sync.fan-in-start` - `yano.upstream.tx.forwarding` - `yano.upstream.validation.body-level` - `yano.upstream.validation.level` - `yano.upstream.validation.opcert-counter-mode` - `yano.upstream.validation.start.era` - `yano.upstream.validation.start.hash` - `yano.upstream.validation.start.mode` - `yano.upstream.validation.start.slot` - `yano.utxo.applyAsync` - `yano.utxo.delta.selfContained` - `yano.utxo.enabled` - `yano.utxo.index-contributors` - `yano.utxo.index.address_hash` - `yano.utxo.index.payment_credential` - `yano.utxo.indexingStrategy` - `yano.utxo.lag.failIfAbove` - `yano.utxo.metrics.lag.logSeconds` - `yano.utxo.prune.schedule.seconds` - `yano.utxo.pruneBatchSize` - `yano.utxo.pruneDepth` - `yano.utxo.rebuild-unmarked-from-genesis` - `yano.utxo.rollbackWindow` - `yano.validation.default-validator-enabled` - `yano.validation.supplementary-rules-enabled` --- # Configuration guide Understand configuration layers, defaults, profiles, and build-time settings. Canonical URL: https://getyano.dev/reference/configuration/ Yano's runnable application uses Quarkus configuration. Edit `config/application.yml` in your extracted release directory for operator overrides. The application also carries bundled defaults; the generated catalog describes the current source version, so check your downloaded release's config for supported settings. ## Layering Run from the extracted distribution directory. Put persistent local overrides in `config/application.yml`; compose optional `application-.yml` files through the launcher: ```bash ./yano.sh start:preprod,relay,praos-lite ``` System properties use the full property name, such as `-Dquarkus.http.port=7071`. When invoking Java directly, JVM `-D` arguments go **before** `-jar`. ```bash java -Dquarkus.profile=devnet -Dquarkus.http.port=7071 -jar yano.jar ``` Environment aliases explicitly supplied by packaged YAML are preserved in the [generated configuration catalog](/reference/configuration-catalog/). That catalog separates each file/profile; a profile value is not a universal default. ## Common settings | Setting | Meaning | | --- | --- | | `quarkus.http.port` | HTTP API and console port; bundled value `7070` | | `yano.network` | Network identity; bundled value `preprod` | | `yano.remote.host` / `port` | Selected upstream connection | | `yano.storage.path` | Local node database | | `yano.server.port` | Node-to-node server port | | `yano.app-chain.storage.path` | Separate app-chain database root | | `yano.block-producer.block-time-millis` | `0` derives timing from genesis | | `yano.plugins.directory` | JVM plugin directory | ## Find every documented setting The [configuration catalog](/reference/configuration-catalog/) is generated from active YAML values in the current checkout. It also lists declared Yano property keys, including settings that are not assigned in packaged YAML. A declared key is not a promise that every possible value is supported. Download [configuration JSON](/ai/configuration.json) for tooling. Comments and commented examples are deliberately excluded from the active-value tables; consult feature guides for semantics and constraints. ## Build-time REST prefix `/api/v1` is the normal REST prefix. A custom prefix is selected when building using `-PyanoApiPrefix=/your-prefix`. It is fixed in the artifact and cannot be changed at launch by setting `yano.api-prefix`, `quarkus.resteasy.path`, or `quarkus.http.root-path`. See [distribution details](/contribute/build-from-source/) before building a custom-prefix artifact. --- # HTTP API Discover release-matched OpenAPI schemas and the main API groups. Canonical URL: https://getyano.dev/reference/http-api/ The default application API prefix is `/api/v1`. Management endpoints under `/q` and the console under `/ui/` sit outside that prefix. For transaction-building SDK integrations, see [Build with any SDK](/develop/blockfrost/). ## Explore the running node Open `http://localhost:7070/q/swagger-ui`. The node supplies these OpenAPI documents: | Document | Scope | | --- | --- | | `/q/openapi-core` | Chain, ledger, transactions, evaluation, read-only node status | | `/q/openapi-app-chain` | App-chain and plugin domain APIs | | `/q/openapi-devnet` | Local faucet, snapshots, rollback, time controls | | `/q/openapi-admin` | Node lifecycle, plugin operations, diagnostics | | `/q/openapi-history` | Historical coverage and maintenance | | `/q/openapi` | All API groups | Download the schema from **your actual binary**: ```bash curl -fsS 'http://localhost:7070/q/openapi?format=json' -o yano-openapi.json ``` The site provides a [source-derived route inventory](/ai/routes.json), not a substitute for a complete OpenAPI schema. Routes can be feature-gated, require credentials, or return unavailable when required data is absent. ## Common requests ```bash curl -fsS http://localhost:7070/api/v1/node/tip curl -fsS http://localhost:7070/api/v1/status curl -fsS http://localhost:7070/api/v1/epochs/latest/parameters curl -fsS http://localhost:7070/q/health/ready ``` Node lifecycle and debug operations are administrative actions. Do not expose a development node's complete API surface to untrusted clients. Consult the relevant feature guide for authentication and configuration. Swagger UI can be disabled with `YANO_SWAGGER_UI_ENABLED=false` or `-Dquarkus.swagger-ui.always-include=false`. Disabling a documentation UI does not disable the underlying APIs. --- # JavaScript testkit reference Lifecycle, helper APIs, and native process options for JavaScript. Canonical URL: https://getyano.dev/reference/javascript/ This guide is for JavaScript and TypeScript developers who want to run tests against a local Cardano devnet without starting Docker or a JVM test fixture. `@bloxbean/yano-testkit` starts the native Yano binary in devnet mode, waits for the HTTP API to be ready, and gives your test code a small helper object for funding addresses, querying chain state, moving devnet time, snapshots, rollback, transaction submission, and assertions. ## What You Get - A real Yano native process started from your JS test. - RocksDB-backed devnet storage by default, isolated in a temporary directory. - Random free HTTP and node-to-node ports by default. - Yano's production Blockfrost-compatible HTTP API under `/api/v1/`. - Devnet-only helpers for faucet funding, time travel, snapshots, and rollback. - No wallet custody inside Yano. Your JS app or Cardano SDK owns keys and signs transactions. ## Install ```bash npm install --save-dev @bloxbean/yano-testkit ``` For preview releases: ```bash npm install --save-dev @bloxbean/yano-testkit@preview ``` The `latest` npm tag is reserved for stable releases. Until a stable package is promoted, install prereleases explicitly with `@preview`. The package depends on optional platform packages that contain the native Yano binary for the local OS and CPU. You normally do not need to set any binary environment variable after installing from npm. Supported native packages: - Linux x64 - Linux arm64 - macOS arm64 - Windows x64 Node.js 20.8 or newer is required. ## Minimal Node Test ```js import assert from "node:assert/strict"; import { test } from "node:test"; import { startYanoDevnet } from "@bloxbean/yano-testkit"; test("starts a Yano devnet", async () => { const yano = await startYanoDevnet({ blockTimeMillis: 200 }); try { const tip = await yano.queries.tip(); assert.ok(tip); await yano.time.advanceSlots(3); await yano.assertions.slotAtLeast(3); } finally { await yano.stop(); } }); ``` Plain Node.js tests must call `await yano.stop()`. Put it in a `finally` block so the native process is stopped even when an assertion fails. ## Vitest Use the Vitest adapter when one devnet should be shared by a suite: ```js import { describe, expect, test } from "vitest"; import { yanoDevnet } from "@bloxbean/yano-testkit/vitest"; const yano = yanoDevnet({ blockTimeMillis: 200 }); describe("my Cardano app", () => { test("reads the chain tip", async () => { const tip = await yano.queries.tip(); expect(tip).toBeTruthy(); }); }); ``` The adapter starts Yano in `beforeAll()` and stops it in `afterAll()`. ## Devnet Object `startYanoDevnet()` returns: ```js const yano = await startYanoDevnet(); console.log(yano.baseUrl); // http://127.0.0.1:/ console.log(yano.apiBaseUrl); // http://127.0.0.1:/api/v1/ console.log(yano.n2nPort); console.log(yano.storage.path); console.log(yano.workDir); ``` Important fields and helpers: - `baseUrl` - root Quarkus URL, useful for health endpoints such as `/q/health/ready`. - `apiBaseUrl` - Yano's Blockfrost-compatible API root. - `url(path)` - creates a URL under `apiBaseUrl`. - `logs()` - returns the recent Yano stdout/stderr log tail. - `stop()` - stops the native process and cleans temporary storage. - `process` - the underlying Node `ChildProcess`, mainly for diagnostics. ## Start Options ```js const yano = await startYanoDevnet({ blockTimeMillis: 200, timeoutMs: 60_000, networkMagic: 42, httpPort: 0, n2nPort: 0, storage: "temp-rocksdb", onStdout: line => console.log(`[yano] ${line}`), onStderr: line => console.error(`[yano] ${line}`) }); ``` Common options: - `blockTimeMillis` - devnet block production interval. - `timeoutMs` - startup readiness timeout. - `httpPort` and `n2nPort` - set fixed ports, or omit them for random free ports. - `networkMagic` - protocol magic for the devnet. - `storage` - `"temp-rocksdb"` or `"persistent-rocksdb"`. - `workDir` / `storagePath` - explicit directories for persistent tests. - `preserveWorkDir` - keep temporary work directory after `stop()`. - `binaryPath` - path to a local Yano binary. - `cwd` - source directory containing `config/` when using a local app build. - `env` - extra environment variables for the Yano process. - `extraArgs` - extra `-D...` JVM/native image system properties. - `onStdout` / `onStderr` - stream Yano logs into your test output. ## Storage Modes The default is `temp-rocksdb`: ```js const yano = await startYanoDevnet({ storage: "temp-rocksdb" }); ``` This uses real RocksDB storage in a test-owned temporary directory. The wrapper copies `cwd/config` or the packaged platform `config` into that directory and runs Yano from the copy, so devnet time-travel and genesis rewrites do not mutate installed package files. The wrapper deletes the directory when `stop()` succeeds. The JavaScript testkit is RocksDB-only. It does not expose an in-memory storage mode. Use persistent storage when you need to inspect chain state after shutdown: ```js const yano = await startYanoDevnet({ storage: "persistent-rocksdb", workDir: "./tmp/yano-case-1" }); ``` `persistent-rocksdb` requires `workDir` or `storagePath`. ## Wallets And Funding Yano HTTP does not own wallets, mnemonics, private keys, or signing callbacks. That boundary is intentional. Create test wallets with your normal Cardano JS library, then fund their addresses through the devnet faucet. ```js const address = "addr_test1..."; const result = await yano.faucet.fundAddress(address, 1000); console.log(result.tx_hash, result.index, result.lovelace); ``` Funding helpers: ```js await yano.faucet.fundAddress(address, 1000); // ADA await yano.faucet.fundAddressLovelace(address, 1_000_000n); await yano.faucet.fundAll([ { address: alice, ada: 1000 }, { address: bob, lovelace: 2_000_000n } ]); ``` `fundAll()` is sequential and non-atomic. If one funding request fails, previous funding transactions are not rolled back. There is also a compatibility alias: ```js await yano.fundAddress(address, 1000); ``` ## Use With Cardano JavaScript SDKs Yano exposes a Blockfrost-compatible API at `yano.apiBaseUrl`. Cardano JS libraries that can use a Blockfrost-style provider should point at that URL. MeshJS example: ```js import { BlockfrostProvider, MeshWallet } from "@meshsdk/core"; const provider = new BlockfrostProvider(yano.apiBaseUrl); const wallet = new MeshWallet({ networkId: 0, fetcher: provider, submitter: provider, key: { type: "mnemonic", words: testMnemonicWords } }); await wallet.init(); const address = await wallet.getChangeAddress(); const funding = await yano.faucet.fundAddress(address, 20); await yano.await.untilTxVisible(funding.tx_hash); ``` After funding, build and sign transactions with the SDK, then submit through the SDK provider or through `yano.transactions`. ## Query Chain State Use `yano.queries` for common Blockfrost-compatible and Yano query endpoints: ```js const status = await yano.queries.status(); const tip = await yano.queries.tip(); const config = await yano.queries.config(); const latestBlock = await yano.queries.latestBlock(); const protocolParams = await yano.queries.protocolParameters(); const utxos = await yano.queries.utxosByAddress(address); const tx = await yano.queries.tx(txHash); const txUtxos = await yano.queries.txUtxos(txHash); ``` `protocolParameters()` calls `/api/v1/epochs/latest/parameters`. With the default devnet profile, protocol parameters come from Yano's epoch-param tracker. If a test disables tracking: ```js const yano = await startYanoDevnet({ extraArgs: ["-Dyano.epoch-params.tracking-enabled=false"] }); ``` then Yano falls back to static `protocol-param.json` content for the Blockfrost-compatible parameters endpoint. Useful derived queries: ```js const slot = await yano.queries.currentSlot(); const block = await yano.queries.currentBlockNumber(); const epoch = await yano.queries.currentEpoch(); const epochStart = await yano.queries.epochStartSlot(2); ``` Response objects preserve Yano's HTTP JSON shape. For Blockfrost-compatible endpoints this usually means snake_case fields such as `tx_hash`, `output_index`, and `block_number`. ## Devnet Time Use time helpers to move the devnet quickly: ```js await yano.time.advanceSlots(5); await yano.time.advanceSeconds(10); await yano.time.advanceEpochs(1); await yano.time.advanceToSlot(50); await yano.time.advanceToEpoch(2); await yano.time.crossEpochBoundary(); ``` `advanceToSlot()`, `advanceToEpoch()`, and `crossEpochBoundary()` are best-effort helpers. They read current chain state and then request a relative advance. When a producer is running, the chain can move during that calculation. Follow them with an await helper or assertion when the exact final state matters: ```js await yano.time.advanceToEpoch(2); await yano.await.untilEpochAtLeast(2); ``` Epoch maintenance helpers: ```js await yano.time.shiftGenesisAndStartProducer(3); await yano.time.catchUpToWallClock(); ``` ## Snapshots And Rollback Create snapshots around destructive test cases: ```js await yano.snapshots.create("before-case"); try { await runCase(); } finally { await yano.snapshots.restore("before-case"); } ``` Or use `withSnapshot()`: ```js await yano.snapshots.withSnapshot("case", async () => { await yano.time.advanceSlots(10); await yano.devnet.rollback({ count: 1 }); }); ``` Other snapshot helpers: ```js const snapshots = await yano.snapshots.list(); const exists = await yano.snapshots.exists("case"); await yano.snapshots.delete("case"); ``` Rollback can target exactly one of `slot`, `blockNumber`, or `count`: ```js await yano.devnet.rollback({ count: 1 }); await yano.devnet.rollback({ slot: 20 }); await yano.devnet.rollback({ blockNumber: 5 }); ``` ## Submit And Evaluate Transactions Build and sign transactions in your app or Cardano JS SDK. Submit the serialized transaction through Yano: ```js const txHash = await yano.transactions.submitHex(signedTxHex); await yano.await.untilTxVisible(txHash); ``` For CBOR bytes: ```js const txHash = await yano.transactions.submitCbor(signedTxCborBytes); ``` Submit and wait in one call: ```js const txHash = await yano.transactions.submitAndAwait(signedTxHex, { timeoutMs: 20_000, pollIntervalMs: 200 }); ``` Evaluate transactions: ```js const result = await yano.transactions.evaluateHex(unsignedOrSignedTxHex); ``` The MeshJS example in `test-examples/yano-testkit-meshjs` shows the complete flow: create a Mesh wallet, fund it, build a self-transfer, sign, submit, wait, and verify. ## Await Helpers Await helpers poll until the condition is true or a timeout is reached: ```js await yano.await.untilReady(); await yano.await.untilSlotAtLeast(10); await yano.await.untilBlockAtLeast(5); await yano.await.untilEpochAtLeast(1); await yano.await.untilTxVisible(txHash); ``` Override polling per call: ```js await yano.await.untilTxVisible(txHash, { timeoutMs: 30_000, pollIntervalMs: 200 }); ``` Custom condition: ```js await yano.await.until( async () => (await yano.queries.utxosByAddress(address)).length > 0, "address to have at least one UTXO" ); ``` ## Assertions Assertions are runner-neutral. They throw normal errors, so they work with Node's test runner, Vitest, Jest, Mocha, and custom runners. ```js await yano.assertions.nodeIsRunning(); await yano.assertions.runtimeNotDegraded(); await yano.assertions.slotAtLeast(10); await yano.assertions.blockAtLeast(5); await yano.assertions.epochAtLeast(1); await yano.assertions.snapshotExists("case"); await yano.assertions.snapshotMissing("case"); ``` Address assertions: ```js await yano.assertions.address(address).hasAtLeastAda(1000); await yano.assertions.address(address).hasAtLeast(1_000_000n); await yano.assertions.address(address).hasExactly(2_000_000n); const balance = await yano.assertions.address(address).balanceLovelace(); ``` ADA helpers accept JavaScript numbers for convenience. Use lovelace helpers with `bigint` or string values when exact arithmetic matters. ## Low-Level HTTP Client Use `yano.client` when a named helper does not exist: ```js const params = await yano.client.getJson("epochs/latest/parameters"); const bytes = await yano.client.getBytes("devnet/genesis/download"); ``` Available methods: - `getJson(path, options)` - `postJson(path, body, options)` - `deleteJson(path, options)` - `postCbor(path, body, options)` - `postText(path, body, options)` - `getBytes(path, options)` The path is relative to `yano.apiBaseUrl`. ## Error Handling HTTP failures throw `YanoHttpError`: ```js import { YanoHttpError } from "@bloxbean/yano-testkit"; try { await yano.queries.tx("missing"); } catch (error) { if (error instanceof YanoHttpError) { console.error(error.method, error.url, error.status); console.error(error.bodyText); } } ``` Startup failures include the recent Yano log tail in the error message. You can also print logs after a failed test: ```js try { await runTest(); } catch (error) { console.error(yano?.logs()); throw error; } ``` ## Use a downloaded native binary The npm platform package normally supplies the native binary. To select a particular release, [download and extract a matching native ZIP](/start/installation/), then point the testkit at that binary and its containing directory: ```js const yano = await startYanoDevnet({ binaryPath: "/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64/yano", cwd: "/absolute/path/to/yano-native-0.1.0-pre12-macos-arm64" }); ``` Use the path for your platform; on Windows the executable is `yano.exe`. Keep `config/` beside the executable. Choose a testkit version compatible with the selected binary; overriding it does not add newer endpoints to an older release. `YANO_TESTKIT_BINARY` can also override binary discovery. If you need an unreleased native build, follow the separate [source-build guide](/contribute/build-from-source/). ## TypeScript The package ships TypeScript declarations: ```ts import { startYanoDevnet, type YanoDevnet } from "@bloxbean/yano-testkit"; let yano: YanoDevnet | undefined; yano = await startYanoDevnet(); ``` No separate `@types` package is needed. ## Troubleshooting ### The process does not start - Increase `timeoutMs` or set `YANO_TESTKIT_TIMEOUT_MS`. - Pass `onStdout` and `onStderr` to see Yano logs while the test runs. - Print `yano.logs()` in failure handlers when a process started but the test later failed. - For local binaries, verify `cwd` points to a directory containing `config/`. ### The package cannot find a binary - Verify the local platform is supported by the optional native packages. - Reinstall dependencies so npm installs optional dependencies for your OS/CPU. - For unsupported platforms or local development, set `YANO_TESTKIT_BINARY`. ### Tests hang after completion - Plain Node.js tests must call `await yano.stop()`. - Use `try/finally`. - In Vitest, prefer `yanoDevnet()` so `afterAll()` stops the process. ### A funded address has no visible UTXO yet - Wait for the faucet transaction: ```js const result = await yano.faucet.fundAddress(address, 1000); await yano.await.untilTxVisible(result.tx_hash); ``` - Then query UTXOs or use address assertions. ## Examples - `test-examples/yano-testkit-js` - minimal published-package smoke test. - `test-examples/yano-testkit-meshjs` - MeshJS wallet, funding, signed transfer, submit, await, and verification. --- # Plugin query & domain APIs Implement committed queries and bounded domain APIs. Canonical URL: https://getyano.dev/reference/plugin-contract/ This guide covers two read-side extension points: - a state machine can answer a bounded query against one committed state root; - the same manifested bundle can publish framework-neutral domain routes that call that query through a constrained host facade. Start with the working project in `scaffolds/plugin-template`. It contains the provider classes, both ServiceLoader descriptors, one manifest, unit tests, and a production-catalog launch probe described below. ## 1. Keep the two execution planes separate `AppStateMachine.apply(...)` remains deterministic consensus execution. `AppStateMachine.query(..., AppQueryContext)` is off-consensus and read-only. The query callback may overlap a later `apply()` on another thread, so do not mutate state-machine fields or use the query callback as a command path. Its result must depend only on the path, parameters, and supplied snapshot; external I/O, wall-clock time, or randomness would produce a payload that the reported state root does not attest. The context's committed height, state root, and all key reads come from one root-fixed snapshot. The context expires when the callback returns. Do not retain it, create child work, emit effects, write state, perform unbounded CPU work, or depend on a callback continuing after its deadline. ```java @Override public byte[] query(String path, byte[] params, AppQueryContext state) { if (!"passport/read".equals(path)) { throw new AppQueryException(AppQueryException.Code.UNSUPPORTED, "unknown passport query"); } if (!validAssetId(params)) { throw new AppQueryException(AppQueryException.Code.INVALID_REQUEST, "invalid asset id"); } return state.get(passportKey(params)).orElse(new byte[0]); } ``` A state-machine query may deliberately report only `UNSUPPORTED` or `INVALID_REQUEST`. The host owns `BUSY`, `TIMEOUT`, `RESULT_TOO_LARGE`, `UNAVAILABLE`, and `FAILED`; it bounds admission, one decoded request to 64 KiB, one result to 1 MiB, and execution time. Unexpected plugin exceptions are logged by type, redacted, and exposed as `FAILED`. The generic HTTP adapter is: ```text POST /api/v1/app-chain/chains/{chainId}/query/{queryPath} Content-Type: application/json {"paramsHex":""} ``` The response binds the opaque payload to `chainId`, `stateMachineId`, `committedHeight`, and the 32-byte `stateRoot`. Query paths are normalized relative paths of unreserved ASCII segments. Percent escapes, empty/dot segments, leading/trailing slashes, and aliases are rejected. ## 2. Publish a constrained domain API product Implement `DomainApiProvider`. Its stable id is the containing bundle id, not a short state-machine selector: ```java public final class PassportDomainApiProvider implements DomainApiProvider { public static final String BUNDLE_ID = "com.example.product-passport"; @Override public String id() { return BUNDLE_ID; } @Override public DomainApi create(DomainApiContext context) { return new PassportDomainApi(context.queryService()); } } ``` The context exposes only a bounded `DomainQueryService`. It does not expose a JAX-RS router, request identity, message submission, effects, mutable runtime services, or administration. In the current API, `DomainApiContext.bundleConfig()` is deliberately an empty map until Yano has a typed, secret-safe configuration/reference contract. Do not put a domain API's operation or security behind assumed configuration values. ### Routes and deterministic matching Return at most 64 immutable `DomainApiRoute` entries and validate the exact set in plugin tests with `DomainApiRouteSet.validateAndOrder(...)`. The host applies the same validator before publication. Route ids are bundle-local stable identifiers. Templates use literal segments and whole-segment lowercase parameters such as `passports/{asset_id}`. When routes overlap, the first segment at which one route is literal and the other is a variable decides precedence; the literal wins. Parameter names do not affect matching. Routes with the same method and structural shape are a startup error, for example `claims/{id}` and `claims/{claim_id}`. ```java private static final List ROUTES = DomainApiRouteSet.validateAndOrder(List.of( new DomainApiRoute("passport.read", DomainHttpMethod.GET, "passports/{asset_id}", DomainApiAccess.READ), new DomainApiRoute("passport.report", DomainHttpMethod.POST, "operator/report/{asset_id}", DomainApiAccess.PRIVILEGED), new DomainApiRoute("debug", DomainHttpMethod.GET, "internal/debug", DomainApiAccess.INTERNAL))); ``` The v1 access classes are: - `READ`: available under the host's read policy. A topic-scoped API key may call it. - `PRIVILEGED`: requires an unscoped full key, independently of broad READ/SUBMIT authentication. If that safe key configuration is absent, the HTTP route is hidden as 404 and the handler is not invoked. - `INTERNAL`: reserved, secret-free inventory only. It is not dispatchable by HTTP or the public host/library gateway in v1. The host owns `/api/v1/plugins/{bundleId}/{relativePath}`, GET/POST method selection, authentication, raw request bounds, queueing, deadlines, response validation, and error redaction. A plugin must not start another HTTP server or assume its route can override a host endpoint. ### Handle requests and encode output safely Dispatch on `request.routeId()`, not on untrusted raw path text. The request contains immutable validated path/query maps and a defensive body copy. GET has no body; POST is limited to 64 KiB. A response is limited to 1 MiB and can be JSON or octet-stream. Plugins may return `200`, `400`, `404`, `409`, `410`, or `422`; every other status is host-owned so redirects, authentication, admission, no-content/partial-content, and server semantics cannot be forged. If returning JSON, use a JSON library already packaged in the plugin or a tested encoder. Never concatenate `chainId`, a query result, a parameter, or an exception message directly into a JSON string. The scaffold's `JsonSupport` is a small dependency-free example and its domain response hex-encodes opaque bytes before JSON encoding. The host rejects malformed or trailing JSON. Translate a committed-query failure by stable code and use a generic message: ```java static DomainApiException translate(AppQueryException failure) { DomainApiException.Code code = switch (failure.code()) { case INVALID_REQUEST, REQUEST_TOO_LARGE -> DomainApiException.Code.INVALID_REQUEST; case UNSUPPORTED -> DomainApiException.Code.NOT_FOUND; case BUSY -> DomainApiException.Code.BUSY; case TIMEOUT -> DomainApiException.Code.TIMEOUT; case RESULT_TOO_LARGE -> DomainApiException.Code.RESULT_TOO_LARGE; case UNAVAILABLE -> DomainApiException.Code.UNAVAILABLE; case FAILED -> DomainApiException.Code.FAILED; }; return new DomainApiException(code, "passport query failed", failure); } ``` Do not copy `failure.getMessage()` to the new exception or response. The runtime preserves a plugin-thrown `DomainApiException` reason code but replaces its message with canonical host-owned safe text. Any other exception becomes `FAILED`. `DomainApi` is lifecycle-owned. `routes()` is snapshotted during construction, callbacks are bounded and serialized per bundle in v1, and `close()` runs after admission is sealed and callbacks drain. Make close idempotent, reject work after close, release only resources owned by the product, and never close a host facade. ## 3. Declare both ServiceLoader and manifest metadata Create this exact resource: ```text META-INF/services/org.yanoproject.api.plugin.domain.DomainApiProvider ``` Its content is the provider's binary class name: ```text com.example.passport.PassportDomainApiProvider ``` Then declare the same class in the bundle-qualified manifest. For schema v1, the domain contribution `name`, `DomainApiProvider.id()`, manifest `id`, and manifest filename id must all be identical: ```json { "schemaVersion": 1, "id": "com.example.product-passport", "version": "1.0.0", "yanoApi": { "min": 1, "max": 1, "minLevel": 1 }, "dependencies": [], "contributions": [ { "kind": "app-state-machine", "name": "passport", "provider": "com.example.passport.PassportStateMachineProvider" }, { "kind": "domain-api", "name": "com.example.product-passport", "provider": "com.example.passport.PassportDomainApiProvider" } ] } ``` The schema-v1 `yanoApi.minLevel` field is required. Compatibility requires both a host API major within `min`/`max` and a host global API level at least `minLevel`; an incompatible bundle is rejected before any provider is constructed. The level advances for additive public plugin APIs (including new contribution kinds) and never resets when the major changes. It is independent of the bundle's `version` SemVer. ServiceLoader remains the behavior-instantiation contract. The manifest is identity, compatibility, policy, ownership, and inventory metadata; it is not an arbitrary constructor list. A missing/mismatched descriptor or provider fails catalog validation before product activation. ## 4. Build, deploy, and secure Package one self-contained reproducible plugin JAR. Compile against `yano-core-api` as `compileOnly` and never bundle `org/yanoproject/api/**`. Shade third-party runtime dependencies into the same JAR; adjacent thin dependency JARs are not one catalog bundle. Copy the JAR into `yano.plugins.directory`. If an allow-list is configured, allow the bundle id. Select the state-machine contribution by its short selector on the app chain. The domain contribution is activated as part of the selected bundle and is addressed by bundle id. For privileged routes configure an unscoped full key: ```properties yano.app-chain.api.keys=,=topic-a|topic-b ``` Send the unscoped key in `X-API-Key`. When broad authentication is enabled, topic-scoped keys can read and submit only to their topics; they cannot call privileged routes. With broad authentication disabled, `READ` and `SUBMIT` are public and only `PRIVILEGED` routes inspect the configured full key. Treat all plugin JARs as trusted in-process code: manifest validation and centralized HTTP auth do not sandbox Java code. Set `yano.app-chain.api.auth.enabled=true` as well when READ and SUBMIT routes must also require keys. ## 5. Test before deployment At minimum: 1. Test `apply()` determinism and replay with `StateMachineConformance`. 2. Test contextual queries with an in-memory `AppQueryContext`: unsupported and invalid input codes, no mutation, root/height envelope, request/result size. 3. Test the complete route set with `DomainApiRouteSet.validateAndOrder`, including structural collisions and literal-before-variable precedence. 4. Test every route id/access class, query-code translation, JSON injection, response bounds, close idempotence, and post-close rejection. 5. Inspect the built JAR for both ServiceLoader descriptors and the bundle-qualified manifest; reject bundled Yano API classes. 6. Launch the JAR through Yano's production directory catalog and resolve both providers. Then run packaged JVM/native smoke and an HTTP test with auth enabled and disabled. The scaffold wires steps 1–5 into its Gradle `check` lifecycle and is the reference authoring baseline. For event-listener and `NodePlugin` lifecycle contributions, see `runtime/docs/events-and-plugins-guide.md`. --- # Release migration details Storage and behavior changes across preview versions. Canonical URL: https://getyano.dev/reference/upgrading/ ## Pool lifecycle correctness This version adds ordered same-block pool lifecycle handling and complete live-state POOLREAP. It also adds the `pool-lifecycle-state-v1` readiness marker. Every chainstate created before this marker is intentionally incompatible. Keep or archive an old chainstate if it is still useful for comparison, then sync the selected network into a clean directory. Startup rejects a populated pre-marker store without changing it and reports that a resync is required. No boundary-v2, rollback-v2 or automatic promotion path is provided. See [Account state and rollback](/reference/account-state/) for the boundary semantics, compatibility contract and one-shot manual rollback guidance. ## History cleanup The legacy replay-worker history implementation and its public Java write-session API have been removed. Projection history is now the only archive writer; `ArchiveBackend` is a generation-pinned read facade. Before upgrading, remove every explicit `yano.history.enabled` property, including `yano.history.enabled=false`. To collect history, configure `yano.history.projection.enabled=true` and select projection sections or epoch artifacts as needed. Also remove `yano.account-history.enabled` and legacy `yano.history.worker.*`, `yano.history.hot-store.*`, `yano.history.datasets.*`, `yano.history.start-mode`, `yano.history.maintenance.*`, and `yano.history.archive.sqlite.*` properties. Startup rejects these keys rather than silently ignoring them, and readiness reports `DOWN` while the configuration error is present. The removed Java surface includes the replay hot-store/progress/resolver types, `ArchiveWriteSession`, `ArchiveReceipt`, `ArchiveRetentionCutoff`, and `DuckLakeWriteSession`. Integrators should use `ProjectionSink` for archive writes and the read-only repositories exposed by `ArchiveBackend` for queries. `GET /history/watermark` no longer consults legacy `archive_coverage` metadata. It reports the projection consistency point or an unavailable response. Existing replay metadata tables are left inert and are not dropped automatically; current projection archives do not require a rebuild. --- # Capability guide See which Yano features fit your runtime and workflow. Canonical URL: https://getyano.dev/start/capabilities/ Yano is modular. Select capabilities for the job, and check the configuration and data prerequisites before relying on a query. | Capability | Where it fits | What to know | | --- | --- | --- | | Cardano synchronization and REST queries | JVM or native node | Current local state depends on sync progress and enabled subsystems | | Local block production | Devnet | Uses local genesis and test funds | | Slot-leader devnet recipe | Advanced development | The bundled `devnet-slotleader` profile enables slot-leader mode; it is not a promise of production pool operation | | Faucet, snapshots, rollback, time travel | Devnet controls | Isolate mutations to test-owned environments | | Java / JUnit testkit | Embedded JVM | Managed lifecycle and real RocksDB storage | | JavaScript / TypeScript testkit | Native child process | Matching platform binary required | | CCL backend adapter | Java testkit | Selected backend services, not the whole CCL backend API | | Wallet first-seen and scan indexes | Optional node capability | Fresh sync and continuous coverage; scans require retained bodies | | DuckLake history projection | JVM | Fresh-sync archive; no native support | | `ordered-log` app chain | JVM or native host | Opaque events; does not enforce application business rules | | Multiple app chains, proofs, and L1 anchoring | App-chain host | Configure identities, membership, thresholds, and anchor requirements | | Certified observations | Optional preview | Disabled by default; explicit committed profile and trust policy | | Dynamic plugin JARs and Yano X extensions | JVM | Matching plugin contracts and explicit installation | | Console, health, and metrics | Runnable application | Optional persistent metrics companion requires Docker Compose | | Embedding and event/plugin SPI | Java libraries | Use public role and extension interfaces | ## Configuration is part of the feature The `wallet` and `projection` profiles serve different purposes. The former enables optional discovery/scan indexes; the latter enables historical projection. A plain default node is not an archive, and a filtered UTxO database is not complete wallet history. Native builds preserve core providers but cannot dynamically load JVM plugins. Use the [configuration guide](/reference/configuration/) and [generated catalog](/reference/configuration-catalog/) to inspect exact source/profile values. --- # Download & install Yano Choose and run a JVM or platform-specific Yano release. Canonical URL: https://getyano.dev/start/installation/ Download a ready-to-run ZIP from [Yano v0.1.0-pre12](https://github.com/bloxbean/yano/releases/tag/v0.1.0-pre12). Extract the complete archive; keep the executable, launcher, and `config/` directory together. ## Choose your download | Distribution | Download | Requirements | | --- | --- | --- | | **JVM — recommended for app chains** | [yano-0.1.0-pre12.zip](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-0.1.0-pre12.zip) | Java 25 | | Linux x64 | [Native ZIP](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-native-0.1.0-pre12-linux-x64.zip) | Linux on x64 | | Linux arm64 | [Native ZIP](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-native-0.1.0-pre12-linux-arm64.zip) | Linux on arm64 | | macOS arm64 | [Native ZIP](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-native-0.1.0-pre12-macos-arm64.zip) | Apple silicon Mac | | Windows x64 | [Native ZIP](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-native-0.1.0-pre12-windows-x64.zip) | Windows on x64 | For app-chain onboarding, **use the JVM distribution for now**. It is also the distribution to choose for JVM extensions. Native images cannot dynamically load plugin JARs. ## Start on macOS or Linux For the JVM ZIP: ```bash unzip yano-0.1.0-pre12.zip cd yano-0.1.0-pre12 ./yano.sh start:devnet ``` For a native ZIP, enter the corresponding extracted directory and use the same launcher. To connect to a public network instead: ```bash ./yano.sh start:preprod ``` Use separate storage for different networks. The launcher runs with configuration from the extracted distribution. See [network configuration](/node/networks/) before changing networks or peers. ## Start on Windows Extract the ZIP with Explorer or PowerShell, then open PowerShell in the extracted directory. Native distribution: ```powershell .\yano.exe -Dquarkus.profile=devnet -Dyano.block-producer.script-evaluator=scalus ``` JVM distribution, with Java 25 installed: ```powershell java -Dquarkus.profile=devnet -jar yano.jar ``` For JVM app chains, use `java "-Dquarkus.profile=devnet,appchain" -jar yano.jar`. ## Releases and documentation versions The download links above select **pre12**, a published release. This site's advanced reference follows the current `org.yanoproject` source line, which is newer. The namespace rename, newer wallet/history features, plugin contracts, and proof APIs are not all present in pre12. Use your release's bundled configuration and `/q/swagger-ui` as the reference for its available endpoints. Pre12 also predates the current split of additional stock app-chain extensions into Yano X. Do not infer a release's bundled machines from the current-source module map. Use release-matched SDKs and verifiers, and check [upgrade notes](/operate/upgrades/) before reusing stored state. You can browse [all releases](https://github.com/bloxbean/yano/releases) for later artifacts. To modify Yano itself or use unreleased changes, see [build from source](/contribute/build-from-source/); building is not required for the download workflow. --- # What is Yano? Understand Yano, choose a starting point, and build your first Cardano workflow. Canonical URL: https://getyano.dev/start/overview/ Yano is a **Cardano data node written in Java**. It follows the blockchain, keeps local ledger state, and gives your application APIs to read that state and submit transactions. You can run it as an application or embed it inside your own Java process. The same project also gives you a local, block-producing development network, integration testkits, and a host for application-specific chains. ## Choose your starting point | I want to… | Start with | | --- | --- | | Try Yano without syncing a public network | [Your first local node](/start/quickstart/) | | Read Cardano data and submit transactions | [Run a data node](/node/networks/) | | Test a Java application against real ledger state | [Java testkit](/develop/java-testkit/) | | Test from JavaScript or TypeScript | [JavaScript testkit](/develop/javascript-testkit/) | | Give several participants a shared, verifiable event history | [Your first app chain](/app-chains/quickstart/) | | Add application-specific rules | [State machines and plugins](/app-chains/extensions/) | | Give an AI assistant accurate Yano context | [Build with AI](/ai/overview/) | See [Yano in use](/develop/devkit/) for Yaci DevKit and UVerify Sandbox integrations. ## Use your favorite language and SDK Yano runs in Java, but your dApp does not have to. Use its [Blockfrost-compatible HTTP API](/develop/blockfrost/) from CCL, MeshJS, Evolution SDK, or another language’s HTTP client to query data, evaluate scripts, and submit signed transactions. ## Three capabilities, one foundation **Data node.** Synchronize Cardano blocks, maintain UTxOs and ledger state, query REST endpoints, evaluate scripts, and submit transactions. Choose upstream peers and optional indexes for your workload. **Development network.** Produce local blocks, fund test addresses, save snapshots, trigger rollbacks, and control time. Java and JavaScript testkits manage the lifecycle for repeatable tests. **App-chain host.** Run independent application ledgers with signed messages, deterministic execution, threshold finality, state proofs, and optional Cardano anchoring. The built-in `ordered-log` records opaque events. More state machines and integrations live in [Yano X](https://github.com/bloxbean/yano-x). ## Current status Yano is **pre-release**. APIs and storage formats can change. Use it for development, testing, experimentation, and downstream prototyping; production validation is not yet the project's release claim. Public-network data synchronization does not imply complete Cardano consensus validation. The Java package root and Maven group are `org.yanoproject`. The JavaScript testkit retains its `@bloxbean/yano-testkit` npm name. Yaci, Cardano Client Lib, JuLC, and Zeroj keep their own dependency namespaces. ## A few words you will see - **UTxO:** an unspent transaction output; the spendable state of a Cardano address. - **Slot / epoch:** Cardano's units of chain time; an epoch contains many slots. - **Devnet:** an isolated network with test funds and its own genesis. - **State root:** a compact commitment to the state of an app chain. - **Finality certificate:** evidence that the configured member threshold certified an app block. - **Anchor:** a Cardano transaction that commits an app-chain root to L1. --- # Your first local node Download Yano and start a local Cardano development network. Canonical URL: https://getyano.dev/start/quickstart/ Start a local Cardano chain from a **ready-to-run release**. You do not need Git, Gradle, or a source checkout. ## 1. Download Yano Open the [Yano v0.1.0-pre12 release](https://github.com/bloxbean/yano/releases/tag/v0.1.0-pre12) and download: - **JVM:** [`yano-0.1.0-pre12.zip`](https://github.com/bloxbean/yano/releases/download/v0.1.0-pre12/yano-0.1.0-pre12.zip), with Java 25 installed. Recommended if you also want to try app chains. - **Native:** the `yano-native-0.1.0-pre12-.zip` matching your operating system and CPU. No Java installation needed. See [installation](/start/installation/) for the platform download links and Windows commands. ## 2. Extract and start Extract the whole ZIP, including its `config/` directory. For the JVM download on macOS or Linux: ```bash unzip yano-0.1.0-pre12.zip cd yano-0.1.0-pre12 ./yano.sh start:devnet ``` For a native download, enter its extracted `yano-native-0.1.0-pre12-` directory and run the same `./yano.sh start:devnet` command. The launcher selects the packaged binary automatically. Keep this terminal running. The default REST port is `7070`; the node-to-node port is `13337`. The local devnet uses network magic `42`. ## 3. Check your chain In another terminal: ```bash curl -fsS http://localhost:7070/q/health/ready curl -fsS http://localhost:7070/api/v1/node/tip curl -fsS http://localhost:7070/api/v1/blocks/latest curl -fsS http://localhost:7070/api/v1/epochs/latest/parameters ``` Repeat the tip query after a few seconds. Once startup completes, blocks should advance. Open [Swagger UI](http://localhost:7070/q/swagger-ui) to explore the API included in your release. ## What you just started This is a local Cardano development chain backed by RocksDB. Its test currency has no public-network value. Keep devnet mutation endpoints on your development machine or a trusted test network. If startup fails, check Java with `java -version` for a JVM installation, confirm that ports are free, and ensure you extracted the complete distribution. See [troubleshooting](/operate/troubleshooting/). ## Next steps [Connect your favorite SDK](/develop/blockfrost/), [submit a transaction](/develop/transactions/), or [start an app chain](/app-chains/quickstart/). Source builds are an optional [contributor workflow](/contribute/build-from-source/).