At some point the bank started sending a text message for every card payment, which is to say the bank installed a sensor in my wallet and forgot to charge for it. A message arrives, a parser files it, and spending became a stream like temperature or particulates: graphable, alertable, and, like every other stream in this house, most interesting on the day it silently stops.
A bank's text message is, if you squint, a sensor. It fires on an event, it carries a value and a timestamp, and it is remarkably reliable right up until the day the bank changes its wording. So we treat it as one, with a watchdog on the sensor, because a spending log that quietly stops logging is worse than none.
Two paths, because one is a rumour
The texts are captured twice, on purpose. An automation app on the phone posts each one to a webhook the moment it arrives; the same app also forwards it to a mailbox, which is read on a schedule. The two paths fail differently, which is the entire point: the webhook needs the house to be up right now, the mailbox survives downtime and delivers late. The redundancy page covers the theory; this is the theory holding real money.
A third path catches what the texts structurally cannot: some payments never generate a message at all, the bank texts one card's transactions and merely app-notifies another class of them, so the phone's wallet and banking notifications are captured as their own stream and parsed like the texts are. It exists because the monitoring reported spending the first two paths could not have seen, which is the redundancy lesson arriving in person.
Records are deduplicated on a natural key (timestamp, amount, card), and each record keeps a note of which paths saw it. A payment seen twice is not a duplicate. It is a payment with a healthy escort.
A watchdog on the wallet feed
The failure mode of a spending tracker is not a wrong number, it is a smaller one. The bank rewords its messages, an OS update revokes a permission, a forwarding rule expires, and the ledger stops growing, which looks exactly like a frugal week. Nobody audits good news.
So the capture itself is monitored. If one path keeps seeing payments the other has stopped recording, that is a broken parser. If nothing at all arrives for longer than the longest real gap in the ledger's own history, that is a broken pipeline, however calm it looks. “A quiet month” is now a claim that requires evidence, like everything else in this house.
That watchdog earned an erratum within its first weeks, and the manner of it belongs here. When the capture grew from two paths to four, the paths were renamed; the watchdog kept counting the old name, which nobody wrote any more, and duly reported ten consecutive misses on a path that was healthy under its new name. A watchdog watching a ghost. Worse, its degraded state no longer contained the word its own alarm was listening for, so it counted faithfully and complained to nobody.
Both surfaced not through any monitor but through the question “do we actually receive all of this, on every path?”, answered by checking arrivals instead of design. The fixes: the watchdog now judges the paths that exist, every degraded state carries the alarm's trigger word, and the wallet's credit no longer depends on which of two notifications wins a three-second race. Rename a path and you have two systems to update; the one that watches is the one everyone forgets.
The security section nobody screenshots
Here is the awkward part. The webhook receiving these messages cannot be meaningfully authenticated: it has to accept posts from a phone out in the world, and a secret embedded in a public page's JavaScript is a secret in the way a note taped to the door is a lock. Pretending otherwise would be decoration. The honest model is that the endpoint is reachable, and everything arriving on it is hostile until proven boring.
So the defence lives at the two ends of the data's life: every string is sanitised on the way into storage, and escaped again at every point where the dashboard renders it. Both ends, because the failure being prevented is not graffiti on a chart.
A payment description is attacker-controlled text; whoever names a card terminal decides part of what my dashboard will try to display, and an unescaped render of that is how a spending chart becomes a way into the platform that runs the alarm. Cutting the chain twice means one mistake is survivable.
What it earns
Categories are assigned by rules and corrected by hand, the monthly shape emerges, and the building-management bills, which arrive on paper and leave by bank transfer, a channel no text message ever sees, get their own importer and a months-behind counter. “Did I pay those?” stopped being a feeling and became an integer with an alert on it.
The household now knows what it spends with the same precision it knows the bedroom CO₂. Whether this is an improvement in quality of life is a question we have agreed not to ask.