feat: add Talos bootstrap provider support - #2
Open
metal3d wants to merge 33 commits into
Open
Conversation
Add a new optional BootstrapProvider field (kubeadm|talos, default kubeadm) to the XOMachine and XOMachineTemplate specs. When set to "talos", the controller passes bootstrap data directly to XO without cloud-init transformation, SSH key injection, or kube-vip.
…hing Clusters created from flat templates (e.g. Talos) may lack the standard cluster.x-k8s.io/cluster-name label that the ClusterResourceSet selector relies on, leaving the CCM/CSI never applied. Add the label on the owner Cluster during XOCluster reconciliation, and grant the controller patch/update permissions on Clusters.
…ckage Split the infrastructure provider (VM lifecycle) from the bootstrap provider. Move kubeadm cloud-config generation, SSH key injection, kube-vip handling and Talos passthrough into a dedicated internal/bootstrap package behind a Provider interface (kubeadm|talos). The VM creation path now receives the network config as a parameter instead of re-detecting the bootstrap provider, removing the import cycle between xomachine and bootstrap. Kubeadm helpers (GetXOCluster/GetOwnerMachine) stay in the xomachine package. Add unit tests for the kubeadm, talos and resolve logic with a dedicated Ginkgo suite and generated XO client mocks.
Add flat (non-ClusterClass) CAPI templates to deploy a Talos Linux cluster on Xen Orchestra, as an alternative to the kubeadm/cloud-init flow. Talos replaces both the OS and the bootstrap provider (TalosConfig/TalosControlPlane). Templates use nocloud images with the xen-guest-agent extension and viridian disabled. The base layout holds community templates with placeholders; per- environment overlays (real XO UUIDs) are git-ignored via templates/*/overlays/.
Mirror the Talos layout under templates/kubeadm: move the community kubeadm templates (clusterclass, machinetemplates, example-cluster, clusterctl, packer) into templates/kubeadm/base and add a top-level kustomization that aggregates them. Per-environment overrides live in templates/kubeadm/overlays and are git-ignored. Also update the example Cluster scaffold: drop the empty clusterNetwork (validated only), point the ClusterClass to the prefilled variant, remove the unsupported remediation.maxInFlight from the MachineHealthCheck, and fix the Makefile and docs to reference the new base paths.
Rebuild dist/install.yaml and dist/infrastructure-components.yaml from the current source. Adds the bootstrapProvider CRD field to both XOMachine CRDs, the cluster.x-k8s.io RBAC rules (clusters/machines/machinedeployments) for the ClusterResourceSet matching, and the KubeadmControlPlane binding pointing at the correct capi-kubeadm-control-plane-system namespace.
nathanael-h
reviewed
Aug 28, 2026
nathanael-h
reviewed
Aug 28, 2026
nathanael-h
reviewed
Aug 28, 2026
nathanael-h
reviewed
Aug 28, 2026
Rename examples Co-authored-by: Nathanaël <7300309+nathanael-h@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds Talos Linux as an alternative bootstrap flow alongside kubeadm, and refactors bootstrap-related logic so the XO infrastructure provider can support multiple bootstrap providers cleanly.
Changes:
- Introduces an internal
bootstrappackage with a provider interface and kubeadm/talos implementations (including bootstrap provider detection). - Updates VM creation and disk selection logic to better handle config drives (and Talos nocloud naming) plus adds/updates unit tests.
- Reorganizes and adds templates for both kubeadm and Talos into a consistent
base/+overlays/layout, and regenerates dist/CRDs/RBAC accordingly.
Reviewed changes
Copilot reviewed 40 out of 66 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| templates/talos/README.md | Adds Talos template documentation and usage instructions |
| templates/talos/kustomization.yaml | Wrapper kustomization pointing to Talos base templates |
| templates/talos/base/talos-xomachinetemplate-worker.yaml | Adds worker XOMachineTemplate for Talos with bootstrapProvider: talos |
| templates/talos/base/talos-xomachinetemplate-cp.yaml | Adds control-plane XOMachineTemplate for Talos with bootstrapProvider: talos |
| templates/talos/base/talos-xocluster.yaml | Adds XOCluster example for Talos (VIP handled by Talos config) |
| templates/talos/base/talos-machinedeployment.yaml | Adds worker MachineDeployment using TalosConfigTemplate |
| templates/talos/base/talos-controlplane.yaml | Adds TalosControlPlane manifest with strategic patches (VIP, NTP, CCM) |
| templates/talos/base/talos-configtemplate.yaml | Adds TalosConfigTemplate for worker join configuration |
| templates/talos/base/talos-cluster.yaml | Adds flat Cluster object wiring infra + Talos control plane |
| templates/talos/base/kustomization.yaml | Base Talos kustomization bundling all Talos manifests |
| templates/README.md | Updates template layout docs for kubeadm vs Talos and base/overlays organization |
| templates/kubeadm/packer/README.md | Documents the new Packer-based AlmaLinux template build flow |
| templates/kubeadm/packer/Makefile | Adds Make targets for building/converting the Packer image |
| templates/kubeadm/packer/cloud-data/user-data | Adds cloud-init user-data used by Packer builds |
| templates/kubeadm/packer/cloud-data/meta-data | Adds empty meta-data file for nocloud/cidata seed |
| templates/kubeadm/packer/almalinux-10-k8s.pkr.hcl | Adds Packer QEMU template for AlmaLinux 10 Kubernetes node image |
| templates/kubeadm/base/machinetemplates/prefilled/rhel-xomachinetemplate-worker-prefilled.yaml | New prefilled worker XOMachineTemplate in reorganized layout |
| templates/kubeadm/base/machinetemplates/prefilled/rhel-xomachinetemplate-cp-prefilled.yaml | New prefilled control-plane XOMachineTemplate in reorganized layout |
| templates/kubeadm/base/machinetemplates/prefilled/kustomization.yaml | Kustomization for prefilled machine templates |
| templates/kubeadm/base/machinetemplates/kustomization.yaml | Kustomization entrypoint for machine templates (from-scratch + prefilled) |
| templates/kubeadm/base/machinetemplates/from-scratch/rhel-xomachinetemplate-worker-from-scratch.yaml | New from-scratch worker XOMachineTemplate in reorganized layout |
| templates/kubeadm/base/machinetemplates/from-scratch/rhel-xomachinetemplate-cp-from-scratch.yaml | New from-scratch control-plane XOMachineTemplate in reorganized layout |
| templates/kubeadm/base/machinetemplates/from-scratch/kustomization.yaml | Kustomization for from-scratch machine templates |
| templates/kubeadm/base/kustomization.yaml | Top-level kubeadm base kustomization aggregating clusterclass/machinetemplates/example |
| templates/kubeadm/base/example-cluster/kustomization.yaml | Rehomes the example cluster kustomization under kubeadm/base |
| templates/kubeadm/base/example-cluster/cp-machinehealthcheck.yaml | Updates CP MHC manifest under new structure |
| templates/kubeadm/base/example-cluster/capi-cluster.yaml | Updates example Cluster topology class names under new structure |
| templates/kubeadm/base/clusterctl/cluster-template.yaml | Moves clusterctl template under kubeadm/base for dist packaging |
| templates/kubeadm/base/clusterclass/rhel-xoclustertemplate.yaml | Adds XOClusterTemplate under the new kubeadm/base layout |
| templates/kubeadm/base/clusterclass/README.md | Updates clusterclass README paths for new structure |
| templates/kubeadm/base/clusterclass/prefilled/rhel-prefilled-worker.yaml | Adds/relocates prefilled worker KubeadmConfigTemplate |
| templates/kubeadm/base/clusterclass/prefilled/rhel-prefilled-control-plane.yaml | Adds/relocates prefilled KubeadmControlPlaneTemplate |
| templates/kubeadm/base/clusterclass/prefilled/clusterclass-rhel-prefilled.yaml | Adds/relocates prefilled ClusterClass definition |
| templates/kubeadm/base/clusterclass/kustomization.yaml | Adds clusterclass kustomization to include both variants |
| templates/kubeadm/base/clusterclass/from-scratch/rhel-from-scratch-worker.yaml | Adds/relocates from-scratch worker KubeadmConfigTemplate |
| templates/kubeadm/base/clusterclass/from-scratch/rhel-from-scratch-control-plane.yaml | Adds/relocates from-scratch KubeadmControlPlaneTemplate |
| templates/kubeadm/base/clusterclass/from-scratch/clusterclass-rhel-from-scratch.yaml | Adds/relocates from-scratch ClusterClass definition |
| README.md | Updates top-level README to point to the reorganized templates and new bootstrap directory |
| Makefile | Updates release artifacts generation to copy the clusterctl template from new path |
| internal/controller/xomachine/vm_ops.go | Passes provider-specific network config into VM create + improves main disk detection |
| internal/controller/xomachine/vm_ops_disk_test.go | Updates tests for new disk selection/cloud-config-drive detection behavior |
| internal/controller/xomachine/resolve.go | Removes bootstrap-data resolution from xomachine package (moved to bootstrap) |
| internal/controller/xomachine/resolve_test.go | Removes tests for the moved bootstrap resolution logic |
| internal/controller/xomachine/k8s_helpers.go | Adds K8s helper(s) (owner Machine lookup, XOCluster lookup) to avoid import cycles |
| internal/controller/xomachine/k8s_helpers_test.go | Adds tests for new K8s helper(s) |
| internal/controller/xomachine/cloud_config_test.go | Deletes prior cloud-config/bootstrap helper tests moved into internal/bootstrap |
| internal/controller/xomachine_reconcile.go | Switches reconciliation to bootstrap-provider interface and passes provider network config |
| internal/controller/xomachine_mock_test.go | Adds Talos passthrough test coverage for VM creation path |
| internal/controller/xocluster_controller.go | Ensures owner Cluster has cluster-name label for CRS matching + adjusts owner ref selection |
| internal/controller/xocluster_controller_test.go | Adds unit tests for ensuring cluster-name label |
| internal/bootstrap/talos.go | Implements Talos bootstrap provider (verbatim user-data + placeholder network-config) |
| internal/bootstrap/talos_test.go | Tests Talos bootstrap helpers |
| internal/bootstrap/suite_test.go | Adds Ginkgo test suite for bootstrap package |
| internal/bootstrap/resolve.go | Adds bootstrap data resolution + provider detection (kubeadm vs talos) |
| internal/bootstrap/resolve_test.go | Tests bootstrap resolution and provider detection |
| internal/bootstrap/mock_xoclient_test.go | Adds generated gomock for XOClient used by bootstrap tests |
| internal/bootstrap/kubeadm.go | Moves/renames kubeadm cloud-config build logic into bootstrap provider implementation |
| internal/bootstrap/kubeadm_test.go | Tests kubeadm cloud-config and SSH key merge logic in new package |
| internal/bootstrap/bootstrap.go | Defines bootstrap Provider interface and dependency bundle + provider selection |
| dist/install.yaml | Regenerates install bundle with new CRD field(s) and RBAC changes |
| dist/infrastructure-components.yaml | Regenerates clusterctl components bundle with new CRD field(s) and RBAC changes |
| config/rbac/role.yaml | Regenerates manager RBAC to allow patch/update of Clusters (for label ensuring) |
| config/crd/bases/vates.infrastructure.cluster.x-k8s.io_xomachinetemplates.yaml | Regenerates CRD to include spec.bootstrapProvider |
| config/crd/bases/vates.infrastructure.cluster.x-k8s.io_xomachines.yaml | Regenerates CRD to include spec.bootstrapProvider |
| api/v1beta2/xomachine_types.go | Adds spec.bootstrapProvider to XOMachine API |
| .gitignore | Ignores per-environment template overlays under templates/*/overlays/ |
Files not reviewed (1)
- internal/bootstrap/mock_xoclient_test.go: Generated file
Suppressed comments (1)
templates/talos/README.md:82
- This
kubectl get talosconfigexample uses two output flags (-o yamland-o jsonpath), which is invalid, and it also omits the TalosConfig name so the jsonpath won’t match a list response.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+14
to
+16
| # Retrieve talosconfig: | ||
| # kubectl get talosconfig -o yaml talos-cp-XXXX -o jsonpath='{.status.talosConfig}' | ||
| # |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
On PR, the tag wasn't well defined (extrap path part). We now use `pr-XXX` where XXX is the event number.
nathanael-h
reviewed
Aug 31, 2026
| // 2. Auto-detect from the owner Machine's bootstrap configRef (TalosConfig/TalosConfigTemplate = talos) | ||
| // 3. Default "kubeadm" | ||
| func DetectBootstrapProvider(spec infrastructurev1beta2.XOMachineSpec, machine *clusterv1.Machine) string { | ||
| if spec.BootstrapProvider != "" { |
Member
There was a problem hiding this comment.
Can spec.BootstrapProvider be en empty string?
In api/v1beta2/xomachine_types.go#L56, it defaults to kubeadm doesn'it?
Rewrite all READMEs to use the overlay pattern consistently: - Users create an overlay under overlays/<your-env>/ with kustomize patches containing their real XO UUIDs, instead of editing base/ directly. - base/ is now clearly documented as a community artifact with placeholders that must never be modified. - Add templates/kubeadm/README.md with complete overlay file contents (kustomization, patch-cp, patch-worker, patch-cluster). - Expand templates/talos/README.md with full overlay creation guide (kustomization, patch-cp, patch-worker, patch-controlplane, patch-xocluster). - Remove all cross-references between READMEs (see <other-README>). - Remove references to lab1/cogent1 (overlays/<your-env>/ instead). - README root: add Talos section, document controller deployment via clusterctl init --bootstrap talos, and expand Quick start with overlay directory structure for both kubeadm and Talos. - templates/README.md: rewrite Workflows to use overlays, update field table to include clusterclass/topology fields.
The Talos bootstrap and control plane providers (CABPT / CACPPT) are not registered by default in clusterctl and must be added manually to ~/.cluster-api/clusterctl.yaml. Without this step, clusterctl init fails with missing TalosControlPlane CRDs. Document the required provider URLs and configuration in both the Talos README and the root README.
…by default Remove incorrect instructions to add Talos providers to clusterctl.yaml. clusterctl already includes CABPT and CACPPT in its default provider list (verified via clusterctl config repositories). Document the namespace error workaround instead: if clusterctl init fails with 'target namespace can't be defaulted', add providers with explicit namespace in ~/.cluster-api/clusterctl.yaml.
Add local development section showing how to point clusterctl at a local build of the vates provider via ~/.config/cluster-api/clusterctl.yaml. Separate the troubleshooting (namespace error) into its own section. Mention the config path in the root README Talos section.
The vates provider is not published to the network, so clusterctl init --infrastructure vates fails trying to fetch it from GitHub. Document that users MUST create a local file:// override in ~/.config/cluster-api/clusterctl.yaml (via make release-manifests) before running clusterctl init.
dist/install.yaml only deploys the vates provider — it does not install CAPI core or the TalosControlPlane / TalosConfig CRDs. Those come from the Talos bootstrap/control-plane providers (CABPT/CACPPT) installed via clusterctl. Document both approaches: everything via clusterctl, or vates via install.yaml plus clusterctl init --bootstrap talos --control-plane talos.
The CACPPT/CABPT binding was included in config/rbac and thus shipped in dist/install.yaml and dist/infrastructure-components.yaml by default, even for users not using Talos. Move it to a dedicated kustomization (config/rbac/talos) applied on demand with kubectl apply -k config/rbac/talos. Drop the now-unnecessary KCP subject namespace patch in config/default (kustomize no longer rewrites non-default subjects) and document the optional Talos binding in the READMEs.
…alos templates Nodes stuck in NotReady for 5 minutes are now remediated: workers via MachineSet replacement, control-plane via KCP/TalosControlPlane. Use remediation.triggerIf.unhealthyLessThanOrEqualTo (CAPI >= 1.8) instead of the removed maxInFlight field.
…l template - Split talos/base into machinetemplates/ and example-cluster/ like kubeadm - Add clusterctl/cluster-template.yaml for clusterctl generate cluster - Document real clusterctl count defaults (CP 1 / workers 0, not base replicas)
…mscratch/almalinux-prefilled Bootstrap fixes: - clusterctl MINOR collision, KCP v1beta2 machineTemplate.spec.infrastructureRef (apiGroup) - injectSSHKeys, unique node hostnames (cp/worker-<ip>), /etc/hosts node resolution - Cilium postKubeadmCommands valid bash, config_path for containerd 2.x mirrors - admin.conf fallback on join CP nodes, YAML flow-sequence guard fix Perf: - single dnf transaction + weak-deps off, background kubeadm image pull - no kubectl on workers, cilium status wait 120s -> 90s Rename: - from-scratch -> almalinux-fromscratch, prefilled -> almalinux-prefilled - examples/ left untouched (backup) WIP: perf still dominated by serialized KCP CP rollout; rename makes room for other distro from-scratch templates
…-vip interface fix Addons (XOCluster spec.addons): - ccm/csi/cni optional, one ClusterResourceSet per addon (cni ApplyOnce) - embedded Cilium manifest (go:embed cni-cilium.yaml), default addons.cni=cilium in templates - Cilium removed from postKubeadmCommands (flat + ClusterClass) - tests for defaultAddons/ensureAddon/removeAddon/reconcileAddons Kube-vip: - detect interface at runtime (ip route default) instead of hardcoded eth0 (Debian uses enX0); fixes VIP not being announced XOMachine disk: - isCloudConfigDrive no longer matches generic 'cloud-init' names (fixes resize skipped for 'Ubuntu 24.04 Cloud-Init (Hub)') Debian: - new debian-fromscratch.yaml (apt, containerd.io, unattended-upgrades lock fix, systemctl restart containerd after config, no CNI in bootstrap) - Ubuntu templates removed Dev tooling: - .dockerignore (build context was ~15GB of packer images) - Makefile.dev: unique dev tag per build, verify running pod image
VM names in XO now include a unique suffix from the CAPI machine name (buildVMName), so CP/worker VMs of a cluster no longer share the same name_label (e.g. PFT-demo3-cp-7n62g instead of PFT-demo3-cp). Apply identifying tags to every VM (best-effort, idempotent): - cluster-name:<cluster> - machine:<CAPI machine name> - role:control-plane|worker Tags are set via the tag.add RPC after creation and on every reconcile (both fast-path ready VMs and the main path), so pre-existing VMs get tagged after upgrade. vm.set rejects 'tags' in this XO version. Tests: buildVMName (cp/worker/fallback) and vmTags (role detection).
Variables and parameters named after the old 'vates' branding are renamed to match the XOCluster/XOMachine types: vatesMachine -> xoMachine, vatesCluster -> xoCluster. Also fixes stale 'vatesmachine' package comments in generated mocks and VatesMachine mentions in comments/tests. No functional change.
XO VMs are now always tagged with a static 'vates-capi' marker and a 'bootstrap:<provider>' tag (kubeadm or talos). The bootstrap tag value is derived from the bootstrap Provider Name(), which is mandatory for every bootstrap provider, so new providers are tagged automatically. Tags are set at VM creation and on every reconcile (fast-path ready VMs detect the provider via the owner Machine), so pre-existing VMs get the new tags after upgrade.
golangci-lint v2.11.4 could not typecheck with Go 1.26+/1.27 export data
('internal/goarch: export data version 4 > max supported version 2'), and the
custom build was pinned to v2.11.4 in .custom-gcl.yml, overriding the Makefile
version. Bump both to v2.13.2.
goconst is not auto-fixable by --fix: set ignore-tests: true and replace the
remaining prod occurrences ('true' x3, 'default' x4) with constants
(trueStr, defaultNamespace).
…es to notes/
- make release-manifests now also ships metadata.yaml in dist/, making the
release bundle complete (infrastructure-components.yaml + metadata.yaml +
cluster-template.yaml) as required by the clusterctl provider contract.
- add make dev-overrides to refresh the local clusterctl overrides from dist/
(a file:// url cannot point at dist/ directly: clusterctl requires a
{basepath}/{provider-name}/{version}/ layout for version resolution).
- README: add 'Installing the provider with clusterctl' section (local
overrides vs published GitHub release) and link RELEASING.md.
- add RELEASING.md with the release recipe (build/push image, release-manifests,
GitHub release assets, GOPROXY caveat).
- convert 'see x/README.md' inline-code references into markdown links.
- rename docs/ (personal notes, gitignored) to notes/.
The local clusterctl config url was hardcoded to /home/<user>. Use the
${HOME} variable supported by clusterctl in file:// urls. Also fix the Talos
README which pointed at dist/ directly (a path clusterctl rejects: a file://
url requires the {provider}/{version}/ layout) — point at the overrides layer
refreshed by make dev-overrides.
The target refreshed the overrides dir but never created the
~/.config/cluster-api/clusterctl.yaml entry pointing at it, so the user had
to write it by hand. Now dev-overrides creates the file (with ${HOME} url)
when missing, and warns when an existing config does not register vates
(without modifying it).
The dev-overrides target is a development convenience (refresh local clusterctl overrides + config), so it belongs in Makefile.dev, not the main Makefile. Docs updated to use 'make -f Makefile.dev dev-overrides'.
…tall steps The local-install sections instructed to run 'make release-manifests' (already a prerequisite of dev-overrides) and to manually create ~/.config/cluster-api/clusterctl.yaml (now created by dev-overrides). Reword to reflect that a single 'make -f Makefile.dev dev-overrides' regenerates dist/, refreshes the overrides and creates the config if missing; manual config is only a fallback.
…terClass The quick start led with the ClusterClass + overlay workflow (managed topology), which is not the primary provisioning path: demos and the default dist template use flat 'clusterctl generate cluster --from' templates, and the Talos flow has no ClusterClass at all. Restructure into: 1) install CAPI + vates, 2) create a cluster (flat kubeadm template), 3) ClusterClass as an optional managed-topology variant documented in templates/kubeadm/README.md.
…er schema golangci-lint v2.13.2 requires go >= 1.26.0, but the CAPI maintainers target Go 1.25, so CI (setup-go reads go.mod -> 1.25.8, GOTOOLCHAIN=local) could not build it. Revert to v2.11.4 (buildable with Go 1.25) and pin GOTOOLCHAIN to go1.25.8 for the lint targets, so local builds (Go 1.27) match CI and vendored linters don't choke on a newer stdlib. Also: - restore the kubebuilder-vendored hack/golangci.v2.11.jsonschema.json and the '--schema' flag in lint-config (it was deleted in favor of an embedded schema that only v2.12+ supports). - goconst ignore-tests is not valid in the v2.11 schema: replace it with a standard path exclusion for *_test.go. - fix the remaining prod goconst finding by replacing 'cilium'/'none' string literals with constants.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Talos Linux as an alternative bootstrap provider to kubeadm, and separates the infrastructure provider (XO VM lifecycle) from the bootstrap provider so both cluster types run side-by-side from the same CAPI controller.
Highlights:
internal/bootstrappackage with aProviderinterface (kubeadm + talos impls), removing the xomachine ↔ bootstrap import cycle. Talos machine config passes through to XO verbatim (no cloud-init transformation, SSH key injection, or kube-vip handling);spec.bootstrapProvider(kubeadm | talos) selects the provider, auto-detected from the Machine bootstrap configRef.XOCluster.spec.addons{ccm,csi,cni}— one ClusterResourceSet per enabled addon (Cilium CNI embedded); defaults ccm/csi on, cni none (templates set cilium).PFT-demo3-cp-7n62g) and XO tags (vates-capi,bootstrap:kubeadm|talos,cluster-name,machine,role).enX0); Debian template handles the unattended-upgrades lock and containerd CRI restart.templates/talos/andtemplates/kubeadm/in a shared base/ + overlays/ layout; Debian 13 from-scratch kubeadm template added (Ubuntu removed).vatesMachine/vatesClusterrenamed toxoMachine/xoCluster.RELEASING.md), README quick start restructured aroundclusterctl generate, golangci-lint pinned to the Go 1.25 line,make lint-fixgreen.Validated: kubeadm clusters (AlmaLinux 9, Debian 13 from-scratch) and a Talos cluster booted on the same controller; all nodes Ready with providerID, CCM + CSI deployed, both Available=True. Debian cluster ready in ~14 min; VMs identifiable by unique names and tags in XO.