Skip to content
Work

FediBitcoin infrastructureLive since Jun 2026

Fedi's portal was rebuilt on the current platform with no DNS change and a byte-identical history

Fedi's portal drifted 170 schema versions behind. We rebuilt it on the current platform with no DNS change and re-grafted 21 custom surfaces.

21route segments written for this client and no other
Build artefact

Found by diffing the client's repository against the platform and every other client.

131documents indexed in the brain
System loggedAug 2026
3 of 3conversations migrated, byte for byte
Build artefact

14 messages and 9 read receipts, compared after the move rather than assumed.

Key highlights

  1. 01A portal 170 schema versions behind, rebuilt underneath its users with no DNS change.
  2. 02Message history moved and verified byte for byte rather than assumed.
  3. 03No client account has signed in to the rebuilt portal. We print that too.

The build

Platform, then what was written for this client alone

Shape

A client fork of the platform, with surfaces written on top of it

Arrived in the box
500
database migrations
69
built-in skills
17
provisioning steps

Build artefactAug 2026

Written for this client

17route segments that exist in this client’s repository and in no other, and not in the platform

  • (dashboard)/release-docsEvery release, and the documentation drafted for it, waiting on approval
  • (dashboard)/release-docs/[id]One draft, its revisions, and the bar it has to clear
  • api/release-docs/generateDraft the documentation for a release
  • api/release-docs/generate/releasesWhich releases are eligible to be drafted
  • api/release-docs/[id]/approveA person says yes, and only then does it count
  • api/release-docs/[id]/rejectA person says no, with the reason attached
  • api/release-docs/[id]/contentThe draft itself, editable before it is approved
  • api/webhooks/githubA signature-verified door, so the job starts from a real event
  • api/webhooks/github/releasesThe release event specifically, which is the only one that matters here
  • (dashboard)/zendesk-suggestionsProposed help-centre updates, none of which publish themselves
  • (dashboard)/zendesk-suggestions/[id]One proposal, with a diff, a preview and an editor
  • api/zendesk-suggestions/[id]/itemsApproval per item rather than per batch, because a batch hides the bad one
  • (dashboard)/llm-costsWhat the model spend actually was, per surface
  • api/llm-costsThe same numbers, for anything that wants to read them
  • api/cron/zendesk-mirror-seedFirst fill of the mirror
  • api/cron/zendesk-embed-refreshAnd keeping it current after that
  • api/cron/digest-backfillSeeds the recaps a new tenant would otherwise have to wait a week for

Found by diffing this repository against the platform and every other client

Before

  • 01The fork had drifted about 170 schema versions behind the platform.Everything still worked. It just could not receive anything new, which meant the client was paying for a product that had stopped arriving.
  • 02The custom pages looked bolted on, because they were.
FIG. 01
The rebuild, and what happened after it. Source: repository history and the tenant database, read 2026-08-20.
ItemValue
Schema versions behind at auditabout 170
Route segments written for this client21
Conversations migrated, verified byte for byte3 of 3
DNS changes required at cutover0
Documents indexed in the brain131
Client accounts signed in to the rebuilt portal0
The rebuild, and what happened after it. Source: repository history and the tenant database, read 2026-08-20.REV 2026.09

Situation

Everything worked. Nothing new could arrive.

Fedi's portal was forked from our platform in April 2026 and then did what forks do. The platform kept moving. The fork did not. By the time we looked at it properly, it was roughly 170 schema versions behind.

The thing about that kind of drift is that it produces no symptoms. Every page loaded. Every custom workflow ran. Nobody filed a bug, because there was no bug to file. What there was instead was an absence: every improvement shipped to every other client in those months had stopped arriving here, and the client was paying for a product that had quietly stopped being delivered.

What we built

A rebuild, underneath the people using it

We chose to rebuild rather than migrate. Migrating 170 versions of schema across a fork with its own tables is a sequence of small risks, each of which has to go right, and the end state is a database nobody fully trusts. A rebuild has one large risk, which is the cutover, and you can plan a cutover.

So: the current platform, a fresh database, and the custom work grafted back on. Twenty-one route segments that exist in this client's repository and in no other and not in the platform. Release documentation drafted from a signature-verified webhook and held behind an approval. Help-centre updates proposed with a diff and a preview and approved one item at a time rather than in a batch, because a batch hides the bad one. A dashboard for what the models actually cost, per surface.

The cutover needed no DNS change at all. The portal is served through a shared router, so moving it meant changing three database pointers and the brand variables. Nobody watching had a reason to notice.

The history moved with it, and we checked rather than assumed: three conversations, fourteen messages, nine read receipts, compared on both sides afterwards. The old database was kept intact as a one-command way back, which we did not need and would have wanted badly if we had.

How it runs

Nothing publishes itself

Every one of the three workflows ends at a person. A release event produces a draft, not a document. A proposed help-centre change produces a diff, not an edit. Approval is per item, and the reject path carries a reason, because a rejection nobody explains teaches the system nothing.

One thing that came out of this build outlived the engagement. Release documentation was the first job on our platform that ran inside the application, rather than posting out to an external pipeline and waiting. That pattern is now how every in-application job is written, for every client. The work was paid for once by one company and every client since has ridden on it.

What we measured

A clean rebuild that nobody has opened

21
route segments written for this client and no other

Twenty-one bespoke route segments. A hundred and thirty-one documents indexed in the brain. Three of three conversations migrated with every message and every read receipt intact. Zero DNS changes at cutover. A twenty-nine page manual.

And zero client accounts have signed in to the rebuilt portal.

The engagement was stopped at the client's request during a restructuring in August, and we left the portal, the brain and all three workflows running rather than tearing them down. That is the context, and it is not an excuse. The rebuild was sound and nobody used it, and a case study that printed the first half of that sentence without the second would not be worth reading.

What the engagement did produce, permanently, is the in-application job pattern that every client now runs on. If you want the version of this with people actually using the thing, the two knowledge-brain builds are the ones to read: one here and one here.

What broke

Eight call sites compiled perfectly and would have failed the moment anyone used them.

Root cause
The fork's own tables collided by name with the platform's, so they were renamed into a namespace. Most references moved with the rename because a type-checker could see them. Eight could not: they were relationship names written inside query strings, which are text as far as the compiler is concerned. Text compiles. Text also does not exist at runtime.
Fix
Every query string was read by hand against the new names, because there was no tool that could have found them.
How we know
Each of the eight was exercised against the rebuilt database rather than type-checked. A green build had already told us they were fine once.

How it was delivered

  1. 01The decisionApr 2026
    • Fork audited against the platform
    • Rebuild chosen over a migration, and written down as a choice
  2. 02The rebuildJun 2026
    • Current platform, fresh database, custom work re-grafted
    • Message history moved and verified byte for byte
    • Cutover with no DNS change and a one-command way back
  3. 03The brainJun 2026
    • Public repository and release notes indexed
    • Retrieval checked against questions with known answers
James Booth
Engineer of record

What we did not measure

We did not measure time saved on release documentation or help-centre upkeep, because no baseline was recorded before the rebuild and inventing one afterwards would be worse than having none. The harder number is the one above: the portal, the brain and all three custom workflows are live, and no client account has opened them. The engagement was stopped at the client's request during a restructuring in August, and the systems were left running rather than torn down.

0client accounts that have signed in to the rebuilt portal
System loggedAug 2026

What to take from this

A fork that cannot take an update is already broken, even while everything on it works. The cost of the drift is everything the client stops receiving, and none of it shows up as an error.

Rename a table and the compiler will follow it everywhere except inside a string. Relationship names written into query text compile cleanly and fail at runtime, and no amount of green build tells you otherwise.

Migrate history by comparing it, not by trusting the copy. Counting three conversations, fourteen messages and nine read receipts on both sides took minutes and is the only reason we can say the move was clean.

Start free. Know your number in five days.

A 3 to 5 day audit of your operations, ending in a plan with the ROI math attached. No obligation.

5x ROI in 30 days. Or we work for free.