LEDGERD is a deterministic, replicated, in-memory balance and delegated-spending engine in Java: a strongly consistent state machine replicated by Aeron Cluster (Raft), with multi-asset balances, two-phase holds, a CQRS read side, write/read client SDKs, and an optional domain event journal. It is the single source of truth for balances and allowances - and does exactly that, fast.
- Understand the design: Architecture and the Architecture Decision Records.
- Use the API: API reference - client SDKs, commands, status codes, and driving the engine directly.
- Operate it: Operations - snapshots, node restart, deployment, Prometheus metrics.
- Plan capacity: Capacity planning - memory model,
CoreConfigsizing, and server mapping. - Contribute: Contributing and Security.
Balance ledgers are correctness-critical: every command must apply exactly once even across retries and leader failover, settlement must conserve value, and results must be reproducible for audit and reconciliation.
LEDGERD solves this as a single deterministic state machine replicated by Aeron Cluster. The engine has no clock, no randomness, no floating point, and no unordered iteration: identical input logs produce byte-identical state and snapshots on every node and every rerun. Commands are idempotent by design, and the steady-state hot path allocates nothing. It targets financial cores, event-sourced systems, and any service where a double-applied command or a nondeterministic replay is a correctness failure.
- Deterministic and reproducible - identical input logs produce byte-identical state and snapshots on every node, so replicas and archived logs always reconcile.
- Exactly-once, even across failover - a per-client dedup window
(
clientId,clientSeq) makes every command idempotent; retries, a leader change, or a killed leader can never double-apply. - Zero-allocation, lock-free hot path - one clustered-service thread owns all state; decode, dispatch, and ACK allocate nothing in steady state. The contract is the tail, not the mean.
- Financially correct - integer-only 64-bit fixed-scale arithmetic with
overflow checks (never a silent wrap), multi-asset isolation (ADR 0009), and
RESERVE/CAPTURE/RELEASEtwo-phase holds with conserved supply (ADR 0010). - Batched transfers with atomic chains - one
TransferBatchmessage carries many transfer legs and amortizes the per-message consensus cost; contiguouslinkedlegs commit or roll back together (ADR 0012). - Reads without touching consensus - a CQRS read replica follows a member's Aeron Archive and answers balance / allowance / supply queries over a plain Aeron protocol, failing over across every member's Archive (ADR 0006, 0008).
- Decoupled fan-out - an opt-in, deterministic domain event journal emits semantic facts off the consensus hot path, recorded per member for downstream consumers (ADR 0011).
flowchart LR
subgraph CLIENT["Clients"]
WC["write-client\n(commands)"]
RC["read-client\n(queries)"]
end
subgraph CLUSTER["Aeron Cluster (Raft)"]
NODE["ClusteredService\nBalanceService + BalanceEngine"]
AR["Archive\n(log + snapshots + journal)"]
end
REP["read replica\n(CQRS)"]
WC -->|"SBE CommandEnvelope"| CLUSTER
CLUSTER -->|"CommandResult"| WC
RC -->|"QueryRequest (UDP)"| REP
REP -->|"QueryResponse"| RC
AR -.->|"consensus log + journal replay"| REP
CLUSTER --> AR
Requires JDK 21 and Linux (the Aeron media driver). Gradle run tasks set the
required --add-opens flags automatically.
# Build everything
./gradlew build
# In-process single-node cluster that submits a credit and a transfer
./gradlew :examples:run
# Run a single-node cluster
./gradlew :launcher:runMulti-node runs, driving the deterministic engine directly, the client SDK walkthrough, and configuration are in docs/API-REFERENCE.md and docs/OPERATIONS.md.
| Module | Responsibility |
|---|---|
protocol |
SBE schema and generated flyweight codecs (wire contract only) |
core |
Deterministic engine: handlers, dedup, snapshot, event journal ring, telemetry |
launcher |
Aeron bootstrap: media driver, archive, consensus, container, journaler |
write-client |
Write-side SDK: leader-change handling, idempotent retry, correlation |
read |
CQRS read replica: Archive replication + failover, Aeron query responder, event follower |
read-client |
Read-side SDK over plain Aeron request/response streams |
examples |
Runnable examples (QuickStart, BatchTransfer, RemoteClient, ReadClient) |
tests |
Unit, property, integration, cluster, fault, and soak suites |
Wire and snapshot formats, data flows, and determinism rules live in docs/ARCHITECTURE.md.
Indicative JMH numbers on x86_64 Linux, JDK 21 (steady state, zero allocation):
| Operation | Time |
|---|---|
| Envelope decode | ~1.9 ns |
| Primitive map lookup | ~0.8 ns |
| Credit dispatch (in-process) | ~24 ns |
| Batch leg dispatch (in-process) | ~22 ns/leg |
Targets: decode < 100 ns, primitive-map lookup < 50 ns, command dispatch < 500 ns, hot-path allocation 0 bytes. See the performance budget. Baseline numbers a reviewer can diff against are committed in benchmark-baseline.txt.
MIT License. See LICENSE.
Built on Aeron, Agrona, and Simple Binary Encoding.