ZAAR

Architecture

ZAAR is a record-keeping network: one deterministic state machine holds every balance, order and credential, and every change to it is a command in one append-only journal. Everything else (history, market data, the API) is derived from that journal.

Principles (from TigerBeetle, adapted)

  1. The journal is the truth. State = replay(journal). History tables, candles and caches are projections that can be dropped and rebuilt at any time.
  2. Deterministic core. zaar-core does no I/O, reads no clock, uses no randomness, no floats and no iteration over unordered maps. The sequencer stamps each command with its sequence number and time, and those are journaled, so replay is bit-exact.
  3. Integers only. Amounts are u128 minimal units; prices and quantities are integers whose product is exact by construction (data-model.md). Nothing rounds in matching; fees round once, towards the network, by a documented rule.
  4. Double entry. Every balance change is a transfer between two accounts. Per asset, the sum of all balances is zero. Tests assert this after every command.
  5. One writer, batched. A single sequencer thread owns the state. It takes commands in batches, journals the batch with one fdatasync, and only then releases the responses (group commit).
  6. Authorization is state. Orgs, users, keys and scopes live in the journal, and the core decides every permission. The gateway only verifies signatures. Replay re-checks every decision.
  7. Assert everything. Invariants are checked in debug builds after every command and in the simulator after every step. A failed invariant stops the process; it never limps on.

Runtime (v1)

          Internet ──443──▶ Caddy (TLS, per-host routing)
                              │
            ┌─────────────────┴─────────────────┐
            ▼                                   ▼
   zaard (prod, :8082)                 zaard (staging, :8081)      one e2-micro VM, us-east1-b
   ┌───────────────────────────────────────────────┐
   │ gateway (tokio, axum)                          │  REST /v1, WebSocket /v1/ws
   │   verify Ed25519 signature, rate limit, decode │
   │        │ bounded channel                       │
   │        ▼                                       │
   │ sequencer thread ── owns ──▶ zaar-core State   │  apply(cmd) → events
   │        │  batch → journal append → fdatasync   │
   │        ▼                                       │
   │ events ──▶ WS hub (market + user streams)      │
   │        └─▶ history projector ──▶ SQLite        │  orders, trades, transfers, klines
   └───────────────────────────────────────────────┘
        │ journal segments + snapshots (every 30 s and on roll)
        ▼
   gs://zaar-network-backups/<env>/

Crates (lower never depends on higher)

zaar-core        types, commands, events, state machine: ledger, matching, fees, authz. No I/O.
   └─ zaar-journal   segment files, snapshots, recovery, backup hook
   └─ zaar-history   SQLite projector + history queries
   └─ zaar-api       HTTP/WS gateway: auth headers, DTOs, error codes, OpenAPI
                     (the `server` feature reads zaar-history's SQLite; D-19)
        └─ zaard         the daemon: config, wiring, sequencer thread, backups
zaar-client      Rust client: signing, typed requests (used by CLI and integration tests)
   └─ zaar-cli       the `zaar` binary

Scaling path (not built yet, but nothing above blocks it)

Step Change Unchanged
More load Bigger VM, local NVMe, pin the sequencer to a core everything else
Read scale Read replicas tail the journal; gateways scale out statelessly core, journal format
HA Replicate the journal (VSR/Raft) to 3 or 6 replicas before acking core, API
HFT Binary protocol (SBE-style) and colocated gateways; shard the core by market, with the ledger as its own shard and cross-shard locks via pending transfers data model, API semantics

Why not TigerBeetle itself (yet)

TigerBeetle wants 6 GiB+ RAM per replica and ECC/NVMe in production, and it has no order book, so we would run two sources of truth. v1 implements the same ledger model (accounts, pending and posted debits/credits, linked transfers, per-asset ledgers) inside our core. Because the model matches, the ledger can move to a TigerBeetle cluster later as the "ledger shard". See decisions.md D-4.