TL;DR: For a logistics hostname cutover, use customer-triggered checks as the visible control and a bounded schedule as the recovery mechanism. The important design choice is governance: customer-owned zones need evidence and an explicit request trail, while platform-owned zones need change authorization and audit records. Keep DNS verification, service readiness, traffic activation, and rollback as four separate decisions.
| Zone model | Pick this when | Verification control | Governance record |
|---|---|---|---|
| Customer-owned | The customer must retain DNS change authority | Immediate recheck plus bounded background attempts | Expected record, requester, observations, and timestamps |
| Platform-owned | The platform is authorized to write the zone | Automatic check after the write plus bounded background attempts | Approved change, write result, observations, and timestamps |
| Split ownership | Delegation boundaries are explicit | Check from the system responsible for the delegated name | Delegation proof, responsible team, and cutover approval |
The least complex safe choice is usually the first row: the customer changes DNS, the onboarding UI checks immediately, and a short-lived schedule keeps checking after the browser closes. It gives an operator a useful button without making that button the reliability strategy.
Should scheduled domain verification polling follow a customer-triggered recheck?
Start with the customer-triggered recheck. It connects an intentional DNS edit to fresh evidence, which makes a typo or wrong record name easier to diagnose. The UI should say that a check was requested, then display the last observation and its time. It shouldn't imply that clicking changed DNS or completed the cutover.
A timer solves a different problem. It preserves progress when a warehouse or carrier integration team moves to another task before the expected record is observable. Bound that timer with a deadline or attempt limit. Otherwise, abandoned onboarding attempts become permanent work, and the metrics stop describing active demand.
For a concrete policy test, start with five eligibility times such as 0, 30, 90, 210, and 450 seconds, add jitter to the scheduled attempts, and stop after the fifth observation. Those numbers are an example, not a DNS guarantee. Load-test them against the verifier's capacity and replace them when the onboarding objective or traffic profile changes; the point is to make the retry budget reviewable instead of hiding an endless loop behind “pending.”
The useful mental model is a control room, not a spinner. In words: an authorized DNS change creates an expectation; manual and scheduled signals request observations; observations enter an evidence log; a policy evaluates that evidence; a human or deployment controller separately authorizes traffic. Each arrow should be inspectable.
Clicks are intent. Schedules are persistence.
Keep them separate.
Neither one should directly activate tracking.customer.example. For a rollback-capable logistics cutover, DNS verification only says that the expected value was observed. Application health still needs its own gate, and the previous hostname stays available for the rollback window chosen by the operating team.
Pick customer-owned zones for explicit authority
Customer ownership fits organizations that require their own administrators to approve DNS changes. Onboarding must carry more context because the platform cannot use a successful write as evidence. Show the precise record name, type, and expected value, then retain what the verifier actually observed.
This is also where scheduled-only verification produces a weak experience. An operator can publish the wrong value and wait without learning why onboarding is still pending. The manual control closes that feedback loop; the bounded schedule prevents a closed tab from becoming a stalled process. Both should append evidence to the same domain attempt, but governance matters more than sharing an implementation function: who requested the observation, which expectation was current, and which result supported the decision?
For example, an operator preparing tracking.customer.example may request a recheck immediately after publishing a TXT record. A mismatch should preserve the observed values and check time. If the expectation is corrected, create a new revision rather than rewriting the earlier evidence. That gives support and security reviewers a coherent sequence without treating an old mismatch as the current truth.
Do not let repeated clicks amplify work. Coalesce a pending check for the same expectation, apply a rate limit at the control boundary, and return the current evidence. The exact limits are product policy; DNS standards do not supply an onboarding retry schedule.
Pick platform-owned zones for controlled automation
Platform ownership removes the customer's manual record edit. It does not collapse change and verification into one fact. Store the desired record, the result of the authorized write, and the externally observed answer separately.
That separation earns its keep during rollback. If application health fails after verification, the traffic controller can restore the prior route without pretending the DNS write never happened. The audit record can answer three distinct questions: who or what approved the change, what the DNS control plane accepted, and what the verifier later observed.
Automatic verification is the natural default here because the system knows when it attempted the write. A customer-triggered recheck can remain useful for fresh evidence, but it shouldn't become an alternate change path. One button, one meaning.
Split ownership deserves its own row because delegated subdomains often cross team boundaries. Document the delegation and make the responsible system observable. An ambiguous boundary is worse than either ownership model: two teams may both wait for the other to act, while a scheduler faithfully records the same unresolved state.
Implement an evidence ledger in Node.js
The implementation below goes deeper on the governance boundary. It records immutable observations against a versioned expectation and keeps the policy that schedules retries outside the DNS lookup function. Node.js joins the character-string segments returned for each TXT answer before comparison; TXT data can be represented as multiple strings.
import { Resolver } from "node:dns/promises";
type Trigger = "customer" | "scheduled" | "post_write";
type Outcome = "match" | "mismatch" | "lookup_error";
type Expectation = {
domainId: string;
revision: number;
hostname: string;
expectedValue: string;
};
type Observation = {
observationId: string;
domainId: string;
revision: number;
trigger: Trigger;
outcome: Outcome;
observedValues: string[];
observedAt: string;
errorCode?: string;
};
interface EvidenceLedger {
appendOnce(observation: Observation): Promise<Observation>;
}
export async function observeTxt(
expectation: Expectation,
observationId: string,
trigger: Trigger,
ledger: EvidenceLedger,
resolver = new Resolver(),
): Promise<Observation> {
const observedAt = new Date().toISOString();
try {
const answers = await resolver.resolveTxt(expectation.hostname);
const observedValues = answers.map(segments => segments.join(""));
return ledger.appendOnce({
observationId,
domainId: expectation.domainId,
revision: expectation.revision,
trigger,
outcome: observedValues.includes(expectation.expectedValue)
? "match"
: "mismatch",
observedValues,
observedAt,
});
} catch (error) {
const errorCode =
typeof error === "object" && error !== null && "code" in error
? String(error.code)
: "UNKNOWN";
return ledger.appendOnce({
observationId,
domainId: expectation.domainId,
revision: expectation.revision,
trigger,
outcome: "lookup_error",
observedValues: [],
observedAt,
errorCode,
});
}
}
observationId is the idempotency boundary. A delivery retry can submit the same observation again without adding duplicate evidence, while a new scheduled attempt receives a new identifier. The expectation revision prevents a late result for an old TXT value from approving the current configuration.
Keep lookup_error distinct from mismatch. RFC 7489, in its DNS-based DMARC discovery procedure, distinguishes temporary DNS errors from a record that is absent. This code is not a DMARC implementation; the transferable lesson is that transport failure and negative evidence demand different retry and user-interface decisions.
Now instrument the control loop. Emit an event when an observation is requested, started, appended, and when its retry budget is exhausted. Useful dimensions include ownership model, trigger, expectation revision, outcome, and attempt number. Track time from expectation creation to the first matching observation, and alert on exhausted active attempts rather than every lookup error. That keeps transient failures visible without turning each one into an operational page.
The before/after is crisp. Before: a green badge overwrites the last result, nobody can explain which value passed, and a late timer can race a corrected record. After: immutable observations point to a specific expectation revision, policy selects the current revision, and traffic activation consumes an explicit readiness decision. The extra storage is a real trade-off: an append-only ledger retains more rows than a single mutable status, and operators need retention and access policies for observed DNS values. Use a simpler current-state record when the hostname is low risk, no separate cutover approval exists, and historical evidence has no operational or governance consumer.
Limits and operating rules
A matching observation is not proof that every resolver sees the same answer. Report the resolver's observation, timestamp, hostname, and expectation. Avoid global claims.
This design is also unsuitable for a workflow that requires synchronous completion. DNS observation can fail or remain mismatched, so the onboarding contract must allow a pending state. Scheduled polling adds queue load and delayed work; customer-triggered checks add rate-limit and abuse concerns. Those limitations are the price of preserving both unattended progress and operator control.
The verifier also cannot authorize a DNS change, prove application health, or choose the rollback window. Those controls belong to change management and deployment policy. Keep the boundaries boring and visible.
Finally, stop background checks on a match, expiration, cancellation, or superseding expectation. Add jitter when many attempts can become eligible together. Retry timing and capacity limits must come from the service's load budget and onboarding objective, not from an invented promise about DNS convergence.
Top comments (0)