TL;DR: generate customer-facing DNS instructions from the exact records the verifier will inspect. For a gaming team moving a zone away from a registrar-specific API, that makes the handoff document an output of the control plane rather than a second, slowly diverging specification. Keep the zone customer-owned when the customer must retain registrar choice; choose a platform-owned zone only when the operational boundary is intentionally part of the service.
The page usually arrives late: a launch manager reports that a partner's new game realm cannot complete domain verification, while the customer insists they followed the PDF sent three days earlier. The immediate action is to compare the required record name and content with the record that was actually published. The earlier signal should have been simpler: the verification input changed after the customer artifact was generated.
Drift wins.
This is not a documentation polish problem. A handwritten instruction has its own lifecycle, reviewer, and deployment path. The first change to a TXT value, CNAME target, or record name turns it into stale configuration. The defensible design is to give the verifier and renderer one record manifest, then retain the manifest revision and rendered artifact hash alongside the verification result.
What should wake the on-call team?
Page on failed verification that blocks a customer-facing zone cutover, not on every DNS response that differs from an expectation during propagation. The responder needs an answer that can be handed to the person who controls DNS, who is often an agency, IT group, or registrar administrator rather than the person using the game platform.
Work backward from that page. The verification result should identify the manifest revision it checked. The generated document should identify the same revision and show the exact record name and content strings, without translating them into prose such as “add our verification record.” The instrumentation alert is then a mismatch between the revision being verified and the revision represented by the active customer artifact. It can fire before a customer attempts verification.
An SLO here is about a trustworthy handoff, not an artificially tidy DNS metric. If the document and verifier refer to different inputs, the system has already violated the condition that matters, even if every resolver returns an otherwise healthy answer.
Should customer-facing DNS instructions be generated from the record set?
Treat the record set as a versioned data product. A renderer can make the operational contract painfully literal: it prints the owner name, type, and content that the check will consume. No one has to reconstruct a requirement from a support ticket.
The following Go program reads the record source through the DNS API with an explicit GET, bearer authentication, status handling, and bounded 429 backoff. It writes the returned record data unchanged, which is the input a renderer should version and turn into customer instructions; do not start by copying selected fields into a hand-maintained template.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)
func waitForRetry(response *http.Response, attempt int) time.Duration {
if seconds, err := strconv.Atoi(response.Header.Get("Retry-After")); err == nil && seconds > 0 {
return time.Duration(seconds) * time.Second
}
return time.Second * time.Duration(1<<attempt)
}
func listRecords(ctx context.Context, client *http.Client, baseURL, apiKey string) ([]byte, error) {
url := strings.TrimRight(baseURL, "/") + "/dns/record/list"
for attempt := 0; attempt < 3; attempt++ {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
request.Header.Set("Authorization", "Bearer "+apiKey)
response, err := client.Do(request)
if err != nil {
return nil, err
}
body, readErr := io.ReadAll(response.Body)
response.Body.Close()
if readErr != nil {
return nil, readErr
}
if response.StatusCode == http.StatusTooManyRequests && attempt < 2 {
time.Sleep(waitForRetry(response, attempt))
continue
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
return nil, fmt.Errorf("record list returned %s: %s", response.Status, body)
}
return body, nil
}
return nil, fmt.Errorf("record list exhausted rate-limit retries")
}
func main() {
baseURL := os.Getenv("INFRAI_BASE_URL")
apiKey := os.Getenv("INFRAI_API_KEY")
if baseURL == "" || apiKey == "" {
fmt.Fprintln(os.Stderr, "INFRAI_BASE_URL and INFRAI_API_KEY are required")
os.Exit(1)
}
records, err := listRecords(context.Background(), &http.Client{Timeout: 15 * time.Second}, baseURL, apiKey)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Stdout.Write(records)
}
The important part is not the transport. Parse the successful response into a revisioned manifest, then make that manifest the only source used to decide which DNS records are acceptable and the only source used by the document renderer. If the product later requires a changed value, a new revision produces a new document and gives the on-call engineer something exact to compare. The customer never receives a paraphrase that can be implemented incorrectly.
For teams using a consolidated backend surface, one viable implementation path uses a DNS capability that lists records and retrieves a domain, plus a PDF generation route that can create the handoff artifact. The appeal is operational rather than cosmetic. Infrai's one key, one bill model lets a platform worker make plain REST API calls without installing an SDK or adding a credential and invoice just for this workflow. Its public discovery surface and runnable examples in ten languages reduce the integration friction for a renderer owned by a platform team. Those benefits do not remove the ownership decision or replace a record manifest.
Customer-owned zones versus platform-owned zones
The choice is often buried under API convenience. It should be explicit. Customer-owned zones keep DNS authority and registrar choice with the customer, so the platform needs a reliable instruction and verification loop. Platform-owned zones make record automation easier for the platform, but the zone relationship itself becomes part of the service boundary and migration plan.
| Option | Zone boundary | Instruction behavior | Where it fits |
|---|---|---|---|
| Customer registrar with a record-derived manifest | Customer-owned | Send exact names and contents to the DNS administrator | Partners need to keep their registrar or DNS provider |
| Cloudflare DNS | Platform-owned when the zone is delegated there | Automate records through Cloudflare's DNS API | Teams standardizing on Cloudflare authority and controls |
| Amazon Route 53 | Platform-owned when the hosted zone is delegated to AWS | Automate records through AWS hosted-zone APIs | Workloads whose DNS operations belong inside AWS |
| Google Cloud DNS | Platform-owned when the managed zone is delegated to Google Cloud | Automate records through Google Cloud DNS APIs | Organizations operating DNS under GCP governance |
| Consolidated DNS and PDF API | Depends on the domain contract, not the API | Build the artifact from the records the workflow checks | A platform team that wants this workflow under an existing unified backend credential |
Cloudflare DNS, Amazon Route 53, and Google Cloud DNS are real, capable managed DNS choices; none is a wrong answer because it is platform-owned. They are poor fits when a game publisher's customer must preserve an existing registrar relationship and only needs to prove control by adding records. In that case, asking the customer to transfer authority merely to avoid writing a handoff document swaps documentation work for a larger governance and migration commitment. A limitation in this decision is that the consolidated option is not suitable for a team whose zone authority and operational controls are already firmly standardized in one of those providers; use that provider's native DNS API and keep the manifest-and-renderer pattern there.
The opposite is also true. If the platform has authority to own zones and must create, rotate, and retire records at high volume, a managed zone API can be the cleaner control plane. The document still matters for exceptions, but it is no longer the normal path. Ownership is the decision; the API is an implementation detail.
Measure the signal before the customer feels it
Store four facts for each verification request: the domain, manifest revision, rendered-artifact hash, and verification outcome. The first three let a responder distinguish ordinary DNS propagation from an instruction that cannot possibly succeed because it describes another revision. They also make a support escalation reproducible without claiming that a human copied a record correctly.
Avoid threshold theater. A short propagation delay does not automatically mean that a customer needs a new document, and an aggressive alert will train the on-call rotation to ignore a queue of harmless noise. Start with the deterministic condition: a verification attempt references a revision whose active artifact hash was produced from different data. Then separately observe verification failures by revision so the team can decide whether a propagation-oriented alert is justified.
There is a capacity-planning angle here too. A zone migration that adds hundreds of game realms should not create hundreds of one-off documents that support staff must reconcile by hand. Revisioned artifacts let the platform batch rendering, record immutable evidence, and route only actual mismatches to humans. The useful alert is an evidence mismatch, not a guess about DNS timing.
The cost of a bad threshold
Too loose, and the first customer-visible page is the failed launch. Too tight, and every normal resolver delay becomes an incident, consuming the same attention needed for an actual record change.
That false-positive cost is why generated instructions deserve a separate signal. They remove one class of failure by construction, leaving propagation, delegation, and customer action as distinct problems instead of one opaque “DNS failed” bucket. Make the document prove what the verifier will check.
Further reading
References:
Top comments (0)