# Domain registrar security requires per-call approval

Giving an AI agent a registrar API credential does not give it one coherent power. It gives the agent several unrelated powers that happen to sit behind the same secret: redirecting the domain, preparing it for transfer, changing the people who receive recovery notices, and altering the DNSSEC chain of trust. Domain registrar security fails when an approval layer treats all of those calls as interchangeable.

Every state changing registrar call deserves a decision tied to the exact action, domain, old value, and proposed value. A human may approve replacing nameservers during a planned migration while rejecting a lock removal, a registrant email change, or a DS deletion made in the same run. Session approval alone cannot express that difference.

## One credential hides several security boundaries

A registrar credential is an authentication fact, not a statement of human intent. Even when a provider offers granular API permissions, teams often put several required write actions into one role so an automation job can finish. Other providers expose a broad registrar token. In both cases, possession answers only who may ask. It does not answer whether this particular change should happen now.

The underlying protocols do not pretend these operations are equivalent. RFC 5731, the EPP domain mapping used between registrars and registries, describes nameserver associations, contact associations, status values, and authorization information as separate domain attributes. Its update command can add or remove nameservers and contacts, change the registrant, change authorization information, and alter client status values. A convenient API may place those operations under one credential, but the registry state keeps their meanings separate.

A public cloud registrar makes the same distinction visible in its command list. The Route 53 Domains API has separate operations for `UpdateDomainNameservers`, `UpdateDomainContact`, `DisableDomainTransferLock`, `AssociateDelegationSignerToDomain`, and `DisassociateDelegationSignerFromDomain`. That list is useful evidence because it names the decisions an approval system should preserve instead of flattening them into `registrar.write`.

I use four questions before any agent receives registrar access:

- Can this call redirect resolution for the whole domain?
- Can it make a later theft or recovery easier?
- Can it change who receives control or verification messages?
- Can it make validating resolvers reject otherwise correct DNS answers?

If two calls produce different answers, they are different approval actions. Sharing an API key does not collapse them.

## A nameserver change delegates the whole zone

A nameserver replacement hands DNS authority to the new server set, so it carries a much larger blast radius than editing one record. RFC 8499 defines delegation as the parent adding an NS record set for the child origin. Once caches follow that delegation, the new authoritative servers can answer for the zone's web addresses, mail exchangers, service discovery records, and verification TXT records.

That last category makes the risk easy to underestimate. RFC 8555 says an ACME client can prove control of a domain by provisioning a TXT value under `_acme-challenge`. An operator who controls the delegated zone can answer that challenge and seek certificates for names under the domain, subject to the certificate authority's checks. A nameserver change is therefore not merely a hosting setting.

Approval should show the complete old and new server sets, not a sentence such as `update DNS`. Sets matter because a migration often adds servers before removing old ones, while some registrar APIs replace the full set in one call. The reviewer needs to see whether the request preserves every intended server, whether glue addresses are involved, and whether the target servers already answer authoritatively for the zone.

Before approval, query each proposed server directly rather than trusting a recursive cache:

```sh
for ns in ns1.new-dns.example ns2.new-dns.example; do
  dig +norecurse +short @"$ns" example.com SOA
  dig +norecurse +short @"$ns" example.com MX
done
```

A healthy check returns an SOA line from each server and the expected MX targets. Empty output, inconsistent serials, or answers without authority need investigation. The exact migration plan decides whether serials must already match, but the reviewer should never approve a server name that has not answered for the zone.

The approval record should identify this action as `nameserver.replace`, include the ordered request and a normalized set diff, and state whether the call also changes glue. Do not bury it inside a generic domain update because a plausible agent mistake here can reroute every service at once.

## Removing a transfer lock opens a window

Disabling a transfer lock does not transfer a domain, but it removes one control that prevents a transfer. ICANN calls the familiar registrar lock `clientTransferProhibited` or a similar status. RFC 5731 says a transfer request must be rejected while `clientTransferProhibited` or `serverTransferProhibited` applies.

That distinction matters during review. An agent may correctly need to remove a domain's lock before a planned move, yet an unexpected lock removal is still dangerous because it creates a useful precondition for another actor. Treat `transfer_lock.disable` as its own action, and require the request to name the destination registrar, change ticket, and expected relock or completion time. Those fields do not make the registrar enforce the plan; they give the approver enough context to reject an unexplained lock removal.

The authorization code is a separate secret and a separate decision. Retrieving it should never hide inside approval for lock removal. A useful control plane asks once to remove the lock and again to retrieve or use the transfer authorization code. If the workflow also changes the registrant, the order matters because ICANN's Transfer Policy requires a 60 day inter-registrar lock after a Change of Registrant in covered cases, unless the registrar offered an opt out before the change and the registrant chose it.

A popular recommendation says to leave the domain locked and therefore treat registrar automation as safe. The lock helps, but that advice stops one step too early. A credential that can remove the lock can also erase the protection. Per-call approval makes the removal visible at the moment it matters, while a standing policy that merely checks the current locked state can be invalidated by the next API request.

Enabling the lock is usually a lower risk repair, but it is still a write with operational consequences. It can block a legitimate transfer already in progress. Show the pending transfer state and let a human confirm unless an incident procedure explicitly authorizes emergency relocking.

## Contact changes alter the recovery path

A registrant or administrative contact update changes who receives important notices and may change transfer eligibility. It does not redirect traffic immediately, which tempts teams to rank it below nameserver work. That ranking ignores how domain recovery actually happens.

ICANN tells registrants to keep contact information current because registrars send protection and management notices by email. Its Transfer Policy also links a Change of Registrant to a 60 day transfer lock in covered circumstances. An agent that replaces a registrant email moments before a planned transfer can delay the work. An attacker who changes a reachable contact may interfere with notifications or future recovery, depending on the registrar and registry process.

The approval card needs a field level diff. `contact.update` is too vague when the request changes an email address, telephone number, organization, privacy setting, and registrant identity at once. Show each old value and new value, mark which contact roles change, and display whether the registrar reports a resulting transfer restriction. Masking can protect personal data in routine logs, but the person authorizing a sensitive identity change must see enough to recognize the intended recipient.

Do not let an agent solve a failed contact update by repeatedly mutating fields until the API accepts them. Registries have different validation rules, and some country code domains apply additional requirements. A safer workflow validates the payload, requests approval for the final diff, submits it once, and records the provider's operation identifier. If the provider processes the change asynchronously, the action remains pending until a read confirms the intended state.

Contact privacy is another distinct action. Changing privacy exposure is not the same as changing the underlying registrant. A reviewer may accept a privacy toggle while rejecting an ownership change, so an approval system should not combine them simply because a vendor uses one endpoint.

## DNSSEC changes can make correct answers fail

A DNSSEC delegation change modifies the chain by which validating resolvers authenticate the child zone. RFC 4034 is precise: the DS record refers to a DNSKEY using a key tag, algorithm, and digest, and the DS record lives on the parent side of the delegation. The corresponding DNSKEY lives in the child zone. Registrar APIs often carry DS material to the registry because the registrant cannot edit the parent zone directly.

A wrong nameserver can redirect answers. A wrong DS record can make resolvers mark genuine answers as bogus. That is a different failure mode and needs a different review. Deleting DS may turn a securely delegated zone into an insecure delegation after caches update. Adding a DS that does not match a published DNSKEY can break validation. Removing the old DS too early during key rollover can strand validators that still rely on cached data.

Approval for `dnssec.ds.add`, `dnssec.ds.replace`, and `dnssec.ds.remove` should display the key tag, algorithm, digest type, digest fingerprint, and the DNSKEY evidence collected from every authoritative server. The reviewer should also see whether the request adds overlap during a rollover or replaces the only DS in one move.

These commands expose the two sides of the chain:

```sh
dig +short example.com DS
dig +short example.com DNSKEY
dig +dnssec example.com A
```

The first query asks the normal resolution path for the parent side DS data. The second retrieves the child DNSKEY set. The third shows the answer and DNSSEC records used for validation; on a validating path, the response flags can include `ad` when the resolver authenticated the data. Do not reduce the review to matching the numeric key tag alone. The digest and algorithm must match the intended DNSKEY, and the rollout sequence must account for caches.

A DNS host's one click `enable DNSSEC` flow can coordinate these details for a human. An agent using registrar and DNS APIs separately does not inherit that safety automatically. Require a proposed sequence, evidence that the new key is published, and an explicit approval for every parent side mutation.

## Approval should bind to a normalized action

The approval object should describe meaning rather than expose only a raw HTTP request. URLs, provider operation names, and JSON shapes vary. A stable internal action name lets the reviewer recognize the same security event across registrars without pretending the providers behave identically.

This is a practical action envelope for an agent tool boundary:

```json
{
  "action": "nameserver.replace",
  "domain": "example.com",
  "before": {
    "nameservers": ["ns1.old-dns.example", "ns2.old-dns.example"]
  },
  "after": {
    "nameservers": ["ns1.new-dns.example", "ns2.new-dns.example"]
  },
  "reason": "CHG-1842 registrar migration",
  "evidence": {
    "authoritative_checks": "passed",
    "checked_at": "2026-07-24T14:25:00Z"
  },
  "request_hash": "sha256:..."
}
```

The gateway should derive the action from the method, endpoint, and validated body. The agent must not choose a friendly action label while sending a different request. Bind approval to a canonical request hash so the agent cannot obtain consent for one nameserver set and submit another. If an asynchronous provider requires a second call to confirm an operation, classify and approve that call according to what it commits.

Normalization also exposes mixed mutations. RFC 5731 permits one domain update command to touch nameservers, contacts, status values, and authorization information. If a registrar endpoint accepts several categories in one payload, split the workflow before approval when the API permits it. When it does not, show every action in the approval and apply the strictest decision. A label such as `domain.update` tells the reviewer almost nothing.

Reject requests that omit the current state. Without a fresh read, the diff may be built against stale assumptions and overwrite somebody else's change. Use the provider's version token or conditional request feature when available. If it offers neither, read immediately before submission, compare the normalized state to the approved `before` value, and stop on any mismatch.

## Reads and writes need different friction

Per-call approval should cover sensitive uses of the registrar credential, not train people to click through harmless inventory reads. If every list operation interrupts a developer, the approval mechanism becomes an obstacle and reviewers stop reading. The correct boundary follows effect and disclosure.

Unauthenticated public DNS lookups need no registrar credential. Authenticated reads of domain inventory, private contact data, transfer codes, billing data, or pending operations deserve different handling. A domain detail read may expose personal contact information. An authorization code retrieval creates immediate transfer capability even though an API catalog may call it a read.

Use a small classification table and keep it understandable:

Listing managed domains and reading masked status can usually use session approval because they disclose inventory without changing it. Retrieving a transfer authorization code needs per-call approval because it produces a secret that can enable a transfer.

Nameserver replacement, transfer lock removal, registrant or administrative contact changes, and every addition, replacement, or removal of DS data need per-call approval because each changes control or validation. Renewal is policy specific: it costs money but normally preserves control, so a team may preauthorize it within a defined spend limit.

The last row is intentionally not forced into the same answer. Renewal has financial and lifecycle effects, but its risk differs from redirecting or transferring a domain. Teams may preauthorize renewal within a spend limit and still require a human for every delegation change. That is the point of naming actions clearly.

Do not use HTTP method alone as the classifier. Some APIs use a generic POST for reads and writes. Do not use a broad provider category such as `Write` either. The Route 53 Domains authorization reference correctly lists nameserver replacement, contact update, and transfer lock removal as separate permissions, but an approval layer still needs the actual domain and value diff.

## The reviewer needs evidence, not agent prose

An agent generated explanation is context, not proof. Approval should lead with facts gathered independently: the authenticated process, the exact domain, the normalized action, the before and after values, and the check results. Put the agent's reason after those fields.

For a nameserver request, collect authoritative SOA and essential record checks from each proposed server. For a DS request, collect the current parent DS and child DNSKEY sets. For lock removal, collect the current status and any pending transfer. For a contact change, display the changed roles and the registrar's stated lock consequence. The code that gathers this evidence should sit on the trusted side of the boundary because an agent can accidentally summarize stale output or omit an inconvenient difference.

The approval must expire. A five hour old nameserver diff may no longer describe current state. Expiry should be short enough to limit drift and long enough for a human to inspect the evidence. Avoid a universal number copied across every workflow; set it from the registrar's concurrency model and the team's response time, then reject if the `before` state changes.

Batch approval is safe only when every member is visible and homogeneous. Approving the same nameserver migration for twenty parked domains can be reasonable if the card lists every domain and the evidence passes for each. Combining a nameserver change, a lock removal, and a contact update into one `approve migration` button destroys the distinctions the reviewer needs.

An approval denial should stop the call, not invite the agent to rephrase and retry until someone clicks yes. Record the denial with the request hash. A materially changed request may ask again, but the system should make the difference obvious.

## Verification is part of the action

A successful HTTP response often means the registrar accepted a job, not that the registry and DNS now expose the intended state. Several domain APIs return an operation identifier for later polling. Treat submission, provider completion, and public verification as separate states in the journal.

A compact verification sequence catches most surprises after a sensitive change:

1. Save the provider's operation identifier and poll its documented status until it reaches a terminal state.
2. Read the registrar state again and compare it with the approved `after` object.
3. Query parent side NS and DS data through more than one recursive path, then query the authoritative servers directly.
4. Test a small set of services that depend on the zone, including mail routing and certificate validation records when those services exist.
5. Close the change only after the evidence matches, or execute the prepared recovery path.

This is one of the few places where retries need blunt limits. Retrying a timed out read is ordinary. Retrying a state changing request after an unknown response can create overlapping jobs or toggle a value twice. Before retrying, query by idempotency token or read the current operation and domain state. If the provider offers no idempotency mechanism, escalate the ambiguous result instead of guessing.

Rollback also differs by action. Restoring old nameservers may recover delegation, but caches delay convergence. Re-enabling a transfer lock can close an exposure if no transfer has progressed. Restoring a contact may trigger another confirmation or lock. Restoring an old DS can fail if the matching private signing key is no longer active. The recovery record must name the action specific reversal and its prerequisites before approval.

A maintenance window is not a substitute for verification. DNS changes propagate through caches according to existing TTLs, and registrar operations may be asynchronous. Keep monitoring until the old and new states behave as the migration plan predicts.

## Separate credentials do not replace per-call consent

Least privilege still matters. Give an agent access only to the registrar account, domains, and operations it needs, and keep billing or unrelated portfolio access out when the provider permits it. Short lived credentials and process isolation reduce exposure further.

But splitting one broad token into four credentials does not prove intent. The agent can still misuse the nameserver credential inside its allowed scope, and a compromised process can wait for the right token to appear. Separate credentials reduce the maximum blast radius. Per-call consent decides whether a particular blast should occur.

Session authorization and per-call approval solve different problems. Session authorization answers whether this agent process may act through the gateway during its run. Per-call approval interrupts the few operations where a human must compare intent with exact effects. Reads and routine low impact calls can continue under the session, while a registrar key marked for approval pauses on every use.

Sallyport implements that separation with a vault gate, authorization for each new agent process, and a per-key option that requires approval on every use while keeping the registrar secret out of the agent. For a broad registrar credential, mark the key per call and make the request description carry the normalized action and diff described above.

Do not teach the agent to hold the API token temporarily so it can finish a batch. Once the token enters the model process, approval becomes advisory because later calls can bypass the gate. The component that stores the credential must execute the approved request itself and return only the result.

## The audit record must reconstruct the decision

An audit entry saying `agent called registrar` cannot settle an incident. The record must show what the human saw, what they approved, what bytes were sent, what the provider returned, and what verification observed later. Otherwise the team can prove activity without proving authorization.

For each sensitive call, preserve the process identity, session identifier, approver, decision time, normalized action, domain, before and after objects, request hash, provider operation identifier, response status, and verification result. Protect personal contact values according to your data handling rules, but retain a stable digest or controlled encrypted copy so investigators can distinguish two changes.

Keep denial and revocation events too. If an operator revokes an agent session after seeing an unexpected lock removal request, that sequence explains why later calls stopped. A write only, hash chained log makes silent editing detectable, while independent export or verification protects against relying on the same interface that performed the action.

Sallyport records agent sessions and individual calls from one encrypted, hash-chained audit log; `sp audit verify` checks the chain offline over ciphertext without the vault key. Verification proves the recorded chain has not been altered, while the action envelope supplies the domain specific meaning investigators need.

The approval record also needs to survive provider abstraction. Store the provider operation name beside the normalized action, not instead of it. That lets an investigator trace a portable label such as `dnssec.ds.remove` back to the exact API call without forcing every reviewer to learn each vendor vocabulary. Preserve the canonical request bytes or their cryptographic digest and record which normalization version produced the action. If the classifier changes later, investigators can reproduce the decision that the operator actually saw rather than applying new logic to old events.

Test that record before trusting it. Run a harmless staging-domain change through approval, deny a modified request with the same stated reason, revoke the agent session, and verify that the journal preserves all three events in order. Then simulate an ambiguous provider timeout and confirm that the workflow reads state instead of blindly resubmitting. An audit design that works only for successful calls will fail during the incident it was meant to explain.

The first useful change is to stop calling the registrar tool `update domain`. Give nameserver replacement, transfer lock removal, contact mutation, authorization code retrieval, and DS mutation their own names. Then place human consent at the call that carries each effect. The credential may remain broad because a provider designed it that way, but the decision no longer has to be.
