- Svelte 35.6%
- Go 29.3%
- CSS 22.5%
- TypeScript 11.8%
- Dockerfile 0.4%
- Other 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| cmd/daybook | ||
| internal/daybook | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| compose.yaml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
Daybook
A personal cash-basis ledger with Vim-style navigation, a Barclays CSV importer, and an InvoicePlane invoice register. Go, Svelte/TypeScript, and SQLite. The built frontend is embedded in the Go executable.
Run locally
Requires Go 1.25 or newer and Node 24 or newer (use the versions in the Dockerfile for reproducible container builds).
make build
./daybook
Open http://127.0.0.1:8734. Import files from the Imports screen. The default database is $XDG_DATA_HOME/daybook/daybook.sqlite, falling back to ~/.local/share/daybook/daybook.sqlite. Use -db to choose another location.
You can also import from the command line:
./daybook -import-bank /path/to/data.CSV -import-invoices /path/to/billing.sql
Add -import-only to import and exit. Source files are read without being modified. Raw files are not copied into the project or retained by the application. The SQL importer reads only invoice-related INSERT data and never executes SQL from the dump; account credentials, application settings, and unrelated customer fields are excluded from the stored model.
Review workflow
- Import the bank export and InvoicePlane snapshot.
- Review bank entries as business income, business expense, personal, or own-account transfer. Set category, business share, and notes where needed.
- Link an income entry to an InvoicePlane payment when the amount matches. Suggestions look within ten days; check the invoice before accepting. Each payment can be linked once.
- Mark invoice lines as service/fee or client-billed expense. This is a review annotation, not an additional deduction.
- Save repeatable description rules from the detail pane. Apply rules from the Rules screen after reviewing the match count. Applications affect only unreviewed transactions in the selected year and can be undone as a batch.
- Check the year summary and unmatched invoice payments. Export the review CSV for a transaction-by-transaction record of included amounts and their source lines.
Focus view shows one transaction at a time with large text and review controls. It is the default; switch to List view for the ledger and batch selection. Your choice is remembered in the browser. Saving a classified entry advances through Focus view, including when viewing all entries.
j/k move; gg/G jump; / searches; b prepares a business review and highlights the category text for replacement; i prepares income. With one suggested invoice payment, Enter accepts the displayed link and saves income. With no match, Enter saves income without a link. With several matches, choose a displayed payment explicitly (keys 1–9 select and save); no first match is accepted automatically. f switches Focus/List view; p marks personal; t marks transfer; x selects for batch review in List view; u undoes the last review; ? opens shortcuts. Shortcuts are inactive in input fields. Tab reaches controls normally. Undo covers transaction reviews, batches, and invoice-line reviews globally, including actions in another year. Rule creation/deletion and file imports are not part of the undo stack.
Paper/ink themes and compact spacing are available in the interface. Shared visual tokens live in web/src/theme.css; shared Svelte primitives start in web/src/lib. Theme and spacing preferences live in browser storage; financial data and review decisions live in SQLite.
Accounting model and current scope
The cash-basis view includes income received and expenses paid within the selected tax year. This follows HMRC's cash-basis recordkeeping guidance. The app does not infer whether a transaction is allowable. Categories are editable organisational labels, not a mapping to Self Assessment boxes.
- Monetary values are stored as integer pennies. Business share uses basis points, with half-penny rounding away from zero per transaction.
- Unreviewed, personal, and transfer entries contribute nothing to confirmed totals. A positive entry classified as an expense reduces expenses (a refund); a negative income entry reduces income.
- InvoicePlane payments provide reconciliation context. They are not added to the bank totals; otherwise the same receipt could be counted twice. Billed expense lines do not subtract from profit separately from the underlying bank expense.
- Confirmed totals cover the imported bank dataset only. There is no manual cash-entry workflow, VAT accounting, tax calculation, return submission, capital-allowance schedule, or arbitrary multi-category split yet. A business/personal percentage split is supported.
- The initial payment matcher supports one bank entry to one recorded payment of the same amount. Partial payments, grouped deposits, foreign currencies, and many-to-many reconciliation need manual investigation outside this version.
- Statement completeness is not inferred from the first and last transaction dates. The coverage checkbox is a browser-local reminder only, not an audited financial record.
- Reimports identify bank entries using date, account, amount, type, memo, and occurrence number. Exact duplicates within one export are preserved. Reimporting the same or overlapping full-day exports does not duplicate them. An export containing only a subset of otherwise identical same-day entries is ambiguous; preserve full-day exports. Corrected descriptions are treated as new entries and require review.
- Invoice snapshots update records by their original InvoicePlane IDs and preserve line classifications. Snapshots are cumulative; records absent from a later dump are not deleted. Use one InvoicePlane installation per Daybook database.
- The Barclays importer expects UTF-8 and the six columns
Number,Date,Account,Amount,Subcategory,Memo. It reports extra unquoted memo commas while preserving the memo. Imports with malformed dates or amounts fail atomically.
Self-host with Docker
docker compose up --build -d
The compose file publishes on loopback only. Daybook is a single-user application without its own login. For access over a network, put it behind your authenticated reverse proxy and set DAYBOOK_PUBLIC_HOST to the public hostname (without scheme or port). The executable also accepts -public-host with a comma-separated list. Unknown Host headers are rejected, API responses are not cached, and mutations require a per-process request token.
Back up the SQLite database before upgrades. Stop Daybook before copying its database, or use SQLite's online backup API/CLI .backup; copying only the database file during a live WAL session can omit recent changes. Keep the database and original exports out of version control. Review history and annotations are in the database.
Development and validation
make check
cd web
npm test
Browser tests require the Playwright Chromium browser (npx playwright install chromium) and a built frontend. They use a temporary database and synthetic financial fixtures. They check imports, duplicate prevention, reviews, payment linking, rules, undo, reload persistence, CSV downloads, themes, keyboard navigation, and mobile overflow. Go tests cover monetary parsing/rounding, SQL escaping and filtering, cash-basis boundaries, duplicate identity, atomicity, refunds, and request protections.
For frontend development, run go run ./cmd/daybook from the repository root and npm run dev from web in another terminal. Vite proxies /api to the local Go server. Production uses the embedded build and needs no Node runtime.