Data Sources
All external data enters the system through a single Cardano node socket. There is no Ogmios, no Yaci Store — only node-to-client (N2C) Unix sockets carrying Cardano mini-protocols.
The public API is not a pass-through node query service. Proof-bearing reads and facts endpoints are served from the local ChainSync-derived RocksDB index: a UTxO CSMT plus cage state and MPF trie columns anchored to one indexed snapshot. LocalStateQuery UTxO scans are forbidden on production proof/write paths because they bypass that proof system.
Connection
graph LR
NODE["Cardano Node<br/>(Unix socket)"]
CS["ChainSync<br/>(mini-protocol 5)"]
LSQ["LocalStateQuery<br/>(mini-protocol 7)<br/>metadata/eval context only"]
LTS["LocalTxSubmission<br/>(mini-protocol 6)"]
NODE --- CS
NODE --- LSQ
NODE --- LTS
IDX["CageFollower + UTxO CSMT"]
READS["IndexerTx proof reads"]
PRV["Provider<br/>(PParams/eval/slots)"]
SUB["Submitter"]
CS --> IDX --> READS
LSQ --> PRV
LTS --> SUB
The service uses two N2C connections to the same node socket. Connection
1 runs ChainSync for the follower. Connection 2 runs LocalStateQuery and
LocalTxSubmission via ouroboros-network channels.
Data Flow
graph TD
subgraph "Cardano Node"
CS["ChainSync"]
LSQ["LocalStateQuery"]
LTS["LocalTxSubmission"]
end
subgraph "Indexed RocksDB"
UTXO["UTxO CSMT<br/>kv + proofs"]
CAGE["cage state<br/>tokens + requests"]
TRIE["MPF tries<br/>facts + proofs"]
end
subgraph "HTTP"
FACTS["/facts/*"]
READ["/tokens/* and /utxo/*"]
EVAL["/eval-context"]
SUBMIT["/submit"]
end
subgraph "Clients"
SPA["Browser SPA<br/>HTTP + CIP-30"]
REACTOR["wasm cage reactor"]
end
subgraph "Server submission"
SUB["Submitter"]
end
CS --> UTXO
CS --> CAGE
CS --> TRIE
UTXO --> FACTS
CAGE --> FACTS
TRIE --> FACTS
UTXO --> READ
CAGE --> READ
TRIE --> READ
LSQ -->|"PParams, PlutusV3 cost models,<br/>SystemStart, era history"| EVAL
SPA -->|"facts + eval context"| FACTS
SPA --> EVAL
SPA -->|"verified facts"| REACTOR
SPA -->|"signed tx CBOR"| SUBMIT
SUBMIT --> SUB
SUB --> LTS
N2C Mini-Protocols
ChainSync (protocol 5)
Streams blocks into the CageFollower, which processes UTxO changes,
cage events, MPF trie mutations, rollback storage, and checkpoints in a
single atomic RocksDB transaction. This is the source of truth for:
- UTxO facts — indexed
TxIn -> TxOutentries with CSMT inclusion proofs and prefix-completeness witnesses. - Cage state — token state UTxOs, request UTxOs, checkpoints, and rollback inverses.
- MPF facts — per-token trie enumeration and inclusion/absence
proofs generated by
haskell-mts.
Handlers compose atomic reads through Context.runIndexerTx, so the
snapshot, UTxO witnesses, request-set witnesses, and MPF proofs in one
response target the same utxo_root and chain point.
LocalStateQuery (protocol 7)
Queries node ledger metadata that is not yet trust-anchored by the proof system. It is used for:
-
Protocol parameters — fee coefficients, max transaction size, minimum UTxO value, collateral percentage, Plutus execution prices. Queried via
GetCurrentPParams(Conway era). -
Script evaluation and slot conversion — used by native paths and exposed to the browser as
GET /eval-context. -
Evaluation context —
GET /eval-contextreturns PlutusV3 cost models/protocol parameters,SystemStart, and live era-history so the pure reactor can evaluate ex-units and deriveEpochInfo.
GET /eval-context is explicitly trusted and interim. Follow-up #311
will trust-anchor this metadata. Until then, tampering is expected to
fail local transaction construction or Phase 2 validation, but the
metadata itself is not proof-bearing.
GetUTxOByAddress is not a production facts source. It scans the node's
ledger UTxO set, has cost proportional to total chain UTxOs, and bypasses
the indexed CSMT proofs. Server-side proof and write handlers must use
the local index instead.
LocalTxSubmission (protocol 6)
Submits a signed Tx ConwayEra to the node's mempool. The
Submitter converts the ledger transaction to a consensus GenTx
and returns either Submitted txId or Rejected reason.
The public route is POST /submit, which accepts signed transaction
CBOR. Script-bearing cage operations reach this route only after the
client verifies facts, runs the local cage builder/reactor, and adds
wallet witnesses.
Channel Architecture
Communication between the N2C connection thread and the Provider /
Submitter uses TBQueue-backed channels:
LSQChannel— carries(query, resultVar)pairs for protocol parameters, script evaluation, slot conversion, and eval-context metadata. It must not be used to source proof-bearing UTxO facts.LTxSChannel— carries(genTx, resultVar)pairs. The Submitter writes a transaction and blocks on the result MVar.
The N2C client thread reads from these queues and drives the protocol state machines.
ChainSync (Connection 1)
ChainSync (mini-protocol 5) runs on a separate N2C connection
from LSQ/LTxS, driven by cardano-utxo-csmt's
mkN2CChainSyncApplication. Each block is handed to the
CageFollower, which processes UTxO changes, cage events, trie
mutations, and rollback storage in a single atomic RocksDB
transaction.
See Block Processing for the full forward/rollback flow.