Numora, a double-entry accounting platform
Start a projectNumora is a multi-tenant, self-hostable double-entry accounting platform: a ledger core that enforces its own rules inside PostgreSQL, fourteen modules from invoicing to bank reconciliation, and a separate control plane that sells and provisions hosted subscriptions.
The challenge
Accounting software usually keeps its books honest in application code. That holds until something reaches the tables another way: a bulk import, a background job, a new module, a hand-written migration or a direct database session. Then a posted entry can be edited, a closed month can quietly change and a balance sheet can stop balancing, with nothing to say when it happened. For software that keeps a business's books, the ledger has to be the one part that cannot be talked out of its rules, whichever path the data takes.
What we delivered
Built with
What we built
We put the rules where every path has to pass them: in PostgreSQL. A deferred check refuses any entry whose debits and credits do not match at commit. Posted entries are append-only, enforced by both privileges and triggers, so a correction is a reversing entry rather than an edit. Closed periods refuse new postings, and each entry carries a hash linking it to the one before, so tampering shows.
We made posting safe to retry. Every post carries an idempotency key, so a request sent twice records once, and entry numbers come from a gapless counter per journal and year. Amounts are stored as whole minor units, never floating point, and invoices hold no stored totals at all: what a customer owes is always read back from the ledger.
Around the core we built fourteen modules that talk to the ledger only through a published SDK: sales, purchases, payments, tax, bank, inventory, fixed assets, projects and project billing, deferred revenue, parties, catalogue, payroll import and e-invoicing. The tax engine reads each rate and its accounts from a versioned country pack, and rates are never edited, only superseded.
Bank statements import from CSV, OFX, QFX, MT940 and CAMT.053 without double-counting a line imported twice. Rules match lines to accounts, every rule-made match can be undone in bulk, and a finished reconciliation is locked so the entries under it cannot be reversed by accident.
Each customer's books live in their own schema, with row-level security kept underneath as a second line. A separate control plane sells subscriptions, provisions books and suspends or resumes them. It treats every billing webhook as a prompt to re-check the customer rather than an instruction, so events arriving late, twice or out of order still end in the right state.
The parts that hold money are tested hardest: 184 test files, mutation testing on the ledger, money and tax code, and tests that check the database guards themselves are still in place.
The result
The result is a ledger that keeps its own rules: an unbalanced or edited entry, or one posted into a closed period, is refused by the database whichever path it arrives by, and a new customer can be signed up, billed and given their own books through the control plane.







