# DNS changes by AI coding agents: make reviews safer

DNS changes by AI coding agents need a stricter shape than ordinary infrastructure edits. An agent should never present "update DNS" as one approval. It should present a creation, an edit, or a deletion as separate actions with different evidence, different reviewer questions, and different failure handling.

That separation sounds bureaucratic until an agent replaces a mail record, adds a CNAME beside incompatible data, or deletes the last address record during a migration. DNS makes small text changes look harmless. Their effects spread through resolvers, clients, delegated zones, certificate checks, mail delivery, and service discovery. A reviewer needs to see the exact verb before they can judge the blast radius.

## DNS record verbs carry different kinds of risk

Creating a DNS record introduces a new assertion into a zone. The reviewer needs to ask who owns the name, whether the target is correct, whether the new data conflicts with existing data, and whether someone could abuse the record for domain validation or traffic capture.

Editing a record changes an existing assertion. That action needs an old value and a new value side by side. A change from one address to another might move application traffic. A change to a TXT record might alter sender authentication, ownership verification, or a configuration consumed by an external service. The old state gives the reviewer a way to tell a deliberate replacement from an agent acting on stale information.

Deleting a record asserts that absence is now correct. It has the widest failure range because the safe outcome often depends on something outside DNS. Perhaps the new endpoint needs time to receive traffic. Perhaps another team still uses a verification TXT record. Perhaps the record is part of a failover arrangement that the agent cannot infer from a repository.

Treating those operations as one generic write causes approval blindness. A reviewer learns that every card says the same thing, clicks through, and notices too late that one card removed an entire RRset. The control must make the dangerous difference impossible to miss.

DNS terminology also needs care. A DNS owner name can hold an RRset, a set of records with the same name and type. Many provider APIs expose a single "record" object, but their update behavior may replace the full RRset. If an agent intends to remove one of two A values and the provider replaces the set, it can accidentally remove both. Review the provider's actual object model before you decide what an action means.

## Make each request declare one irreversible verb

A DNS action should contain one verb: `create`, `edit`, or `delete`. Do not accept a vague method named `apply`, `sync`, or `upsert` at the approval boundary. Those names are convenient for a programmer and expensive for an operator.

The request must also say whether it addresses one provider record object or an entire RRset. If the API cannot edit an individual member safely, the action should state that it will replace the whole RRset and list every retained value. Silence on that point is how an apparently minor address change becomes an outage.

Use a request shape that preserves intent and gives the executor enough state to reject drift:

```json
{
  "action": "edit",
  "zone": "example.net",
  "owner": "api.example.net.",
  "type": "A",
  "expected_rrset": {
    "ttl": 300,
    "values": ["198.51.100.24"]
  },
  "proposed_rrset": {
    "ttl": 300,
    "values": ["198.51.100.81"]
  },
  "reason": "Move the API endpoint after the service owner confirmed health checks",
  "verification": [
    "authoritative lookup returns 198.51.100.81",
    "HTTPS health check succeeds at the new endpoint"
  ]
}
```

For a create action, `expected_rrset` should say that the RRset is absent, or should state an allowed existing state when the provider's model requires it. For deletion, `proposed_rrset` should explicitly be absent. Do not encode absence as an empty string or omit the field. Ambiguous payloads produce ambiguous logs.

The executor should read the current zone state immediately before writing. It should compare that observation with `expected_rrset` and stop on any mismatch. This is a compare and swap discipline, even if the DNS provider does not offer a compare and swap endpoint.

RFC 2136, Dynamic Updates in the Domain Name System, puts this idea directly in the protocol. Its prerequisite section lets a client state which RRsets must exist or must not exist before the server accepts an update. Most hosted DNS APIs do not expose RFC 2136 wire messages, but the operational lesson remains sound: a write based on an unverified earlier read is unsafe.

An agent may retry a read. It should not retry a failed write by changing the expected state to whatever it sees. A mismatch means another actor changed DNS, the agent's plan is stale, or the provider normalized data in a way the planner did not expect. Escalate that condition to a human reviewer.

## Creation needs ownership and conflict evidence

A new record needs proof that the name belongs to the intended service before anyone approves it. A repository path, ticket reference, or a natural language request can suggest ownership, but none of them proves that a new public name is free to use.

The agent should inspect the exact owner name across record types before it proposes a creation. This matters most for CNAME records. RFC 1034 says that if a CNAME RR is present at a node, no other data should be present there. A planner that checks only for an existing CNAME can propose a CNAME at a name that already has address, mail, or TXT data. The provider may reject the write, or worse, replace data under a provider specific rule.

Creation review should show four things in plain language:

- The fully qualified owner name, zone, record type, value, and TTL.
- The current records at the same owner name, including types the agent will not modify.
- Who requested the name and what service will consume it.
- The target check appropriate to the record type.

Target checks depend on the data. For an A or AAAA record, the agent can confirm that the address belongs to the expected deployment inventory if such an inventory exists. For a CNAME, it should resolve the target and make sure the target name is complete. For an MX record, it should confirm the mail host name and priority with the mail owner. For a TXT record used by an external verifier, it should retain the exact supplied value rather than "cleaning up" whitespace or quoting based on guesswork.

Do not let an agent infer that a newly requested subdomain is harmless because it sits below a familiar domain. `login`, `auth`, `mta`, `vpn`, and `admin` carry obvious consequences, but arbitrary names can matter too. A public DNS name becomes an interface once someone depends on it.

Creation also differs from a release deployment because rollback may not mean deletion. If a new record supports a verification flow, deleting it after the flow completes can break renewal or future validation. The proposal should say whether the record is temporary, who will decide when it expires, and whether the requester accepts removal later. If nobody owns that answer, do not invent an expiration date.

## An edit must name the state it replaces

An edit is safe only when the proposal identifies the exact state it will replace. "Point the app at the new host" is intent, not an executable DNS action.

Require the previous RRset in every edit request, including values the agent plans to preserve. Consider an A RRset with two addresses:

```text
Current:  app.example.net. 60 IN A 198.51.100.10
          app.example.net. 60 IN A 198.51.100.11
Proposed: app.example.net. 60 IN A 198.51.100.11
          app.example.net. 60 IN A 198.51.100.12
```

That is a controlled rotation. A reviewer can see that the action keeps one serving address while replacing another. If the provider replaces RRsets as a unit, sending only `198.51.100.12` would be a deletion and a creation disguised as an edit.

TTL changes belong in the same review card. Teams often lower TTL before a migration, then raise it after the migration. Both changes have consequences. A lower TTL raises resolver query frequency and may expose configuration mistakes faster. A higher TTL can hold traffic on the wrong endpoint longer after a bad edit. Neither change deserves to disappear inside a generic record update.

An agent should also distinguish data edits from formatting differences. Providers can canonicalize owner names, append a trailing dot, split TXT strings, or reorder values. A planner that compares raw API text will produce needless approval cards or false conflicts. Normalize representation before comparison, but preserve the semantic data a reviewer needs to read.

Do not approve an edit simply because an agent found a matching string in source control. Source control can contain a desired state that lost relevance after an emergency DNS change. The authoritative current state remains the condition for the write. Repositories explain intent; DNS tells you what clients can receive.

## Deletion needs a reason for absence

A deletion should answer why the record may disappear now, not merely why the agent found it unattractive. Old records often look redundant until somebody discovers they support a legacy callback, an email system, a certificate validator, or a delegated service.

The safe pattern separates traffic movement from cleanup. First, add or edit the replacement data. Then verify the intended authoritative answer and the dependent service. Only after the owner accepts the new behavior should the agent request deletion of the old data. Keep the removal request distinct, even when the provider could combine it with the edit.

A deletion request should include these facts:

- The exact RRset or provider record object to remove.
- The service or team that confirmed it no longer depends on the data.
- The replacement action, if one exists, and its verification result.
- The planned verification after removal.
- A statement on whether the removal changes an entire RRset.

Apex records deserve extra suspicion. Zone apex behavior varies by provider, especially where providers offer aliases or synthetic records that imitate CNAME behavior. An agent should never translate a desired CNAME into a provider specific apex feature without a reviewer who understands that provider's semantics. A familiar DNS word does not guarantee familiar DNS behavior.

Negative caching complicates restoration after removal. RFC 2308 describes how resolvers can cache negative answers, including NXDOMAIN and no data responses. If you delete a record and then restore it, some clients can continue to see the earlier absence until their negative cache expires. This does not mean deletion is forbidden. It means rollback planning must include the period when authoritative DNS is fixed but users still receive a cached failure.

A delete action should not silently broaden itself when the provider rejects removal of an individual value. The executor should report that the provider requires a whole RRset replacement and request a new, explicit action. That extra approval is cheaper than explaining why a supposedly unused address record vanished.

## Review cards need context beyond a zone diff

A raw diff gives reviewers data, but it often hides the consequence. Review cards should lead with a short sentence that names the operation and expected behavior: "Delete the obsolete TXT verification record for `verify.example.net`; the requester confirmed that the external verification is complete." Then show the exact records.

The card should make these fields visible without opening a separate view: action verb, zone, owner name, type, old state, proposed state, TTL, requester, agent process identity, reason, and verification plan. If the write can affect an entire RRset, say that in the first line.

Use direct wording. "Replace both A values" is better than "reconcile record set." "Remove this MX record" is better than "apply desired configuration." Reviewers make faster decisions when the system uses operational verbs instead of abstraction.

A useful card also gives the reviewer a reason to reject the action. For a CNAME creation, show conflicting data at that owner name. For an edit, show if the current state differs from the expected state. For a delete, show when the agent lacks confirmation from the service owner. The approval interface should expose uncertainty, not bury it in an execution log.

Do not turn reviewers into a manual policy engine by giving them a large free text exception box. If the agent requests an action outside the normal shape, require it to prepare a new action with the missing evidence. A reviewer who must reconstruct DNS semantics from a chat transcript will eventually approve a mistake.

### A worked review example

Suppose an agent must move `api.example.net` to a replacement address. A bad card says: "Update DNS for API." It gives the reviewer no way to spot that the provider will replace a two member RRset.

A reviewable edit says: "Replace `198.51.100.24` with `198.51.100.81` in the A RRset for `api.example.net`; retain `198.51.100.25`; keep TTL 300." It then displays the current and proposed full RRsets, names the service owner, and states that the agent will query the authoritative nameserver and run the approved health check after the write.

If the reviewer instead wants a cutover with no overlap, they should reject this card and request that intent explicitly. The agent should not decide that overlap is unnecessary because its deployment files contain a new address.

## DNS caches make timing part of the approval

Authoritative DNS and recursive DNS answer different operational questions. The authoritative server tells you the zone's current published data. A recursive resolver can return an earlier answer until the TTL it received expires. A browser, operating system, application runtime, load balancer, or internal resolver may add another cache.

Approval should therefore name the desired verification point. "The authoritative servers return the new answer" verifies the write. "A public resolver returns the new answer" verifies some propagation. "The application serves traffic at the new destination" verifies the outcome users need. Those are separate checks.

Use commands that make the source of an answer visible. Replace the names and address with the zone's actual authoritative server:

```sh
dig @ns1.example.net api.example.net A +noall +answer

; expected answer shape
api.example.net. 300 IN A 198.51.100.81

dig @1.1.1.1 api.example.net A +noall +answer

; resolver answer can retain an older value with a lower remaining TTL
api.example.net. 117 IN A 198.51.100.24
```

The first query checks the published state at one authoritative server. The second query checks what one recursive resolver currently returns. Neither proves every client has switched. Record both results in the action log so an incident responder can distinguish a failed provider write from expected cache behavior.

Do not assume a TTL is a countdown to global consistency. A resolver receives a TTL when it caches an answer, so different resolvers begin their countdown at different times. Some clients may also hold connections or cache application configuration independently of DNS. A migration plan needs service level verification, not a promise that a number of seconds passed.

DNS delegation changes need another level of care. Editing NS records, glue records, DS records, or other delegation related data can affect resolution beyond the child zone. An agent should categorize those as a separate class of change and require a reviewer who owns the parent and child relationship. Do not fold them into an ordinary host record workflow.

## Broad DNS credentials are the wrong shortcut

Giving an agent a provider credential that can edit every zone feels efficient because the agent can finish its task without waiting for an integration change. It is also the wrong boundary. A prompt injection, a mistaken repository instruction, or a confused plan then has authority far beyond the service it was asked to change.

Limit each execution path to the zones and operations it needs. Separate read access from write access where the provider permits it. Keep a mapping between a repository or service identity and the zones it may request. Reject a request that names a zone outside that mapping before it reaches provider credentials.

The boundary should constrain actions, not merely hide a token. An agent that can ask an intermediary to send arbitrary provider API requests still has broad control if that intermediary accepts arbitrary paths, methods, headers, and request bodies. The action layer should own the provider call shape for DNS operations and reject fields outside its schema.

This is where generic "upsert" endpoints cause trouble. They let an agent collapse creation, edit, and deletion into one request body, often with provider defaults that a reviewer cannot see. Define separate executor methods and make each method validate the action's required evidence. A deletion method should refuse a payload that contains a replacement state. An edit method should refuse a missing expected state.

Keep DNS credentials outside the agent process. For teams using Sallyport, the app can execute the HTTP call while credentials remain in its encrypted vault, so the agent receives the result instead of the credential. That addresses credential exposure, but it does not decide whether a proposed DNS action is sensible; the action schema and approval evidence still do that work.

## A stale plan can delete the wrong live record

A common failure begins with an agent reading DNS at the start of a long coding task. It sees `cdn.example.net` with one CNAME target and plans to replace it after a deployment. While the agent works, an on call engineer updates the target to divert traffic during an incident.

The agent finishes later and sends the old replacement request. If its provider call uses a generic update or an unconditional delete then create sequence, it overwrites the on call change. The request may look correct relative to the state the agent observed hours earlier. It is wrong relative to the live zone.

Fresh reads before writes prevent this specific failure. The executor reads the RRset, compares it against the action's `expected_rrset`, and refuses the write when the on call target differs. The refusal should retain both values in the log and tell the reviewer that another actor changed the record. It should not attempt a merge.

Merging DNS data requires service knowledge. Two address values may represent deliberate capacity, a temporary overlap, a geographic split, or an emergency route. An agent cannot safely infer which one from the fact that it recognizes only one address in a deployment file.

The same rule applies after a partial failure. If a provider reports a timeout, the executor must read the authoritative provider state before retrying. The write may have succeeded even when the client never received the response. Blind retries create duplicate values on some APIs and unwanted replacement behavior on others.

## Logs must preserve what the reviewer actually approved

A DNS audit record needs more than a provider activity event saying that a record changed. Store the proposed action, the observed state before execution, the approval decision, the process that requested it, the provider response, and the verification results. Preserve enough data to reconstruct whether the executor followed the approved request.

A tamper evident log adds a useful property: a person investigating an incident can test whether history changed after the fact. That matters when DNS answers have already aged out of caches and people begin relying on memory or screenshots. The evidence should remain understandable without the original agent conversation.

Separate the run record from the call record. A run record answers which agent process received authority and when that authority ended. A call record answers which exact DNS action it requested, whether someone approved it, and what happened. Instant revocation of a run should stop later calls, but it cannot undo a DNS write already accepted by the provider. The audit trail needs to make that boundary plain.

Use action IDs in both the approval card and execution record. If an operator sees a bad result, they should be able to locate the exact approved deletion or edit without searching an undifferentiated stream of agent output. The ID should connect the request, state comparison, provider response, and verification commands.

The first practical change is small: ban generic DNS writes at the approval boundary. Force every request into create, edit, or delete, require the full expected RRset for edits and deletions, and reject drift before execution. That one constraint makes an agent's proposed DNS work readable enough for a human to catch the errors that matter.
