Bean Counter
A household budgeting app for two people. It reads supermarket e-receipts and bank exports into a database and shows joint and personal budgets, a yearly plan and spending analysis, with each person's private spending kept private.







What it does
- Imports receipts from the supermarket's rewards site and bank exports (the formats of several Australian banks are recognised). Bank rows are classified, categorised by merchant rules and matched to receipts by amount and date, only when the match is unambiguous.
- Splits spending into joint and personal. A joint-card line can be marked as a personal item that one person reimburses, and a shop paid on the wrong card can be moved. Corrections are stored separately, so reloading data never loses them.
- Plan: each category against its target, per month or per year for lumpy bills such as rates, with pace, projection and a twelve-month strip. A yearly plan is proposed from last year's spending. Years are financial years, July to June.
- Analyse: spending by category over time, grocery spending by item category, item prices and specials, and every receipt with its lines.
- Manage: upload, a month-close routine run after each upload (uncategorised payments, large one-offs to tag, receipts with no bank match), merchant and item rules, and a change history.
- Money: a finance section covering holdings, share parcels with capital gains, net worth over time, interest for the year, a twelve-month cash-flow forecast, a drawdown projection of how long savings last under scenarios you can change, and tax-time figures. Each person sees only their own items and the joint ones.
- Now: a one-column daily snapshot made for a phone. Cash today is each account's last imported balance moved forward by what that account usually spends (its own weekly history over the last three years), shown with a range that is right four times in five. Below it are the share portfolio's value and recent change, what is due in the next two weeks, and this month's budget.
Under the hood
- TypeScript that Node runs directly by stripping types, with no build step. One Cloudflare Worker serves the API and a vanilla-JavaScript front end, with a D1 database (SQLite) and an R2 bucket. Money is stored as integer cents.
- Behind Cloudflare Access, with the Worker verifying the token itself and allowing only the two named people. A local stub login exists for development and refuses anything that arrived through Cloudflare's edge.
- Who can see what is decided in one function. Every receipt and bank query goes through it and it can only narrow the result. A test calls every read route as each person and checks that the other's private markers never appear.
- Uploaded originals are encrypted with AES-GCM before they go to R2. The database itself is left readable so budgets can be plain SQL sums. Budget totals come from a database view, and a payment split across categories subtracts from the original and adds a row per part, so totals never change.
- A small Firefox extension reads receipts inside the logged-in tab and sends them on, so the retailer's login never leaves the browser.
- A second, tiny Worker exposes a read-only summary to another of my apps, and accepts only an Access service token.
- The charts are hand-written SVG, and the app calls no third party except a daily call for share prices, which sends only a ticker code and a date range.
- The real data lives only in the private database. The tests use generated households, including a whole invented year pushed through the real code and checked so that every cent is accounted for.
A private tool for one household, not available to use. Screenshots use a generated household with invented merchants, accounts and amounts.