Marketplace Listing Helper: A helper that never presses Publish - Part 1
~6 min read
I had a few dozen things to sell, mostly Warhammer models, and a spreadsheet describing them. Facebook Marketplace wanted each one typed into a form, one at a time: title, price, category, condition, description, photos. None of it is hard. All of it is repetitive, and repetition is exactly what a computer should be doing for me. So I started building a Chrome extension to do the typing, and the first decision was about what it must not do.
The rule everything else hangs off
The
Childcare calculator: From two childminders to four - Part 2
~7 min read
Last time the calculator had exactly two childminders, written into the markup as "A" and "B". That matched my own situation, but it's a poor assumption for a tool other people might use. This post covers the commit that replaced it with one to four childminders, and what that did to the code.
The assumption was everywhere
Two childminders sounds like a small thing to hardcode. In the first version it showed up in:
* the markup, as two pairs of name and rate inputs
* the sched
Central login service: Moving configuration into the database - Part 6
~25 min read
By the end of Part 5, the service was doing real work: apps behind the proxy used it for forward-auth, and Jenkins signed in through it with OpenID Connect. What it wasn't was pleasant to operate, and the reason was the same in both of its configurable parts.
There were two pieces of configuration that changed over time:
* The apps directory: the cards on the landing page, held in apps.json.
* The OIDC client registry: the relying parties allowed to sign users in, held in oidc_
Personal finance aggregator: Handing authentication to identity-system - Part 9
~8 min read
Post 6 ended with a login system I was reasonably pleased with: Argon2id passwords, opaque server-side sessions, TOTP two-factor with recovery codes, a password-change form that borrowed login() to verify the current password. It had also, by the end of that post, hit two gaps in the library underneath it - no way to update a user's name or email, and no built-in current-password check - that I'd worked around rather than fixed.
The next day I deleted all of it. This post is about
House-move inventory: Why a spreadsheet wasn't enough - Part 1
~7 min read
We're moving from the UK to France, and the removals company, the French customs form and my own sanity all want the same thing: a list of what is in every box. Not a rough list. Which box is the kettle in? Did every box that left the house actually arrive? What's the declared value of the kitchen? Which boxes are we not taking after all?
The obvious tool is a spreadsheet, and I started with one. This post is about why I stopped, what I built instead, and the few decisions in the f
Childcare calculator: Why a static page, and why not a spreadsheet - Part 1
~6 min read
Childcare costs are one of those bills that look simple until you try to predict them. A childminder charges an hourly rate. You book some days. The government funds some of the hours. Bank holidays don't count. And somehow the number on the invoice is never the number you guessed.
I wanted a way to type in my setup and see the month's cost, day by day, before the invoice arrived. This post is about the first decision: what to build it with, and what the first version actually did.
Central login service: Making Jenkins sign in - Part 5
~20 min read
Part 4 ended with an OpenID Connect provider that passed its test suite, covered the whole Authorization Code flow, required PKCE, hashed its client secrets, and was, by the specification as I'd read it, correct.
Then I pointed Jenkins at it, and the next half hour produced three commits with titles like fix jenkins login, fixing the fix further and Adding preffered username. This part is about those three commits. They're short (the three of them touch well under a hundred lines
Personal finance aggregator: Account balances and disconnecting banks cleanly - Part 8
~10 min read
The next two pieces of this project both came from the same question: does the app actually reflect the full state of my accounts, in both directions - what's currently in them, and which ones I've actually chosen to keep connected. This post covers pulling real balances into the dashboard, and building a way to disconnect a bank that doesn't leave stale data lying around pretending to be current.
Balances were already paid for
TrueLayer's Data API has balance endpoints - /data/
Central login service: Becoming an OpenID Connect provider - Part 4
~28 min read
Everything so far assumed one thing: that the app I'm protecting can sit behind my reverse proxy. The proxy asks the login service "is this request allowed?", the answer arrives as a status code and a few headers, and the app never has to know how any of it happened.
Then I wanted Jenkins to use the same accounts. Jenkins can't play that game. It isn't a little app I wrote; it has its own idea of users, sessions and security realms, and it ships with a plugin that wants to speak O
Personal finance aggregator: Keeping data fresh: manual sync, resilience, and real stats - Part 7
~10 min read
Everything up to this point assumed the nightly sync just works. It mostly does - right up until one bank connection has a problem, at which point the version of fetch() I'd had since the very first CLI prototype turned "one bank's data is temporarily unavailable" into "nobody's data updates tonight." This post is about the sync loop growing up: a manual trigger for when 3am is too long to wait, failure isolation so one bad connection can't take the rest down with it, error message
Central login service: An admin page and a proper frontend - Part 3
~25 min read
By the end of Part 2 the login service was deployed and doing its job, and its entire user interface was one HTML form built from a Python string. That was fine for a service whose only visible surface was "type your password". It stopped being fine within a day, because I immediately wanted three things the form couldn't give me:
1. A way to see who has an account and when they last signed in.
2. A landing page listing the apps that sit behind the login, so the service is somew