Feature Specification: Resource dns

Created: 2026-10-07

Status: Implemented

Input: Reconstructed from the code of core/resources/dns/ and its routing in core/setups/ at 093e68b6f, pull request #1235.

User Scenarios & Testing (mandatory)

The dns resource gives a node the coordinates of the authoritative DNS service of its deployment. It writes no records: the deployer does, with the octodns-zone effect (see 010 Effects). Like netbox, it is a coordinate resource. The shared rules are in the resource family spec.

User Story 1 - Find the authoritative DNS service (Priority: P1)

A deployment must publish its records to exactly one authority, and must not stand up a second authority for a zone that a third party holds. Without an external DNS service, the deployment publishes to its own bind primary. A hosting provider that holds its zone at an external service (PowerDNS by default, or deSEC in the account of the operator lane) gives the setup its URL, and the deployment then has no authoritative servers of its own. In both cases the node gets the provider kind, the address, port and scheme, a path to an API token, and the zone.

Independent Test: a unit test of the DNS routing.

Acceptance Scenarios:

  1. Given no external service, When a node requests dns, Then the result points at the bind primary with no token, and the deployer authenticates with the TSIG key of the effect.
  2. Given an external service URL, When the coordinates resolve, Then the scheme and port come from the URL, and a zone of the external provider replaces the requested zone.
  3. Given an external route, When the roster is built, Then it drops the self-hosted DNS nodes, except a node that answers ACME challenges.

User Story 2 - Hand the coordinates to the operator deployer (Priority: P2)

Operator deploys publish their app names to the same DNS service as the hosting provider. windmill requests dns for itself and puts the result into the operator configuration of its workers.

Independent Test: a unit test of the operator configuration.


Edge Cases

  • An external provider without a host is inert.
  • The result holds a path to the API token, never the token.
  • The zone must already exist at the provider. The effect creates no zones.

Requirements (mandatory)

Functional Requirements

  • FR-001: The request MUST hold the zone. The result MUST hold the provider kind, host, port, scheme, API token path and zone.
  • FR-002: The policy MUST put its zone into the request.
  • FR-003: The bind provider MUST answer with its own kind, the host and port of the primary (the standard DNS port by default), plain HTTP, no API token, and the requested zone.
  • FR-004: The external provider MUST pass its kind (PowerDNS by default), host, port, scheme and token path through, and MUST replace the requested zone with its own zone when it has one.
  • FR-005: The setup MUST treat the DNS as external only when it receives an external service with a URL.
  • FR-006: The setup MUST take the scheme from the URL, and the port from the URL, else from the scheme, else the default port of the PowerDNS API.
  • FR-007: The setup MUST request dns on each node with the domain of the group and the default routing row, and MUST add the consumer side to each node.
  • FR-008: The request MUST hold only data, and emits nothing unless both it and the policy are enabled. The consumer side MUST state the want, so the node gets the result under its consumer name. A module that reads the result MUST import the contract.

Key Entities

  • bind provider: the self-hosted route to the primary.
  • External provider: the route to a third-party service, with its kind, address, port, scheme, token path and an optional zone.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: The unit tests of the DNS routing, the operator configuration, the coordinate consumer and plain-data requests pass.

Assumptions

  • The zone exists at the routed provider before a deploy.

Known Deviations

None.