A wallet that cannot lose money.
walletflow is the rewrite of Fincra's core wallet and ledger as a Bun/Hono DDD service over an immutable Formance double-entry ledger. Balances are never stored — only derived from postings. Every movement is idempotent, every overdraft is rejected at the ledger, and every book foots to zero.
Architecture map
What the system is
DDD + hexagonal layering, the Formance split, and the double-entry model: accounts, classes, normal balance, and why there are no balance columns.
02How it works
The request lifecycle, pay-in and the payout saga, the COA→GL→Numscript pipeline, FX/GPS/stablecoin/corrections, and the async outbox.
03How to run it
Deploy, the migration/cutover order, the four reconciliation gates, observability, the env contract, and the automated Formance test harness.
04The operating console
The /ops cockpit built into the service: every wallet/ledger movement as an idempotent command, the hold lifecycle, live recon gates, flags — and the playbooks for operating them.
05Operate the UI
A task-first field guide to /ops: every operator workflow walked end to end — the form fields, the success card, the exact rejection string — drawn from a full console click-through.
ΔBefore / After
The case for the rewrite, argued side by side: the legacy failure classes (TTL'd dedupe, check-then-set races, mutable balances) against walletflow's permanent claims and double-entry postings.
Money & Currency
Value objects; authoritative ISO-4217 scale table; rejects unknown currencies (no guessed scale).
Ledger
Postings, account naming, Numscript builders, the monetary→minor-unit conversion.
COA
GL-code→account resolver, account-class normal balance, the address classifier.
CoreWalletService
All use cases — pay-in, transfer, payout hold/settle/release, FX, GPS, stablecoin, rolling reserve, corrections — each idempotent.
CoaService
CSV ingest, Ledger Template.csv generation, drift detection.
ReportingService
Trial balance, ALM views, hold & trial-balance reconciliation.
Port interfaces
LedgerPort, ProviderPort, TemplateCatalogPort, CoaCatalogPort — the domain's only view of the outside.
Formance adapter
HTTP client to Formance v2; postings + Numscript; balances; revert; paginated account listing.
Provider adapter
HTTP payout rails behind a per-provider registry; ambiguous-state signalling.
Postgres repos
Command state, idempotency keys, holds, outbox, COA, read models, feature flags.
AWS
SigV4 signing, credential chain, outbox relay → SNS publish / Step Functions start.
Outbox & statements
Transactional-outbox relay and the statement read-model rebuilder.
18 Numscript templates
pay-in, payout, transfer, holds, FX, GPS pool + cross-currency, stablecoin, counterparty, rolling reserve, correction, migration, bank funding.
COA + GL seed
Bundled chart of accounts and event→GL mappings derived from the RTCP spec.
Payout saga
Step Functions + Lambdas: submit → requery → settle/release → dead-letter → manual review.
Ops console
Server-rendered hono/jsx + htmx 4 cockpit at /ops; 17 idempotent commands, holds, gates, flags.
Testcontainers
Ephemeral Postgres + a real Formance ledger; the production script-mode path verified end to end.