No. 07 · Aegis Suite Production-grade 94% test coverage

A ledger that
stays balanced
under concurrency.

DoubleX is a production-grade double-entry ledger service with strong consistency guarantees: SERIALIZABLE isolation at the database level, idempotent postings with advisory-lock de-duplication, end-of-day close with balance snapshots, FX settlement via the clearing-account method, reconciliation CLI with CSV import and tolerance matching, and first-class observability through Prometheus, OpenTelemetry and a Grafana SLO dashboard.

Live

Post, replay, repeat.

The buttons below call the real DoubleX API deployed at stelioszach.com/doublex-ledger/live/api/transfers/. Post a balanced journal once, then click Replay — the service returns the same journal_id with status=duplicate and the account balance does not move. That's idempotency enforced at the DB layer, not by application discipline.

Draft journal entry
Range limited to ≤ 500 EUR for the demo tenant.
Auto-generated on load. Rotate to start a fresh ledger.
Debit (Dr)
DEMO-CASH
+10.00 EUR
Credit (Cr)
DEMO-REV
10.00 EUR
Service response
waiting — submit a journal
journal_id
status
round-trip
created_at
DEMO-CASH · EUR
Debit side
DEMO-REV · EUR
Credit side
{}
Guarantees

Six promises the schema keeps.

Every guarantee is enforced where it's hardest to break — at the database layer. The service code is thin on top: it reads, validates, posts under a transaction, and surfaces the solver's view. The invariants don't rely on application discipline.

Atomicity

SERIALIZABLE isolation

All posting operations execute at SERIALIZABLE isolation, not the default read-committed. Conflicts are retried with exponential backoff; the ledger_serialization_retries_total metric exposes contention.

Replay safety

Idempotent postings

Every transfer carries an idempotency_key. A retry of the same payload returns the original posting — not a duplicate. Implemented with advisory locks plus an upsert sentinel; safe across concurrent clients.

Cut-off

End-of-Day close

A single command closes the day: freezes balances to snapshot rows, records the close timestamp, and installs a backdating guard that rejects any posting earlier than the latest closed day. Re-runnable.

Multi-currency

FX two-journal settlement

Cross-currency transfers post two balanced journals joined through a per-currency clearing account. Rates are upsertable and normalized (pair inversion handled once); rounding policy is explicit in decimal context.

Reconciliation

Recon CLI + API

Bulk CSV import, exact ref match first, then amount within ±0.01 tolerance. Outputs CSV/JSON/HTML artifacts and inserts ReconException rows; served via /api/reports/recon.

Observable

First-class telemetry

Prometheus counters for ledger_tx_total, latency histograms for post_journal and get_balance, OTEL spans on every business path, and a pre-provisioned Grafana Ledger SLO dashboard.

Architecture

FastAPI on top, Postgres does the work.

The schema enforces the invariants — balanced journals, non-negative balances where required, currency match on legs — so the application can be stateless. Alembic owns migrations; SQLAlchemy 2.0 models generate them. OTEL and Prometheus wrap every request and every span.

── ingress ──────────────────────────────────────────────────────────
  client  ─── POST /api/transfers ──▶  FastAPI  ──▶  Pydantic v2
                                          │
                                          ▼
── business logic ───────────────────────────────────────────────────
                          ledger.domain.double_entry
                                  +  idempotency  (advisory lock)
                                  +  fx           (two-journal method)
                                  +  eod          (backdating guard)
                                          │
                                          ▼
── persistence ──────────────────────────────────────────────────────
                          SQLAlchemy 2.0  ──▶  PostgreSQL
                                                    SERIALIZABLE isolation
                                                    DB triggers: balance = 0
                                                                currency match
                                                                non-negative
                                          │
                                          ▼
── observability ────────────────────────────────────────────────────
         OTEL spans     +     Prometheus /metrics     +     Loguru (JSON)
              │                        │
              ▼                        ▼
       OTLP collector            Prometheus scrape
              │                        │
              └──────────┬─────────────┘
                         ▼
                    Grafana · Ledger SLO dashboard
                    (TPS · p95 post_journal · SERIALIZABLE retries · recon mismatches)
API

A narrow surface.

Accounts, transfers, journals, reports. REST + OpenAPI, Pydantic-validated. The entire production surface fits in four prefixes; everything else — EoD, reconciliation imports — happens via Typer CLI or background workers.

POST /api/accounts/ Create
# register a new account curl -X POST /api/accounts/ \ -d '{ "code": "1001-CASH", "name": "Cash", "ccy": "EUR" }' # → 201 Created { "id": "66c8714d-b2a1-…", "code": "1001-CASH", "name": "Cash", "ccy": "EUR", "status": "active" }
POST /api/transfers/ Idempotent
# balanced transfer, replay-safe curl -X POST /api/transfers/ \ -d '{ "idempotency_key": "tx-0c47a2", "legs": [ { "account": "2001-LIAB", "side": "debit", "amount": "4500.00", "ccy": "EUR" }, { "account": "1001-CASH", "side": "credit", "amount": "4500.00", "ccy": "EUR" } ] }' # → 201 Created (or 200 on replay)
GET /api/accounts/{code}/balance Read
# current balance in account ccy curl /api/accounts/1001-CASH/balance # → 200 OK { "account": "1001-CASH", "ccy": "EUR", "balance": "128459.00", "as_of": "2026-04-16T21:14:00Z", "snapshot_source": "live" }
GET /api/reports/recon Report
# daily reconciliation summary curl /api/reports/recon?date=2026-04-16 # → 200 OK { "date": "2026-04-16", "ledger_total": "128,459.00", "bank_total": "128,459.00", "matched": 482, "unmatched": 3, "exceptions": [ { "ref": "bnk-0911", "delta": "0.04" } ] }
Metrics

Four numbers that matter.

Coverage as a floor, latency as a ceiling, retries as contention telemetry, reconciliation mismatches as correctness. Everything else is a derivative.

Test coverage
94%
pytest + Hypothesis across domain, API, recon, observability. Property tests for balanced postings and EoD re-runs.
post_journal p95
~18 ms
SERIALIZABLE transaction + double-entry posting + DB triggers on a single-node Postgres. Measured under Locust load.
Isolation retries
< 0.4%
Serialization conflicts handled transparently with exponential backoff; rate exposed at ledger_serialization_retries_total.
Recon tolerance
±0.01
Amount match tolerance after exact-ref match fails. Anything outside lands in ReconException rows.
Anatomy

Under the binding.

Every layer is independently testable and observable. The domain is pure Python, the persistence layer owns the invariants, and the service tier is thin glue.

Layer Tech Role
Domain Pure Python · decimal double_entry · eod · fx · idempotency · locking. Property-tested with Hypothesis.
API FastAPI · Pydantic v2 /api/accounts · /api/transfers · /api/journals · /api/reports. OpenAPI 3.1 auto-generated.
Persistence SQLAlchemy 2.0 · Postgres Alembic migrations, SERIALIZABLE isolation, DB triggers enforcing balance + currency + sign.
Observability Prometheus · OTEL · Grafana Counters, histograms, OTLP trace export; pre-provisioned Ledger SLO dashboard.
Tooling Typer CLI · Locust bench Recon import / export CLI, Locust load-test harness, CI on GitHub Actions.
Runbook Docker Compose · Alembic MIGRATE_ON_START=true auto-runs Alembic at boot; JSON logs via Loguru when LOG_FORMAT=json.