In production · since 2026
Kassensturz
My wife and I settled our shared expenses on paper every month. I turned that into an app that now runs our whole household settlement and my finances.
Problem
My wife and I settled shared expenses on a paper sheet every month. Whatever was left we pushed into the next month with an arithmetic trick, and when a receipt turned up later, nobody knew which balance was right. My own finances were scattered across several banking apps.
My role
I designed and built the solution, and I run it in my homelab.
Case study
My solution
I built the settlement the way bookkeeping works: a closed month is frozen, and the open amount between us is the sum of immutable entries. Payments and later corrections are entries of their own with a reason, never changes to the past.
- Settlement periods with closing, fixed-cost templates and PDF and CSV export
- Account access via PSD2, strictly read-only, receipts from my Paperless
- I classify bookings with rules, a suggestion inbox and a locally trained model, without an external AI
- I enter cash on the go as a PWA; plus year comparison, forecast, budgets and subscription detection
Implementation
Classification always starts with rules I can read myself. A rule is a pure function without a database, and only when none matches does the model suggest something:
Kassensturz.Api/Domains/Classification/Application/RuleEngine.cspublic static ClassificationRule? FindMatch(IEnumerable<ClassificationRule> rules, BankBooking booking) =>
rules
.Where(rule => rule.IsEnabled)
.OrderBy(rule => rule.Priority)
.ThenBy(rule => rule.Id)
.FirstOrDefault(rule => Matches(rule, booking));It is built with ASP.NET Core, EF Core and PostgreSQL, the interface with Angular. Every push runs through tests, an API contract check and end-to-end tests before GitHub Actions rolls out over our private network; afterwards an automated acceptance checks the running app.
Decisions
- The balance is a ledger
- I do not store the open amount; I compute it from immutable entries. More rows and a bit more logic, but every balance can be explained, even months later.
- Our data stays apart, even when I make a mistake
- Private data carries an owner without a database default. If I forget it in code, there is an error instead of a booking with the wrong person. The price is passing the owner everywhere.
- AI without running costs
- I rely on rules first, then on a locally trained model. Suggestions apply automatically only with a proven hit rate and never for bookings that touch our shared settlement. Some of it stays manual.
Outcome
My wife and I have used Kassensturz every day since July 2026. Settled months stay unchanged, open amounts can be explained at any time, and my accounts flow in automatically, read-only. Behind it are 12 domains, around 850 backend tests and 23 end-to-end tests.