---
title: "Sync-RelayAllowlist.ps1 Deleted Domains That Were Still Live"
canonical: https://dxdev.com/blog/honest-migration-state/
datePublished: 2026-08-20
---
We told a client their site would be live on the new relay by Friday. Friday came and went.

After that we stopped writing status updates about outcomes and started writing them about state.

## What the relay actually does

We are mid-migration on a project that moves domain handling for one of our contracts, a legacy web application, onto a [public relay running on DigitalOcean](/blog/off-origin-caddy-relay-bare-domain-ssl-cloudflare-pro/). The relay runs Caddy with on-demand TLS. It does not pre-provision certificates. It mints one the first time it sees a real TLS handshake for a domain on its allowlist. No handshake, no cert. No cert, no site on the new path.

So the relay's job is only half the story. The app can push a domain onto the allowlist all day, but nothing completes until traffic for that apex domain reaches the relay, and traffic only arrives once the customer's DNS points there. We do not control that step. Their registrar, their delay in updating a record, their IT person who is out until Monday: that is the bottleneck, not our infrastructure.

We had been talking about the migration as a pure engineering timeline. It was an engineering timeline gated by a customer action we could not schedule.

## The sync script that deleted live domains

The allowlist taught us this before the messaging did. `Sync-RelayAllowlist.ps1` kept the relay's allowlist in step with the database, and we wrote it to make the file exactly match the DB. Whatever the DB said, the file said. That sounded correct, and it was the wrong turn.

The DB knows which domains exist. It does not know which of them have finished the customer-side DNS cutover. A sync that replaces the allowlist wholesale can drop a domain that is mid-migration: live on the old path, waiting on the customer, not yet current in the DB's view. Once it is dropped, the next handshake attempt fails outright. A domain that was fine yesterday breaks today, for a reason unrelated to anything we changed on purpose.

The fix was to stop treating "matches the DB" as the goal. The allowlist became append-only, a union of what the DB knows plus a separate supplemental file for domains where we own the timeline. Removal became a deliberate, reviewed action instead of an automatic side effect of a routine sync. The design note for the fix put it this way: removal is the direction that breaks things. Additions are cheap and safe. Deletions need a human to mean it.

## Two states instead of a date

With the mechanism fixed, the messaging was still sitting there. We were still telling customers "you'll stay online through the switch" and "expect this done by end of week," promises that depended entirely on an action outside our system.

So we changed the vocabulary. Instead of a completion date, we report one of two states: cert ready, or awaiting customer.

Cert ready means the relay has the domain on its allowlist and Caddy has successfully minted a certificate. Our half is done. Awaiting customer means we are done, and the handshake that finishes the job depends on them updating a DNS record we cannot touch.

That told the customer exactly what to check on their end. It told our own team, at a glance, whether a stalled domain was an infrastructure problem or a customer problem, which makes for a very different Tuesday depending on the answer. And it stopped us from absorbing blame for delays that were never ours.

## Why state holds up

Promising an outcome is the natural instinct. "Your site will be live by Friday" sounds more reassuring than "the cert is ready whenever you update your DNS." But the first is a bet on someone else's timeline dressed up as a commitment, and when it slips it reads as a broken promise even though nothing on our side failed.

Reporting state feels less confident in the moment. It is more accurate about where the dependency sits, and it holds up a week later, when the customer finally makes their DNS change and the cert is already there. The work was done on time. We just stopped pretending we owned the last step.

If a migration depends on a step another party controls, report status as state, like "cert ready" or "awaiting customer," and leave the completion date out. Then check whether any automated sync can remove a domain that is still mid-migration, and make removals a reviewed action.
