Feature Specification: Resource dns
Created: 2026-10-07
Status: Implemented
Input: Reconstructed from the code at 093e68b6f (core/resources/dns/default.nix, core/resources/dns/contract.nix, core/resources/dns/self-hosted.nix, core/resources/dns/external.nix, core/lib/coordinate-resource.nix, core/lib/coordinate-provider.nix, core/setups/wiring/dns-routing.nix, core/setups/wiring/resource-routing.nix, core/setups/hosts-common.nix, core/components/hosting-provider/windmill/default.nix), pull request #1235.
User Scenarios & Testing (mandatory)
The dns resource gives a node the coordinates of the authoritative DNS
service of its deployment. It does not write records. The deployer writes
records with the octodns-zone effect (see
010 Effects). The shared resource rules are in the
resource family spec. The resource has the same shape as
netbox: both use core/lib/coordinate-resource.nix.
User Story 1 - Find a self-hosted DNS server (Priority: P1)
A node needs to know where the deployment's own named node listens. The
deployment has no external DNS service. The routing row selects the bind
provider and sets fediversity.dns-self-hosted.host to the address of the
DNS primary. The node gets the result at
contracts.dns.results.<consumerName>.default.
Why this priority: a deployment without an external DNS service must publish its records to its own server.
Independent Test: nix-unit-setups-dns-routing, test
test-self-hosted-result.
Acceptance Scenarios:
- Given a
dnssetup argument ofnull, When a node requestsdns, Then the result is{ provider = "bind"; port = 53; scheme = "http"; apiTokenFile = ""; }plus the primaryhostand the requesteddomain. - Given a self-hosted route, When the result renders, Then
apiTokenFileis"", because the TSIG key reaches octoDNS through thesecretFiles.key_secretof theoctodns-zoneeffect.
User Story 2 - Use an external DNS service (Priority: P1)
A hosting provider already holds its zone at an external service (PowerDNS
by default). The setup gets a dns argument with a non-empty serverUrl.
core/setups/wiring/dns-routing.nix parses the URL into scheme, host and
port. The routing row selects the external provider. The roster drops
the self-hosted named nodes.
Why this priority: the deployment must not stand up a second authority for a zone that a third party holds.
Independent Test: nix-unit-setups-dns-routing, tests
test-external-result, test-external-overrides-domain,
test-external-coordinates-* and test-roster-drops-node-when-external.
Acceptance Scenarios:
- Given
serverUrl = "https://pdns.example.org", When the coordinates resolve, Thenschemeishttpsandportis443. - Given a
serverUrlwith no scheme and no port, When the coordinates resolve, Thenschemeishttpandportis8081. - Given an external route with its own
domain, When the result renders, Then thatdomainreplaces the requested one. - Given an external route, When the roster is built, Then it does not contain the self-hosted
namednodes, except a node that answers ACME challenges.
User Story 3 - Hand the coordinates to the operator deployer (Priority: P2)
The windmill component deploys operator groups. It requests
"dns".dns = { enable = true; consumerName = "windmill"; }. A client
module imports the contract module and, gated on the result, reads
contracts.dns.results.windmill.default into the operator curation.
The curation feeds HOSTING_CONFIG of the worker.
Why this priority: operator deploys publish their app names to the same DNS service as the hosting provider.
Independent Test: nix-unit-components-operator-curation.
Acceptance Scenarios:
- Given a
windmillnode, When its configuration evaluates, Then the curation contains thednscontract result of that node.
Edge Cases
external.host = ""makes the external provider inert.apiTokenFileis a path. The result never holds the token.- The policy option
domainmust already be a zone at the routed provider. The effect writes records and creates no zones. - A request with
enable = false, or a policy withenable = false, emits no module.
Requirements (mandatory)
Functional Requirements
- FR-001: The
dnscontract request MUST be{ domain }. The result MUST be{ provider; host; port; scheme; apiTokenFile; domain; }. - FR-002: The policy MUST copy its
domainoption into the request. - FR-003: The
bindprovider MUST fulfillprovider = "bind",hostfromfediversity.dns-self-hosted.host,portfromfediversity.dns-self-hosted.port(default53),scheme = "http",apiTokenFile = ""and the requesteddomain. - FR-004: The
externalprovider MUST passprovider(defaultpowerdns),host,port(default8081),scheme(defaulthttp) andapiTokenFilethrough, and MUST replace the requesteddomainwhen its owndomainis not empty. - FR-005:
dns-routing.nixMUST treat the DNS as external only when thednsargument is notnulland has a non-emptyserverUrl. - FR-006:
externalCoordinatesMUST deriveschemefrom the URL prefix andportfrom an explicit port, else443forhttps, else8081. - FR-007:
core/setups/hosts-common.nixMUST set"dns".dnswithenable = true, the group domain, androuting = mkDefault providerModules.dns, and MUST add theconsumerSideof itsprocessto each node. - FR-008: The
dnsrequest MUST hold only data:enableandconsumerName. TheconsumerSideMUST declare thewantof the request, so the node gets the contract result atcontracts.dns.results.<consumerName>.default. A client module MUST importcore/resources/dns/contract.nixto read it.
Key Entities
dnscontract: local contract incore/resources/dns/contract.nix.fediversity.dns-self-hosted: optionshostandportof thebindprovider.fediversity.dns-external: optionsprovider,host,port,scheme,apiTokenFileanddomainof theexternalprovider.dnssetup argument: the coordinates from theDNS_*environment or fromeffect-defaults.octodns-zoneinHOSTING_CONFIG.nullkeeps the self-hosted route.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001:
nix-unit-setups-dns-routingpasses. - SC-002:
nix-unit-components-operator-curationpasses. - SC-003:
nix-unit-resources-coordinate-consumerandnix-unit-resources-plain-requestspass.
Assumptions
- The zone exists at the routed provider before a deploy.
- The external service speaks the PowerDNS API unless
providersays otherwise.
Known Deviations
None.