Feature Specification: Setups and Wiring

Created: 2026-09-22

Status: Implemented

Input: Reconstructed from the code at 0bf4ecd36 (core/setups/groups/, core/setups/hosts-common.nix, core/setups/wiring/, core/setups/nixos/, core/apex.nix), pull requests #1818, #1822, #1828, #1831 and #1836, commit ba2b92c41, issue #527, and the design notes of the maintainers.

User Scenarios & Testing (mandatory)

User Story 1 - Generate a full deployment from a minimal configuration (Priority: P1)

A hosting provider gives a group of machines, an apex domain and a hosting configuration. The setups generate the full NixOS configuration of each machine: DNS records and zones, TLS certificates and trust, SMTP, secrets, workload identity, the database roster, and the routing of resources to their providers.

Why this priority: without the setups, each provider writes this wiring by hand, and the deployment programs have nothing to deploy.

Independent Test: evaluate the nixosConfigurations of the operator group with the default configuration. Each machine evaluates, and the zone has a record for each published name.

Acceptance Scenarios:

  1. Given a group with published and internal machines, When the setups evaluate, Then only the published machines get public DNS names.
  2. Given DNS_DOMAIN, When the setups evaluate, Then all names are under that apex.
  3. Given a component that provides a capability (for example OIDC or SMTP), When another component needs it, Then the wiring connects them, whatever the node names are.

User Story 2 - Change one wiring decision with confidence (Priority: P2)

A developer changes how one decision is made (for example which node gets a TLS certificate). The decision is a pure function in core/setups/wiring/, and a nix-unit test asserts on the same expression that the deployment uses.

Independent Test: change a decision function so it gives a wrong result. Its nix-unit-setups-* check fails.

Edge Cases

  • A node has no address. The zones do not include it.
  • The public uplink value is not a CIDR. The evaluation fails.
  • DNS_DOMAIN is not set. The evaluation fails with an error that names DNS_DOMAIN.

Requirements (mandatory)

Functional Requirements

  • FR-001: The setups MUST define the groups (operator, hosting-provider, dev, hypervisor), each with published and internal machines.
  • FR-002: The setups MUST generate the NixOS configuration of each machine from the group, the apex and the hosting configuration.
  • FR-003: Each wiring decision MUST be a pure function with its own nix-unit test that asserts on the expression the deployment uses.
  • FR-004: Wiring MUST read the capabilities that components provide and need, not node names.
  • FR-005: The setups MUST read the apex domain once, from DNS_DOMAIN, and MUST NOT fall back to a default apex. A fallback apex conflicts with the goal of portable infrastructure.
  • FR-006: The setups MUST NOT include a machine without an address in a zone.
  • FR-007: The groups of core MUST NOT contain machines whose components only a front-end provides. A front-end adds such machines with the fold helpers.
  • FR-008: The setups MUST give the overview page the browser UIs of only the nodes that the deployment deploys (usedNodes). A node that an external service replaces (such as netbox with an external NetBox) MUST NOT get a tile.
  • FR-009: When the overview page sets servedAtApex (the default), the setups MUST also serve it at the apex domain. Both zone sources (the eval-time zone and the NetBox renderer) MUST give the apex a copy of the A and AAAA records of the overview node, because a CNAME cannot be at the apex. At most one node MUST set servedAtApex.
  • FR-010: The central group (the one group whose roster provides varsBackend) MUST find the other groups from their group data or components, not from their names. The apex and the deploy runners come from the central group. The operator groups are the groups that state fqdnFor. The central-login groups are the groups that set centralLogin. The central group selects these groups from the groups of the caller, and evaluates the model of each selected group from the same groups, so a group that only the caller defines has a model. The node that runs the containers is the one published node that provides instances. The other groups read the central group as centralGroup.

Key Entities

  • Group: a named set of published and internal machines.
  • Machine: a node with hardware, addresses and a set of components.
  • Apex: the domain that all names of a deployment are under.
  • Wiring decision: a pure function from the model to one part of the configuration (DNS, TLS, SMTP, routing, served domains, node selection, workload identity, database roster, NetBox pinning, public uplink, S3 keys).

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: All nix-unit-setups-* checks pass.
  • SC-002: The integrations-bridge-dns, integrations-smtp-*, integrations-spire-*, integrations-openbao-pki and integrations-incus-* checks pass.

Assumptions

  • The groups of the hosting provider and of the operator are in core today. core can split after the split of the monorepo (issue #527).

[NEEDS CLARIFICATION: the central OpenBao and OpenTelemetry hosts are read from the central group (the group whose roster provides varsBackend) in core/setups/hosts-common.nix (a TODO says they must come from remote state). Must a deployment of one provider work without the group of another?]

Known Deviations

None.