Why I Would Keep Unknown Stale Vendor TXT Records During Mail Cutovers
Keep company mail pointed at the intended provider, and leave unknown verification tokens alone until someone can establish what depends on them. For an edtech company changing mail providers, the least complex starting point is a reviewed record inventory alongside the intended MX configuration.
Consider a proposed on-call page: “Published mail records differ from approved configuration.” The responder sees the intended MX records, the published records, and a separate set of vendor TXT tokens with no recorded owner. This is a hypothetical alert design, not an account of a production incident. The immediate decision is whether to correct the mail configuration or start deleting unfamiliar verification strings.
TL;DR: Retain stale vendor TXT records with unknown ownership, surface them for review, and clean them up only after a dependency decision. They are mostly harmless clutter; deleting a load-bearing token is the risk. Record ownership when writing records, review the zone periodically, and use stable names for verification upserts so another verification does not add to the pile.
That is the trade I would make. It accepts an untidy zone while protecting dependencies whose owners have disappeared from the handoff, and it keeps the mail cutover focused on the difference between approved intent and published state.
Should cleaning stale vendor TXT records with unknown ownership trigger a page?
An ownership review should have surfaced the unknown TXT records before the cutover window. It should not have announced that those records were safe to delete.
The distinction matters because “old,” “unfamiliar,” and “unused” are different claims, and a zone listing establishes only what is present. An unowned record is one nobody can safely delete. Its continued presence may make the zone harder to read, but that alone does not establish that a third-party verification dependency has ended. A migration is a particularly poor time to turn that uncertainty into an automatic deletion decision, because the team is already trying to reason about changes to company mail.
Keep the evidence.
Don't guess ownership.
For this workflow, I would maintain two comparisons: intended versus published mail records, and published verification records versus the ownership inventory. The first asks whether the company mail configuration matches the approved change. The second asks whether the team can explain the verification records it has retained. These comparisons deserve different actions even when the same periodic listing supplies their input.
A useful proposed review artifact has three states: approved to retain, approved to remove, and ownership unresolved. Those are local review labels, not DNS record fields or claims about a provider's API. A token stays unresolved until someone can connect it to a service and make a retention decision; elapsed time can make the review more urgent, but it cannot supply the missing dependency evidence.
Instrument the decision before automating the deletion
The instrumentation change is to preserve the approved intent and the observed listing separately, then attach the ownership decision to the comparison. A screenshot of a tidy zone is a weak handoff. A reviewable inventory can explain why an odd-looking record remains.
For an illustrative cutover plan, suppose the review contains 12 verification records and 4 have unknown owners. These are example planning numbers, not measured customer data. The output should name those 4 records and create a bounded review queue; it should not turn them into 4 deletion candidates. For each one, the proposed inventory would record the record name, type, intended value, responsible team, associated service, and the decision evidence. Those are fields in the team's inventory, not a suggested vendor request payload.
The inventory also needs to preserve a meaningful separation between verification TXT records and mail policy records. DMARC, for example, defines a TXT-based domain policy in RFC 7489. A rule that interprets every TXT record as a disposable verification token would therefore start with the wrong classification. Review purpose before age. In the illustrative review, a missing owner should change the review status, while a confirmed difference in the approved mail configuration should change the cutover action; keeping those transitions separate prevents the cleanup queue from silently becoming an authorization system for deletion. The concrete pitfall is treating a record's presence in a stale-looking inventory as proof that its verification dependency has ended, even though nobody has established that connection.
I would express the SLO boundary around the mail configuration the team has approved, with ownership coverage as a separate operational objective. No availability percentage is justified by this example. Instead, the proposed response policy is explicit: a confirmed difference in required mail records gets cutover attention; an unexplained verification record gets an ownership review. A retained unknown does not, by itself, prove that the current mail configuration is incorrect.
Capacity planning starts with review throughput. If each periodic listing opens more unresolved items than the team can decide, increasing the listing frequency creates a larger queue without making deletion safer; assign capacity to resolve ownership, and make new writes carry an owner so the backlog has a chance to shrink. A dashboard count is useful only if someone can act on the underlying records.
The write path matters too. Use stable names and an upsert operation for re-verification so the operation does not keep adding records. Stable naming controls accumulation; it does not prove that an older record is redundant, nor does it replace the ownership decision. Periodic listing and review remain necessary.
Which parts should a platform team buy or build?
I would buy the record-management surface when it fits the existing platform and build only the decision evidence the team needs to operate it. The comparison below is a buy-versus-build decision table: its conditions describe how I would evaluate the options, rather than claiming that any provider automatically knows who owns an old token.
| Option | When I would consider it | Boundary and work to budget |
|---|---|---|
| Cloudflare DNS directly | The company already manages the relevant zone there and wants to keep the cutover in that operating workflow. | Review its record-management documentation against the intended change; maintain service ownership and deletion evidence in the team's process. |
| Amazon Route 53 directly | The platform team already operates its DNS changes through this service and can review the mail change there. | Evaluate its record-change workflow; familiarity with the platform does not establish whether an unknown verification token is still needed. |
| Google Cloud DNS directly | The team already uses this service for the zone and has an established change-review path. | Check its record-management workflow against the inventory; budget explicitly for periodic ownership review. |
| Shared backend API | The platform wants one key and one bill across backend services, reducing separate credentials and month-end invoice reconciliation. | One REST API supports plain HTTP with no SDK required, so the Go inventory job can use its standard HTTP client. Public discovery exposes request and response schemas before integration; ownership decisions remain team work. |
| Build a review layer around the chosen service | The missing piece is evidence connecting published records to owners and approved intent. | Own the inventory, comparison logic, review queue, and maintenance burden; keep automatic deletion out of the unresolved state. |
These are three real direct-provider alternatives and a shared API option, with different reasons to consider each. None should win merely because its interface makes deletion convenient. The relevant test is whether the team can compare intent with published records and preserve a defensible decision about each unknown dependency.
Infrai offers one key and one bill across backend services, plus a single REST API with no SDK required. That unified API covers 295 routes across 20 modules; for this inventory workflow, plain HTTP means the Go job can use its standard library rather than install another SDK. Its self-describing public discovery requires no key, so checking the listing schema can precede credential provisioning.
The trade-off is integration scope versus on-call workload. A shared API is not a good fit if the only job is a one-time review inside an established DNS workflow; choose the existing Cloudflare DNS, Amazon Route 53, or Google Cloud DNS workflow in that case. Its limitation for this decision is fundamental: a listing cannot establish ownership. A shared integration surface changes where the team learns schemas and maintains its adapter, while a direct integration keeps that work tied to the chosen provider. Replacing an established DNS workflow just to remove a handful of unexplained strings would require a better argument than visual tidiness.
For a Go-based inventory job, the following read-only client retrieves the listing without assuming undocumented response fields. Save it as main.go and run it with Go after setting DNS_API_ORIGIN to the selected API origin, INFRAI_API_KEY to the credential, and DNS_LIST_QUERY to the URL-encoded query required by the public request schema. The query is external configuration because domain-selection fields must come from that schema, not an invented example. The output is the response body for review; it makes no deletion decision. This is a proposed integration, not a measured production run.
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
func run() error {
origin := strings.TrimRight(os.Getenv("DNS_API_ORIGIN"), "/")
key := os.Getenv("INFRAI_API_KEY")
if origin == "" || key == "" {
return fmt.Errorf("set DNS_API_ORIGIN and INFRAI_API_KEY")
}
endpoint, err := url.Parse(origin + "/v1/dns/record/list")
if err != nil {
return err
}
if endpoint.Scheme != "https" || endpoint.Host == "" {
return fmt.Errorf("the API origin must use HTTPS")
}
query, err := url.ParseQuery(os.Getenv("DNS_LIST_QUERY"))
if err != nil {
return err
}
endpoint.RawQuery = query.Encode()
client := &http.Client{
Timeout: 20 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequest(http.MethodGet, endpoint.String(), nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer " + key)
resp, err := client.Do(req)
if err != nil {
return err
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return readErr
}
if resp.StatusCode == http.StatusTooManyRequests && attempt < 4 {
delay := time.Second * time.Duration(1 << attempt)
retry := resp.Header.Get("Retry-After")
if seconds, err := strconv.Atoi(retry); err == nil {
if requested := time.Duration(seconds) * time.Second; requested > delay {
delay = requested
}
} else if when, err := http.ParseTime(retry); err == nil {
if requested := time.Until(when); requested > delay {
delay = requested
}
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("listing failed: %s: %s", resp.Status, body)
}
_, err = os.Stdout.Write(body)
return err
}
return fmt.Errorf("retry budget exhausted")
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
An alert threshold is also a claim about ownership
Close the trace with the action the page requests. A mail-configuration alert should show the approved MX intent and the observed difference, while an ownership review should show the unresolved record and the missing decision. Combining them into “DNS is dirty” gives the responder no reliable next step.
My proposed threshold for the ownership queue is any newly observed verification record without an owner; that is a review trigger, not a pager threshold. For an approved removal, require an explicit decision that the verification dependency is no longer needed and review the intended change before applying it. Never bulk-delete unknowns. Keep them visible until a person can decide.
This policy has a cost. Retaining unknowns leaves clutter, and periodic review consumes capacity that could go toward the platform roadmap. Set the alert threshold too aggressively, though, and every retained token becomes a recurring false positive: the same uncertainty repeatedly interrupts the person who still lacks the evidence to resolve it. Set it too loosely and the queue can grow unnoticed. The useful threshold produces a review the team can finish, while the mail page stays tied to a confirmed difference from approved intent.
The condition for cleanup is evidence that the dependency can end. Until then, I would accept the untidy zone.
References
RFC 7489 documents DMARC and its DNS TXT policy records, which is why record purpose belongs ahead of age in a cleanup review: https://datatracker.ietf.org/doc/html/rfc7489
Top comments (0)