@corridor/ledger) is the system of record. Chains and partners are evidence; the ledger is what Corridor says happened, and reconciliation proves the two agree.
Accounts
An account is identified by its type and owner, scoped by corridor, asset and venue:
Named helpers (
Accounts.customer, .customerHold, .vault, .clearing, .revenue, .expense, .equity) open accounts on first use, so legs never build account ids by hand.
Invariants
Every entry balances per asset
The sum of debits equals the sum of credits for each asset in an entry. Checked before any write; an unbalanced entry never reaches the database.
No overdraft where it matters
Vaults and customer balances are flagged
no_overdraft. After applying postings, the natural balance of each such account is checked inside the same transaction, which rolls back on violation.Exactly once per external movement
unique (external_venue, external_ref) on journal entries. A Tempo log, a Solana signature or a partner order id can be booked once; a second attempt returns the original entry as a duplicate.Append-only
Database triggers reject
UPDATE and DELETE on the journal and postings. Mistakes are corrected with reversing entries, so the history is never rewritten.checkInvariants() re-verifies globally (every entry balances per asset; the balance projection equals a full recompute from postings) and runs in tests and on demand.
Schema
numeric(78,0) integer minor units, wide enough for any 256-bit on-chain amount. Balance rows are locked in a stable order before posting, so concurrent writers cannot deadlock.
Posting
usd-peg clearing pair nets to zero across a flow, and the console shows it doing so.
Storage
Locally and in tests the ledger runs on PGlite, Postgres compiled to WebAssembly, so a developer needs no database server. Production uses managed Postgres with the same schema and triggers. Both satisfy the same smallDb interface (query, exec, transaction).