0092. Approval sets the primary NIC, its addressing and extra disk roles as typed spec fields

Status: Accepted · Date: 2026-10-27 · Design section: Nodes (approval, disk roles)

Decided: TalosMachine.spec.network (one primary link chosen by permanent MAC, DHCP or static addressing, optional VLAN, DNS servers) and TalosMachine.spec.disks (roles ephemeral and userVolume on disks identified like spec.install.disk) are written at approval by the UI form and ichor machine approve flags; pkg/render emits a LinkAliasConfig alias net0 with a CEL selector on the permanent MAC and refers to the alias from DHCPv4Config, LinkConfig, VLANConfig and ResolverConfig, a VolumeConfig for EPHEMERAL and a UserVolumeConfig per volume, and the legacy machine.network.interfaces before the multi-document contract; link addressing documents are classified live in pkg/fieldmeta, aliases and everything else keep the reboot-required default; bonds, the storage role and unused are not done

Context

ADR 0077 gave approval the system disk and left the rest of the design's approval step ("role, labels, disk roles and basic networking") without a field to carry it, so the form could not say which NIC to use or how to address it, and a node with a second disk could not put EPHEMERAL or a data volume on it.

Decision

  1. spec.network {mac, addressing, addresses, gateway, dnsServers, vlanID}. mac is the link's permanent address (pattern in the CRD). addressing is dhcp (default) or static; static needs at least one address (isCIDR) and dhcp takes no addresses or gateway (CEL on the struct). gateway and each DNS server are isIP. vlanID is 1 to 4094. Unset leaves Talos's default. The field is mutable in the CRD; what a change costs is the fieldmeta classification below, not an admission rule.
  2. spec.disks[] {role, disk, name, maxSize}. role is ephemeral or userVolume; disk is the same one-of selector as spec.install.disk. A user volume needs a name (letters, digits, hyphen, at most 32, mounted at /var/mnt/<name>) and may carry maxSize (200GiB, MiB/GiB/TiB). At most eight entries, one ephemeral, unique names (CEL). Whether the system disk and an ephemeral disk differ is not expressible in CEL (the two selectors can name one disk differently), so the UI checks it and Talos decides otherwise.
  3. Network rendering, from the multi-document contract (MultidocNetworkConfigSupported, Talos above 1.11, so both v1.13 and v1.14):
    • LinkAliasConfig net0, selector mac(link.permanent_addr) == "<lower-case mac>", parsed against celenv.LinkLocator() so a wrong expression fails at render. This is the fix for interface names changing.
    • DHCP: DHCPv4Config named by the alias (IPv4 only; DHCPv6 is not requested).
    • Static: LinkConfig for net0 with up: true, the addresses, and a default route when a gateway is set.
    • VLAN: the alias gets a LinkConfig with up: true and nothing else, and a VLANConfig net0.<id> with parent: net0 carries the addresses and routes, or has a DHCPv4Config of its own.
    • DNS servers: a ResolverConfig nameservers list, merged into the generator's own ResolverConfig. Contract 1.11 and older render one machine.network.interfaces entry with deviceSelector.permanentAddr, the VLAN under it, and machine.network.nameservers; the legacy default route is 0.0.0.0/0 or ::/0 by gateway family.
  4. Disk rendering: a VolumeConfig named EPHEMERAL whose provisioning.diskSelector.match is the same CEL expression spec.install uses (diskExpression), which replaces the generator's own EPHEMERAL document by merge; a UserVolumeConfig per volume with that selector, grow: true, maxSize if given, otherwise minSize: 1GiB because Talos refuses a user volume with neither. These are emitted for every contract, as the documents exist well before 1.12.
  5. fieldmeta (pkg/fieldmeta), read against the Talos v1.14.3 controllers in internal/app/machined/pkg/controllers/network: LinkConfig, VLANConfig and DHCPv4Config are safe (link_config.go, address_config.go, route_config.go, operator_config.go take the active MachineConfig as a weak input and rewrite their specs on every change, and link_spec.go, address_spec.go, route_spec.go, operator_spec.go program the kernel); ResolverConfig.nameservers is safe on the same grounds as the existing machine.network.nameservers rule (resolver_config.go). LinkAliasConfig is left at the reboot-required default although link_alias_config.go also reacts live, because it renames the handle every other document uses. ResolverConfig.hostDNS and the volume kinds are unchanged: VolumeConfig.provisioning is already install-time-only and UserVolumeConfig keeps the default, as the design says a new user volume on an unused disk needs no reset. "Safe" is about the reboot, not about reachability: an address change on the link the controller dials moves the node (ADR 0086 covers re-finding it).
  6. Approval. ApproveMachineRequest.network and .disks, mirrored on Machine; TalosLink.permanent_mac is new on the wire (the details RPC showed the current MAC, which differs for a bond member or a spoofed address). The backend re-checks everything the CRD checks and requires the MAC to be one of the machine's discovered identity MACs, so a typo is InvalidArgument with the reason. CLI: --nic MAC, --address CIDR (repeatable; any address makes it static), --gateway, --dns (repeatable), --vlan, --ephemeral-disk DISK, --user-volume NAME=DISK[,size=200GiB] (repeatable), with DISK in the --install-disk syntax. UI: a card picker listing physical links as name, MAC and speed (a typed MAC if the node reports none), address fields, and disk pickers from the disks GetNode reads.
  7. Layer. Network and disk documents are system layer, applied with the install document and below every patch, so a TalosConfigPatch can still override them.

Not done

  • Bonds. The design says a bond takes members by name or alias and a MAC form emits one alias per member; that needs a second link, a mode and a BondConfig, and is its own change.
  • The storage role (needs a TalosStorage preset, ADR 0050), unused (no document to render) and RawVolumeConfig.
  • Whether Talos still runs its default DHCP on a link that has a LinkConfig but no DHCPv4Config was not traced in operator_config.go; static addressing is rendered as written and not checked against a node.
  • No end-to-end run on a real node. The documents are validated by Talos's own machinery at render time and the golden tests check their content at v1.14, v1.13 and v1.11.
  • The "accepted, no effect" report for an edited EPHEMERAL selector on an installed node (design, Nodes) is not part of this change; VolumeConfig.provisioning is already classified install-time-only.
  • The details RPC only reports links of a maintenance-mode node through the insecure API, so a node whose Talos API is unreachable gets a typed MAC.

Search the docs

Search pages and headings