Dynadot rejects set_ns when any NS hostname is not yet registered
as a glue record in the customer's account. The 31-line code comment
above SetNameservers documents this requirement but the implementation
never landed at the adapter layer — only the per-request handler-side
glueIP path (BYO Flow B, issue #900) registered glue, leaving the
mothership parent-domain onboard flow exposed.
Live blocker on 2026-05-15: founder attempted zero-touch onboard of
fresh parent domain omani.homes; the flow stalled because
ns3.openova.io had never been registered as a Dynadot glue record on
this account (ns1/ns2 had been registered long ago when openova.io
itself was onboarded). Failure surface:
"'ns3.openova.io' needs to be registered with an ip address before
it can be used."
Required out-of-band manual API calls to unblock, defeating the
zero-touch property the architecture is supposed to deliver.
Fix (adapter layer, no per-request flag, always-on when configured):
- Adapter gains NSGlueIP field; SetNameservers iterates every NS
hostname BEFORE set_ns, skips in-bailiwick children of the domain
being set, calls RegisterGlueRecord(host, NSGlueIP) for the rest.
- RegisterGlueRecord (already idempotent per issue #900) short-
circuits via get_ns on identical IP, falls through to set_ns_ip
on a stale IP, and runs register_ns when the host is missing — so
a SetNameservers retry costs only get_ns probes, not extra writes.
- A typed registrar error inside the register loop returns
immediately without calling set_ns (fail-fast contract).
- POOL_DOMAIN_MANAGER_NS_GLUE_IP env var (canonical operator-config
pattern in this repo) threaded through cmd/pdm/main.go onto the
Dynadot adapter at PDM startup. Empty value preserves prior
pass-through behaviour, keeping BYO Flow B handler-level glue
authoritative for per-request Sovereign add-domain calls.
Tests (httptest server, 7 new cases) cover:
- AllFresh: 3 NS hostnames, all unregistered → 3× (get_ns+register_ns)
+ set_ns (7 API calls, in order).
- OneAlreadyRegistered: middle NS short-circuits via get_ns,
others register, set_ns runs.
- RegisterFails_SetNsNotCalled: 429 mid-register surfaces
ErrRateLimited unwrapped; set_ns must NOT execute.
- SetNsFailsAfterRegister: pre-register completes, set_ns
returns Dynadot error; ErrDomainNotInAccount surfaces.
- SkipsInBailiwick: in-bailiwick NS hostname (child of domain
being set) is skipped entirely (no get_ns, no register_ns).
- DisabledWhenNSGlueIPEmpty: backward-compat — bare SetNameservers
issues exactly one set_ns call when env var unset.
- IsInBailiwickHost: case- and trailing-dot-tolerant table test.
go build ./... and go test ./... both green across the entire
core/pool-domain-manager module.
Co-authored-by: hatiyildiz <hatice.yildiz@openova.io>