0098. The Cilium bootstrap manifest is the embedded chart rendered with Helm
Status: Accepted · Date: 2026-10-28 · Design section: CNI · Extends: 0013, 0024
Decided: BootstrapManifest for the Cilium preset returns the pinned Cilium chart expanded with the preset values into plain manifests, rendered with Helm's Go SDK from a chart archive embedded in the binary, and the tunnel protocol is a preset option that defaults to Cilium's VXLAN
Context
ADR 0013 left chart expansion "to a later unit", so BootstrapManifest returned only the Helm values. ichor init --cni cilium put that values file in cluster.inlineManifests, which Talos cannot apply, so no CNI was installed and the first node stayed NotReady. On the e2e harness Cilium's default VXLAN tunnel also passes no traffic between nodes, while geneve works.
Decision
- The chart
cilium-1.18.0.tgz(the versionChartVersionpins, from https://helm.cilium.io/) is checked in underpkg/cni/cilium/chart/and embedded withgo:embed.make cilium-chartfetches it and refuses a file whose SHA-256 differs from the digest recorded in the Makefile. A test checks that the embedded chart's name and version equalChartNameandChartVersion. BootstrapManifestloads the archive, renders it withhelm.sh/helm/v3(engine, release namecilium, namespacekube-system) and the preset values, drops hooks and NOTES, and joins the rest in Helm's install order. There is no cluster access and no network. The output is deterministic. The 1.18.0 chart has nocrds/directory (the operator installs Cilium's CRDs itself), so none are included. Thecilium-secretsnamespace and its Roles come from the chart.- Rendered size with kube-proxy replaced is about 58 KB, which fits in the machine config comfortably.
Preset.WithTunnel("vxlan"|"geneve")setstunnelProtocolin the values. The default is unset, so Cilium's VXLAN. The same values go to the Argo CD Application, so the hand-over still shows no diff (ADR 0024).
Why
Embedding means no network at init time and the same bytes every run, which helm template against a live repo cannot promise. Downloading at render time was rejected for that reason. Helm v3 is used because it builds with the Kubernetes libraries Ichor already pins; v4 would pull a second set.
Consequences
Moving to a newer Cilium means bumping ChartVersion, the Makefile digest and the archive together. The binary grows by about 230 KB plus Helm's dependencies. ichor init --cni-tunnel picks the tunnel protocol and stores it in spec.cni.tunnel on the TalosCluster, so ichor gitops export reproduces it in the Argo CD Application instead of reverting to Cilium's default.
Amendment 2026-11-03: the bootstrap renders for the release set's Kubernetes and pins one operator replica per control plane
Found on the rs2 hardware run (ADR 0113 amendment). The bootstrap render used Helm's default capabilities, a Kubernetes version older than 1.33, so the chart omitted hostUsers from the operator and agent pod templates. The hand-over runs real Helm on the cluster (1.36.6), which renders hostUsers: true. The pod template changed, the operator Deployment rolled, and on a one-node cluster the rollout could not finish (the second replica has no node and no free host port), so the hand-over stalled.
- The render targets one Kubernetes version.
renderCharttakes its capabilities fromRenderKubernetesVersion(v1.36.6, the set's Kubernetes;pkg/releasechecks they agree), so the bootstrap and the hand-over render the same templates. The goldens now carryhostUsersand the native AppArmor profile that the chart gates on this version. - The operator has one replica for one control plane.
operator.replicasis 1 when the TalosCluster has one control plane (spec.controlPlane.count == 1, which covers thesinglepreset and single-control-plane clusters whose workers come later). Any other count keeps the chart's default of 2. Cilium's docs (Context7,docs.cilium.io/en/stable/overview/component-overview, read 2026-11-03) say the operator "is not in the critical path for any forwarding or network policy decision" and that "a cluster will generally continue to function if the operator is temporarily unavailable"; its failures delay IPAM and the kvstore heartbeat. They recommend a high-availability setup for resilience (the BGP operation page), which a one-node cluster cannot have. The chart requires one operator pod per node throughpodAntiAffinity(chart 1.20.4templates/cilium-operator/deployment.yaml), so a second replica on one node never schedules. The value is not an owned key: a user may still setoperator.replicasinspec.cni.cilium.values. - Readback. The release record's
operator.replicassays whether it was made for one control plane, so an adopt readback reconstructs the same values.
Covered by TestOperatorReplicasFollowTheControlPlaneCount (counts 0, 1, 2, 3) and the goldens.
The rule above covered only count == 1. An HA cluster has the same problem while it has one node: see ADR 0113, amendment 2026-11-04, where the replicas follow the nodes that exist.