Build1 publisher3 min readPublished
Two records for one DNS entry turn a verified badge into a drift check
A dev.to onboarding design keeps the SPF, DKIM and DMARC values you generated in one row and the answers a resolver returned in another. Its comparator is a single stripped string match, so an SPF value the customer appended to lands in drifted.
The Engineer · Build desk
What happened
- A dev.to post on custom-domain onboarding stores the DNS record you generated and the DNS answer you observed as two separate rows, and never overwrites the expected value with the observation.
- Copy mode is the default: the customer publishes into their own authoritative zone while a verifier polls public DNS, and write mode is confined to one zone, three record types, an audit entry and a mutation preview.
- The post treats "write accepted", "record visible" and "mail authentication passing" as three different facts that the interface has to show separately.
- The problem it is written against is a property-management product whose UI says "verified" while a stale TXT record keeps sending leasing notices to spam.
- It names three failure modes: a subdomain that sends while the form verified the parent domain, a zone that moves registrar after onboarding, and DNS responses retained forever.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Because the comparator is a stripped string match, an SPF record the customer appended to will keep reporting drift until the expected value becomes the merged record or the SPF path compares mechanisms.
- decision Anyone shipping write mode has to decide up front whether they can restore a prior value, because a rollback that has not read the zone first is a deletion.
- exposure Products that treat one passing check as a finished step end up holding domains that quietly stopped authenticating after the zone moved, and it is the customer's mail that fails.
- cost Unbounded retention of DNS responses is storage and privacy work the vendor pays for, and the post's position is that it adds nothing to diagnosis.
The comparator is one line of Python: `observed.strip() == intent.expected.strip()`. If the observation is `None`, the state is `missing`. If the strings match, the function returns `verified` and stamps the `key_id` that generated the value. Anything else is `drifted`, and that branch returns a hash of the expected value and a hash of the observed one, so the strings themselves stay in the observation row [3]. The separation exists because a record can look right in your own database and still be absent from public DNS, cached at a resolver, or superseded by a second SPF record [19].
That comparator sits awkwardly beside the SPF guidance in the same post. SPF has a one-record constraint in practice, so the post says to append an authorized mechanism to the customer's existing `v=spf1` value instead of publishing a second record [8]. Once the customer does that, the published TXT is their string with your mechanism inside it, and a stripped equality test against the value you generated returns `drifted` [21]. Either the expected value for SPF is the customer's merged record, or the SPF branch compares mechanisms rather than strings. DKIM does not have the collision, because names are selector-specific: rotation means publishing a new selector, waiting for propagation, then retiring the old one [9].
DMARC is the domain owner's call, and the post is blunt that `p=none` is a useful observation phase and not proof that messages are aligned [10]. Alignment is also the first failure mode. A property group may send from `rentals.example-property.com` while the onboarding form verifies the parent domain, so the verification job has to carry the exact envelope and header domains and test alignment, not just TXT presence [14]. The `classify` function does not do that. It compares one name's string at one moment against one expected value [22].
For the same reason, a successful write response is not completion. The post's rule is to re-query an independent recursive resolver, record that observation, and require the customer to retry after a propagation window [6].
Write mode has a precondition that is easy to skip. Rollback means restoring the prior value, not deleting a record you did not recognise [13], which means reading the zone before mutating it and keeping what you read. The post rules out automatic writes for customers with split DNS, DNSSEC change controls, or a provider that requires a ticket for production edits [12]. Copy mode has its own detail work: a copy button per field, whitespace preserved, and a label showing whether the console expects `_dmarc` or the fully qualified name, because registrars normalize names differently and a screenshot does not survive that [11].
Then the badge expires. A tenant can pass a token check, finish onboarding, and later move the zone to another registrar, so verification has to recheck on a schedule [15]. "A green badge is a current observation, not a permanent entitlement," the post says [16]. It also declines to keep every DNS response, on the grounds that unbounded retention creates privacy and storage work without improving diagnosis [17].
What to watch
- Whether the SPF branch gets a mechanism-level check, or the expected value is redefined as the customer's merged record.
- Whether implementations read the existing zone before a write, so rollback can restore a prior value instead of deleting a record.
- What recheck interval a verification badge is given once the first tenant moves a zone to another registrar.