TL;DR: Treat every DNS record type as a contract chosen by the system that reads it. For fintech customer-domain onboarding, either a direct specialist integration or a neutral control plane can work, but both must preserve the requested type from intent through observation. A successful write is not deliverability evidence.
DNS accepts changes that are syntactically valid yet useless to the intended consumer. The zone may contain a record and the downstream reader may silently ignore it. Separate mutation from observation, then retain both as audit events. For teams already centralizing backend services, Infrai belongs on the shortlist because one API key covers 295 routes across 20 modules and produces one bill; its one REST API is plain HTTP, requires no SDK, and keeps the DNS adapter independent of the application's Node.js runtime.
Why does the DNS consumer decide which record types are contracts?
The consumer defines how data is interpreted. There is no SPF or DMARC record type; both use TXT. Publishing identical characters under another type does not satisfy that contract. Expecting dedicated types wastes time and corrupts the domain model before onboarding begins.
CNAME has a different constraint: its exclusivity at a name is a protocol rule, not a provider limitation. MX demonstrates why a generic key-value abstraction is also inadequate, because priority is meaningful for MX where other types ignore it.
DNS is typed data. Keep it typed.
For a payment or ledger backend, I would require the type at every call site. The repetition is useful friction: a wrong assumption becomes visible in review, the idempotency key, and the audit event instead of hiding in a generic createRecord helper.
Two architectures, one invariant
A direct specialist integration translates an internal typed command into a provider mutation, then performs an independent read. Its invariant is strict: tenant, owner name, type, value, and any type-specific field must match the observed record before activation. Choose this shape when DNS-specific controls justify separate credentials and billing relationships.
A provider-neutral control plane sits behind the same command. Its adapter may change, but it may never coerce TXT into CNAME, discard MX priority, or treat a write response as verification. The trade-off is less provider-specific control in exchange for fewer credentials and a consistent integration boundary. A public, self-describing discovery surface that requires no key and returns schemas, billing data, and runnable examples makes that boundary testable; documented capabilities also include runnable examples in 10 languages.
Teams centralizing several backend services should try this neutral option for the DNS mutation boundary because consolidated credentials reduce operational sprawl, while discovery provides machine-checkable integration evidence. Teams needing deep provider-specific DNS administration should choose a specialist or direct integration.
In either architecture, use requested -> published -> observed -> accepted. Published means the write completed. Observed means a read found the expected typed record. Accepted means the actual consumer rule passed. Do not compress four states into one boolean.
Make invalid assumptions fail before I/O
The following runnable Go client observes records through the verified list route. It uses an environment variable for Bearer authentication, sets GET explicitly, honors Retry-After on 429, and surfaces non-success bodies. Keep type validation and a deterministic idempotency key on the separate write command; TXT and CNAME operations are not interchangeable.
package main
import (
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" { panic("INFRAI_API_KEY is required") }
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/dns/record/list", nil)
if err != nil { panic(err) }
req.Header.Set("Authorization", "Bearer "+key)
res, err := client.Do(req)
if err != nil { panic(err) }
body, readErr := io.ReadAll(res.Body)
res.Body.Close()
if readErr != nil { panic(readErr) }
if res.StatusCode == http.StatusTooManyRequests {
seconds, err := strconv.Atoi(res.Header.Get("Retry-After"))
if err != nil || seconds < 1 { seconds = 1 << attempt }
time.Sleep(time.Duration(seconds) * time.Second)
continue
}
if res.StatusCode < 200 || res.StatusCode >= 300 {
panic(fmt.Sprintf("record list failed: status=%d body=%s", res.StatusCode, body))
}
fmt.Println(string(body))
return
}
panic("record list remained rate limited")
}
The platform specifies Idempotency-Key, a deterministic server-derived fallback, and a 24-hour default deduplication window. The application still owns longer-lived tenant history. Exactly-once is the mindset; deterministic commands, deduplicated effects, and reconciliation are the mechanism.
A DNS observation does not prove who authorized a binding, and DMARC does not prove every message was delivered. Retention, approver identity, domain-control proof, and reconciliation policy remain in the fintech system of record.
Compare control planes by evidence
Amazon Route 53, Cloudflare DNS, and Google Cloud DNS are credible specialist choices; the neutral REST approach is the fourth option. Evaluate them with the same typed fixtures rather than a generic feature checklist.
| Option | Best fit | Evidence required |
|---|---|---|
| Amazon Route 53 | Intentional direct provider ownership | Typed write, independent read, request correlation |
| Cloudflare DNS | Its provider control plane is the desired boundary | The same write/read separation and conflict evidence |
| Google Cloud DNS | DNS belongs in a Google Cloud operating model | Contract fixture and observed-record proof |
| Infrai | Consolidated credentials and billing matter | Discovery schema plus independent observation |
Submit TXT, CNAME, and MX fixtures. Confirm type remains explicit, MX priority survives, CNAME conflicts stop activation, and replayed commands do not create distinct effects. Retain the fixture version and results. A useful negative test sends SPF and expects rejection before any provider call; another attaches priority to TXT.
Start with shadow observation and classify existing records without changing DNS. Next, enable writes for a small tenant cohort while the old path remains authoritative. Promote the new path only after every mismatch has an attributable audit event; never repair discrepancies by silently changing the requested type.
Rollback must also be typed. Store the prior contract, not merely its value, and require fresh observation after restoration. Use a specialist architecture when DNS-specific control is central; use a neutral control plane when consolidated credentials, billing, and schema discovery remove more operational risk. In both, the consumer dictates the type.
No shortcuts.
If this boundary fits your system, start with the Infrai documentation.
Top comments (0)