In September I scaled every JobFilter production workload to zero replicas and deleted the four autoscalers so nothing could bring them back. Then the namespaces, the postgres cluster, the backup bucket, and the DNS records. Nothing answers at jobfilter.ai now. The code is still in the repo, and the reasoning behind all of it was shorter than the action it took: there was no external consumer, and there never had been one.
What I shut down was a free developer preview — an authenticated search API
over the Swedish hiring market, stateless MCP tools on top of it, open
self-service registration, and revocable jobs:read tokens, with per-account
limits of 60 requests a minute, 1,000 an hour and 5,000 a day. Production ran
three postgres instances with synchronous quorum on a three-node HA cluster,
backed up to object storage with a rehearsed restore path. None of it was sized
for the traffic it got.
This is the second time I've taken part of JobFilter apart. In July I deleted roughly 66,000 lines — CV parsing, matching, cover letters, Stripe billing — and kept the corpus layer, because the moat was never the matching (the decision tree). That pivot was right, and it is still right. What it didn't touch was the customer question: I changed what I was building and left the shape of the business alone.
There is one thing I could have settled by reading a license before writing the code. Arbetsförmedlingen's job data is open, keyless, and non-exclusive, non-transferable and non-sublicensable. I can consume the feed; I cannot relay or resell it. The corpus was never going to be a data product no matter what I wrapped around it, which left the judgment layer — whether a listing is real, current and deduplicated across sources — as the only part that wasn't a commodity. That is a real skill and a narrow product. Narrow enough that the only consumer I could name on paper was my own business-development pipeline, which reads other tools and never called the corpus.
So the failure mode repeated one level down. The first JobFilter was built for job seekers who didn't come; the second was built for developers who didn't come. Both times I sized the scope to a consumer who didn't exist, and both times the cost was real: a three-node HA estate, three synchronous postgres instances, nightly backups and a governed enrichment pipeline, holding up a surface nobody called.
The pivot was a good call. That is what makes the second shutdown worth writing down — it never showed up as a bad decision at any point. Every step was defensible, the moat reasoning held, and the customer question sat unasked through all of it.
What survived is the part that doesn't run: the deduplication, staleness reconciliation and verification logic, plus the accumulated notes on how this market actually reads. It is in the repo. Re-opening the hosted service takes a demonstrated consumer and a new decision, not a revert.
The rest left with it. Two of the three estate machines are powered off and the cluster runs on one node now, because the workload that justified etcd quorum was the one I had just switched off.