Verification and securityJournal
Journal
The public record of every decision: signed wallet roots, run roots and daily Cardano mainnet anchors.
Every decision Exchain makes, allowed or refused, lands in a public journal at journal.exchain.network: signed every hour per wallet, and anchored once a day on Cardano mainnet.
Each decision is in a signed wallet root, and each day one Cardano mainnet transaction commits to a run root.
Layers
| Layer | Built from | Protected by | Cadence |
|---|---|---|---|
| Decision leaf | blake2b-256 over the decision with the domain string exchain.decision.v1: decision id, wallet id, wallet input, transaction body hash, outcome, and per purchase its id, output, amount, asset, counterparty, job hash, agent identifier, verdict, code and numbers. Byte layout below | — | Every decision |
| Wallet root | Merkle root of the wallet's leaves since its last record | Ed25519 signature by the publisher key over canonical JSON of 12 fields: schemaVersion, walletId, epoch, fromSeq, toSeq, count, root, from, to, publishedAt, publisherKeyHash, auditHead | Every hour |
| Run root | Merkle root of all wallet roots in wallet-id order, plus sha256(auditHead) as the last leaf | Not signed. Committed by the anchor | Every hour |
| Anchor | Cardano mainnet transaction with metadata label 1958: { v, root, walletCount, epochRange } | Cardano mainnet | Once per UTC day, on the first run after 00:00 UTC. Includes decisions from every network and chain |
Merkle rule: each node is SHA-256(left ‖ right), and a lone last node is paired with itself. The anchor fixes that run's wallet roots and the ledger heads.
{ "1958": { "v": 1, "root": "<32 bytes>", "walletCount": 27, "epochRange": [2, 1198] } }leaf = blake2b_256(
"exchain.decision.v1"
‖ len‖decisionId ‖ len‖walletId
‖ walletUtxoHash(32) ‖ walletUtxoIndex(u64)
‖ txBodyHash(32) ‖ outcome(u8) ‖ count(u64)
‖ per member:
len‖purchaseId ‖ outputIndex(u32) ‖ amount(i128)
‖ len‖asset ‖ len‖counterparty ‖ len‖jobHash ‖ len‖agentIdentifier
‖ verdict(u8) ‖ len‖code
‖ opt bound ‖ opt used ‖ opt attempted
)- Lengths and integers are big-endian.
optis0x00, or0x01followed by an i128.outcome: 0 allowed, 1 denied, 2 security.verdict: 0 allowed, 1 denied, 2 not_evaluated.
Statuses
| Status | Meaning |
|---|---|
pending | No root includes the decision yet |
signed | In a signed wallet root and run; no landed anchor at or after its run |
anchored | The first landed anchor at or after its run is its own run's |
covered | A later run's anchor is the first to land after this decision's run |
Public and private fields
| In the leaf | On the public journal |
|---|---|
| Decision id, wallet id, outcome, transaction body hash, member count, per-member verdict and code | Yes |
| Amount | Only after settlement, totalled per asset |
| Counterparty, job hash, agent identifier, purchase id, bound, used, attempted, wallet UTxO ref | No |
Recomputing a leaf needs the decision JSON from GET /v1/decisions/{decisionId} or the evidence bundle.
Files
Served at https://journal.exchain.network without credentials.
| File | Content |
|---|---|
publisher.json | { keyHash, vkey } of the publisher key |
latest.json | The latest run, with its signed wallet records |
<walletId>/<epoch>.json | One signed wallet record, with its anchor once landed |
<walletId>/latest.json | The wallet's latest signed record |
d/<decisionId>.json | Decision summary, verdicts, proof and status |
w/<walletId>.json | Wallet counts and up to 500 newest decisions |
tx/<hash>.json | Pointer from a transaction hash to a decision or run |
anchors.json | Every anchored run: run, txHash, slot, network, root |
recent.json | Journal statistics and anchoring status |
history/<scope>-<n>.json | All decisions in pages of 500, oldest first |
API alternative: GET /v1/journal/roots?walletId=&from=&to=, no token. Check a decision step by step: Verify a decision.