Short answer: when a DNS record write is rejected because a zone ID is not a domain name, resolve the opaque reference first, canonicalize the returned name, and compare it with the owner before writing; retain the decision, not every payload.
The bill for a failed write is rarely the rejected request itself. It is the retained verification evidence, repeated lookups, and operator time needed to explain which authority was trusted. The reliable design is to resolve an opaque reference, canonicalize the resulting name, and compare it with the requested owner before any write.
What is the retention cost of proving ownership?
In an onboarding system, a customer may submit z_7f31 while the DNS control plane returns example.dev.. Those values identify different things. The durable evidence is the mapping between the opaque reference, canonical name, account, delegation result, and decision timestamp. Raw TXT content and full provider responses are usually unnecessary after the decision window.
Retention has a failure mode on both sides. Keeping every response body increases sensitive-data exposure and storage work; keeping only a boolean makes a later dispute impossible to reconstruct. I retain normalized identity fields, request and correlation IDs, and the policy version, then expire raw responses on a documented schedule. That is a conscious loss: when an incident arrives after expiry, an operator may need to re-run a DNS observation.
The dominant term is often repeated evidence, not bytes in the record. A retry loop that stores a response per attempt can multiply retention without improving confidence. Bound retries, make the decision idempotent, and overwrite equivalent observations instead of appending duplicates.
Stop.
There is a subtle accounting trap here. Verification systems commonly keep a “latest status” row and an event stream, then copy the provider response into both. That doubles the payload before backups, replicas, and log shipping are counted. A better split is a small current-state row containing the canonical identity and policy version, plus an event containing only state transitions and a hash or request ID that permits correlation. The hash is not a substitute for the original evidence when an auditor needs byte-for-byte replay, so the retention policy must say which cases trigger archival. This is where storage architecture and onboarding semantics meet: the cheapest record is the one you decide you will never need, and that decision should be explicit.
Why is a DNS record write rejected because the zone name fails validation?
DNS names are case-insensitive, and a trailing root dot is presentation. Example.Dev and example.dev. should therefore compare as the same name. Record data is different: do not lower-case TXT content merely because the owner name was normalized.
One label cannot exceed 63 octets under the DNS rules described in RFC 1034. That limit belongs in validation, alongside the identity check.
The useful trace has three values: submitted reference, resolved canonical name, and normalized owner. It catches a display label copied into an identifier field, a stale mapping after a domain transfer, and an owner such as api.other.dev that is outside the resolved authority. A lookup can succeed while the write is still invalid. RFC 1034's presentation rules do not make an opaque identifier interchangeable with a DNS name.
from dataclasses import dataclass
@dataclass(frozen=True)
class Authority:
reference: str
name: str
account: str
def canonical_name(value: str) -> str:
return value.rstrip(".").lower()
def owner_relative(authority: Authority, submitted: str, owner: str, account: str) -> str:
if submitted != authority.reference:
raise ValueError("authority reference does not resolve to this authority")
if authority.account != account:
raise PermissionError("authority belongs to another account")
base = canonical_name(authority.name)
candidate = canonical_name(owner)
if candidate == base:
return "@"
suffix = "." + base
if not candidate.endswith(suffix):
raise ValueError("owner is outside the resolved authority")
return candidate[:-len(suffix)]
The resolver and writer should share one authorization context. If a delete-and-recreate race can change the target between those operations, carry a version or equivalent concurrency token. Otherwise the preflight check proves one object and the commit reaches another.
Customer-owned authorities require evidence that remains meaningful after a tenant transfer: the canonical name, account at verification time, delegation observation, and last successful resolution. Platform-owned authorities shift cleanup and lifecycle control to the platform, but a platform reference alone does not explain which customer was authorized.
| Boundary | Write target | Retained evidence | Failure to surface |
|---|---|---|---|
| Customer-owned | Resolved customer reference | Name, account, delegation, policy version | Wrong account or delegation changed |
| Platform-owned | Platform reference | Tenant mapping and authorization result | Customer reference used in platform scope |
Do not silently convert one boundary into the other. A customer reference that happens to look like a platform token is still the wrong principal. Make the ownership mode an explicit state-machine field, and require a fresh authorization decision when that mode changes.
How do you operate the check without preserving a warehouse?
Use failure-oriented fixtures: uppercase names, a trailing dot, an apex owner, a delegated subdomain, an unknown reference, and an authority belonging to another account. Add a contract assertion that the write function is never called when resolution or authorization fails. Keep the test matrix under source control; a six-case fixture set is easier to audit than an unbounded generated corpus.
Metrics should separate reasons instead of grouping everything under HTTP status. An owner-outside-authority rejection, an unknown reference, and a delegation change have different remediation paths. Log canonical identity only under the access policy for domain data, and propagate one correlation ID through lookup, normalization, authorization, and commit.
The practical stopping rule is modest: retain enough structured evidence to explain the onboarding decision, expire raw observations, and make retries converge on one result. This approach is a poor fit when regulations require immutable copies of every provider response; in that case, use an append-only archive with explicit access controls and accept the added retention burden. Otherwise, reducing storage and privacy burden while preserving identity evidence is the defensible trade-off.
Top comments (0)