# 0075. adopt skips unapproved machines and names the v1.14 documents it cannot capture

Status: Accepted · Date: 2026-10-26 · Design section: Bootstrap (`ichor adopt`), Patch layers

Decided: `adopt` reads approved machines only; for the v1.14 documents Ichor cannot yet express it says which document and why. Whether those documents become patch kinds or spec fields is left open (below).

## Context

A real-cluster test on Talos v1.14.3 could not reach zero drift. Two findings.

1. `ichor adopt` reported `KubeNetworkConfig`, `UnattendedInstallConfig` and `KubeFlannelCNIConfig` (in another run:
   pod subnets, service subnets, cert SANs, `UnattendedInstallConfig`) as fields it "cannot patch automatically", and
   the tester fell back to `versionContract` v1.13, where the default pod subnet 10.244/16 then collided with the outer
   network.
2. `adopt` dialed every `TalosMachine` of the cluster, including a pending machine in maintenance mode, and failed with
   a TLS error. The tester had to disable discovery, delete the machine, adopt, and re-enable discovery.

## What is true (Talos machinery v1.14.3, Ichor at this commit)

- `pkg/render.baseConfig` calls `generate.NewInput` with only the secrets bundle, the version contract and the
  control-plane scheduling option. It passes no pod or service subnet, no install disk or image, no certificate SANs and
  no CNI choice. Nothing in `TalosCluster.spec` or `TalosMachine.spec` carries those values either (there are no such
  fields).
- At contract v1.14 the generator therefore emits its defaults: `KubeNetworkConfig` with 10.244.0.0/16 and
  10.96.0.0/12, `KubeFlannelCNIConfig` (vxlan), an empty `machine.certSANs`, and no `UnattendedInstallConfig` at all
  (`generate/init.go` only emits it when an install disk was passed). Below v1.14 the same values live in the legacy
  `cluster.network`, `machine.install` and `machine.certSANs`.
- The `TalosConfigPatch` document kinds are `SysctlConfig`, `KernelModuleConfig`, `Config`, `VolumeConfig`,
  `UserVolumeConfig` and `RawVolumeConfig`. `KubeNetworkConfig`, `UnattendedInstallConfig`, `KubeFlannelCNIConfig` and
  `KubeAPIServerConfig` (cert SANs) are not among them.
- The only route left is the legacy `Config` document, and Talos rejects it next to the v1.14 documents:
  `KubeNetworkConfig` and `KubeFlannelCNIConfig` conflict with `cluster.network`, `UnattendedInstallConfig` conflicts
  with `machine.install` (`V1Alpha1ConflictValidate` in machinery). `ichor init` writes `machine.install` and
  `machine.certSANs` through such a patch, which works only because the base render has no install document.
- So at v1.14 an Ichor-rendered cluster can have neither non-default subnets, nor a chosen install disk (other than the
  legacy form), nor "no flannel". A node generated by `talosctl gen config` (or by a harness that drops the flannel
  document) differs from the render in exactly those documents, and `adopt` cannot capture them because it only
  captures `SysctlConfig` and `KernelModuleConfig`.

## Decision

Done here:

- `adopt` only reads machines with `approvalState: approved`. Pending and rejected machines are not dialed, and the
  report says `machine "x": skipped, approval state is pending (...)`. An unapproved machine has no credentials in the
  bundle and, in maintenance mode, its Talos API accepts no client certificate, so a failure there is certain.
- When the paths adopt leaves alone belong to `KubeNetworkConfig`, `UnattendedInstallConfig` or `KubeFlannelCNIConfig`,
  the report names the document, what it holds, and that no patch kind exists for it. The old advice to "write a
  TalosConfigPatch by hand" is kept only for the legacy `Config` paths, where it is true.

## Open: how a v1.14 cluster gets these values

Decided by ADR 0077 (option C, with the per-node half as a spec field).

Not decided here, because the design document does not settle it and each option reaches beyond `pkg/render`.

A. Extend the patch allow-list (design: "Kinds outside the allow-list are listed, and the user chooses between
   extending the list and dropping them"). Add `KubeNetworkConfig`, `UnattendedInstallConfig` and `KubeAPIServerConfig`
   as `mirrorgen` roots; then `adopt` can capture them and subnets/disk/SANs are patch values at the right layer
   (cluster for subnets, machine for disk and SANs). Cost: CRD size, the proto and generated TS, the UI kind list,
   `fieldmeta` rules, conversion in `DocumentFromPatch`. A patch cannot delete a document, so "no flannel" cannot be a
   patch.
B. Spec fields. `TalosCluster.spec.network.{podSubnets,serviceSubnets}`, `TalosMachine.spec.install.{disk,image}` and
   cert SANs, rendered by the system layer into whichever form the contract allows (the pattern `injectKubeProxy`
   already uses). "No flannel" falls out of the system layer: drop `KubeFlannelCNIConfig` when `spec.cni.preset` is not
   `flannel` (design: the system layer generates "CNI settings"). Cost: CRD changes, webhook rules (subnets immutable
   after creation), `adopt` would map drift to those fields.
C. A mix: B for the cluster-wide, install-time values (subnets, CNI), A for per-node documents.

Dropping the flannel document for non-flannel presets changes the rendered hash of existing v1.14 Cilium clusters, so
that step needs its own review of what the drift check does to them.
