Self-hosted double-entry fund accounting

Books that fit one nonprofit exactly.

cuento is fund accounting for a small, multi-subsidiary nonprofit -- one Go binary and one SQLite file, serving bilingual server-rendered HTML. Money is tracked exactly, every change is versioned, and the reports a Form 990 preparer needs come out directly.

  • One binary
  • One SQLite file
  • Bilingual en / es
  • MIT licensed

See it running

A live instance seeded with a fully synthetic multi-subsidiary nonprofit -- several years of multi-currency transactions, restricted funds, a capital campaign, budgets, expense reports, and bank reconciliations. Every value is invented, and the database resets to a pristine copy every hour, so you can click around, edit anything, and break nothing real.

Open the live demo →

Sign in with any of the demo accounts below -- each shows a different permission level.

Username Password Role
admin demo-admin Administrator -- full access
submitter demo-submit Expense submitter -- write and submit
viewer demo-view Read-only viewer
campdir demo-camp Program-scoped viewer (Educacion subtree only)

What it looks like

Seven screens from a live instance. Flip the switch to see the same pages for a user whose preferences are the opposite in every axis -- Spanish instead of English, dark instead of light, dd/mm/yyyy instead of mm/dd/yyyy, 1.234,56 instead of 1,234.56, debit and credit columns instead of one signed amount, compact instead of comfortable. Nothing is re-skinned for the screenshot: those are per-user settings, saved on the preferences page, and every screen honours all of them.

  • The transaction editor

    A keyboard-driven split grid. The header account carries the balancing residual, so you type only the lines you know; every split takes a fund, a program and -- on an expense -- a functional class, and the transaction must balance to zero both overall and within each fund before it will save.

    The transaction editor in cuento, English interface on the light theme. The transaction editor in cuento, Spanish interface on the dark theme.
  • Chart of accounts

    The account tree with as-of balances, per subsidiary and per currency, collapsible to any depth. Accounts are tagged for the roles the ledger cares about -- cash, receivable, intercompany, reconcilable -- and can be merged, deactivated or renamed with the full history kept.

    Chart of accounts in cuento, English interface on the light theme. Chart of accounts in cuento, Spanish interface on the dark theme.
  • Statement of position

    The balance sheet as of any date, consolidated across the subsidiary tree and converted to one reporting currency, with a comparative column for each prior year-end. Intercompany balances are eliminated after a net-to-zero check; every account figure drills through to the transactions behind it.

    Statement of position in cuento, English interface on the light theme. Statement of position in cuento, Spanish interface on the dark theme.
  • Statement of activities

    Revenue and expense over a period, here at quarterly granularity -- one column per quarter plus the total. Month, quarter and year granularities are a single control, and the statement narrows to one fund or one program subtree without leaving the page.

    Statement of activities in cuento, English interface on the light theme. Statement of activities in cuento, Spanish interface on the dark theme.
  • Program statement

    Accounts down the side, the program tree across the top, with management and fundraising as their own columns. Parent programs roll up their subtrees and collapse to a single total, so a ten-program org still reads on one screen.

    Program statement in cuento, English interface on the light theme. Program statement in cuento, Spanish interface on the dark theme.
  • Fund cover page

    The page a grant report starts from: one fund from inception, with received, spent and remaining across the top, then the receipts, the spending by account and the assets still held -- reconciled to the fund's closing balance.

    Fund cover page in cuento, English interface on the light theme. Fund cover page in cuento, Spanish interface on the dark theme.
  • Everything you can reach

    The "All" landing: every page in the app on one screen, grouped by area and gated by permission, so a read-only viewer simply never sees the cards they cannot open. It is also the whole surface at a glance -- the ledger, funds, programs, reconciliation, budgeting, expenses, bank import, the report catalog, settings and administration.

    Everything you can reach in cuento, English interface on the light theme. Everything you can reach in cuento, Spanish interface on the dark theme.

Captured from the public demo database -- a fully synthetic multi-subsidiary nonprofit. Every name, figure and date is invented.

What it does

A complete fund-accounting ledger -- not a demo. Every capability below maps to built, tested functionality, documented in depth on the features page.

  • Double-entry fund accounting

    Every transaction balances to zero in its currency and to zero within each fund, so donor-restricted money is conserved through every account it touches -- cash, buildings, or loan principal.

  • Donor-restricted funds

    Funds carry funder, purpose, and restriction metadata and drive the with/without-donor-restrictions presentation in reports -- derived from fund tagging, not journaled transfer entries.

  • Multi-subsidiary + consolidation

    Subsidiaries form a tree with per-entity base currencies. Reports scope to any subsidiary consolidated with its descendants; intercompany accounts collapse after a net-to-zero check.

  • Multi-currency, GAAP FX

    Amounts are exact integer minor units. Report-time FX follows ASC 830: monetary remeasurement recognized in income (830-20), foreign-entity translation held in equity as a CTA (830-30).

  • Programs + functional classes

    Revenue and expense splits carry a program (990 Part III) and, for expenses, a functional class -- program, management, or fundraising (990 Part IX) -- as orthogonal reporting dimensions.

  • Report catalog with drill-down

    Trial balance, balance sheet, income statement, functional expenses, fund activity, FX detail, and the Form 990 package. Every figure drills to its contributing splits and exports to CSV.

  • Bank statement import + reconciliation

    Bank data enters only as an uploaded file -- a CSV you map column by column, or an OFX/QFX statement that names its own fields and deduplicates on the bank's transaction id -- staged for review through the same ledger invariants as manual entry. No bank credentials are ever stored.

  • Budgeting + expense reports

    Budgets as projected dated splits, kept out of the ledger, driving per-fund cashflow projection and budget variance. Plus a submit-then-review expense workflow decoupled from book-editing.

  • Append-only audit trail

    Every change appends a full-snapshot version row, attributable to the acting user. Transactions are voided, never hard-deleted; no code path rewrites history.

Why a purpose-built ledger

cuento is a bespoke, single-organization tool -- not a general package. For a small nonprofit whose books have to be exactly right, owning an open ledger beats renting a generic one.

ConsiderationcuentoGeneral accounting SaaSEnterprise fund accountingSpreadsheets
Data ownership You host it; one SQLite file you own outright Hosted; your books live in their cloud Hosted; your books live in their cloud You own the files
Fund-level conservation Balances to zero within each fund, not just the top line Class/tag tracking, top-line restriction reporting True fund dimension Manual and error-prone
Double-entry + append-only audit Enforced on write and re-checked; history never rewritten Double-entry; edit history varies Double-entry with strong audit No enforcement
Multi-currency GAAP (ASC 830) Remeasurement to income + CTA in equity, at report time Limited or add-on Comprehensive Hand-rolled
Cost & lock-in Open source, MIT; no subscription, no vendor lock-in Subscription; closed data; migration friction Subscription; higher cost; closed data Free; no lock-in
Fit Built to fit THIS org exactly Configure your work to fit it Configure your work to fit it Fits anything, guarantees nothing

Claims about other tools are kept high-level and defensible; the point is not that they are bad, but that a purpose-built, self-hosted, open ledger is the right trade for one organization that needs its books to be exactly right.

Built to fit one nonprofit exactly

cuento is a customized, single-organization tool, not a general-purpose accounting package. Its data model and its design decisions are deliberately tailored to one nonprofit’s real workflows (name-keyed receivables, this organization’s fund / program / subsidiary structure, cash-flow-first budgeting) rather than trying to fit every organization. It is not configurable into someone else’s chart of accounts, tax regime, or bookkeeping process, and does not try to be. Building a bespoke line-of-business tool this complete – one that fits the organization exactly instead of forcing the organization to fit the software – is what AI-assisted coding now makes practical: the marginal cost of a purpose-built system has fallen far enough that adapting the work to a generic package is no longer the only sensible option.

cuento is intentionally small and boring. It runs as a single process on a single VM behind TLS, with the entire database in one SQLite file. There is no framework, no bundler, no CDN, no external database, and no bank connection – bank data enters only as a file the user uploads, a CSV or an OFX/QFX statement.

It targets two adversaries: authenticated misuse (a logged-in user doing something they should not) and commodity automated web attacks. It does not try to defend against a nation-state attacker, a malicious VM host, or an attacker with filesystem access to the server; those are handled operationally (disk encryption, VM hygiene, off-host backups), not in the application.

Under the hood

Deliberately small and boring: no framework, no bundler, no CDN, no external database, no bank connection.

  • One Go binary, one SQLite file -- pure-Go driver, CGO-free static build
  • Server-rendered HTML with a pinned, vendored copy of htmx
  • Strict Content-Security-Policy -- no inline script or style
  • Forward-only migrations as the source of truth, reads via sqlc
  • A single write funnel -- every mutation records a change and a version row
  • Ledger invariants enforced on write and re-verified by cuento check

Guiding constraints

Four commitments shape every design decision. They are enforced structurally and by tests -- not left to convention.

  • Money is exact

    Amounts are integer minor units, never floating point in stored values or ledger math. Exponents from zero to four are supported at no cost.

  • Every change is versioned

    Each write lands in an append-only audit trail, attributable to the acting user. History is added to, never quietly overwritten.

  • Restricted funds are conserved

    Every transaction balances to zero within each fund, not only overall -- so donor-restricted money is conserved through every account it touches.

  • Form 990 reports come out directly

    The functional-expense matrix and program-level statements a preparer needs are produced by the application, not reconstructed by hand.

An open ledger you own outright.

One binary, one SQLite file, MIT licensed. Read the code, run it yourself.

View on GitHub →