Skip to content

Stash

Ledger companion: funding allocation, priced debt, parsed transactions.

A companion dashboard for Actual Budget, covering the views the ledger itself does not surface: what each obligation still needs this month, where the money should go next, and what the debt actually costs.

The boundary

Actual Budget owns the ledger. Stash reads and writes it through the Actual API rather than reaching into its database, so there is one writer and one schema owner, and an Actual upgrade cannot silently break the dashboard’s assumptions. Dashboard-only state, tax assignments, project reserves, the debt annotation log, lives in a separate SQLite file that Actual never sees. The dependency runs one way: Stash reads and writes Actual, and Actual never touches Stash.

Stash budget grid showing two months side by side, with per-person category allocation, funding shortfalls, and scheduled debt payments
Budget grid, two months at once. Each category carries a template rule, so the month can be funded in priority order rather than by hand; the anchor month flags a shortfall before it becomes an overspend.

Obligations before wants

Categories are annotated with template rules that classify them as obligations or flexible spend. Funding a month walks the obligations first and reports what is left, which turns budgeting from an arithmetic exercise into an ordering decision. Where a category is bound to a schedule, the due date drives the ask.

Stash debt view showing total debt, monthly payments, estimated monthly interest, prime rate, and a three-month upcoming payment matrix
Debt, priced rather than listed. Estimated monthly interest against the current prime rate, and a three-month obligation matrix so a payment is never a surprise.

Ingest, then confirm

Bank notifications are captured and parsed into proposed transactions with a merchant, an amount, and a mapped account. Nothing posts on its own: a parsed row sits in a review queue until it is confirmed, rejected, or marked duplicate. Where a rule does not match, the row says so and asks. The parser is per-institution and imperfect by design, which is exactly why the review queue sits in front of it.

Stash inbox showing eight captured bank notifications with status facets, parsed merchants, amounts, and mapped accounts
Captured notifications awaiting review, faceted by state. One row could not be mapped to an account and says so rather than guessing.

Stack

Next.js in an Nx workspace, React front end with API routes, the Actual API client in companion mode, and SQLite for dashboard-only configuration. Containerised and deployed to the home lab beside the Actual sync-server it reads, behind single sign-on.