Files
home-network-k3s-impl/bootstrap/flux/README.md
T
jakman85andClaude Opus 4.8 cbc2301b30 docs: reconcile docs/comments with post-upgrade cluster state
Text-only cleanup after the upgrade campaign. Updates stale version
strings, prose, and comments to the verified current facts; no
Kubernetes behavior or version pins changed except the kubectl
toolchain bump (below).

Verified facts applied: k3s v1.36.4+k3s1, Debian 13 trixie,
Cilium 1.20.1 / Gateway API v1.6.1, Longhorn 1.12.1, node RAM 24 GiB,
dedicated virtio2 SSD etcd disk per node, Velero chart 12.x / v1.18,
Grafana 13.2.0.

- Version strings across infra/ and platform/ READMEs + comments
- kubectl toolchain 1.31.4 -> 1.36.4 (devcontainer/ci/conformance
  versions.env; 1.31 vs a 1.36 cluster exceeded the +-1 skew)
- Retired-single-apps-Kustomization refs -> apps-wave-0/1/2 + apps-vms
  with per-wave SOPS decryption (flux/clusters docs + bootstrap.sh)
- COMPLETED/historical banners on the three executed plan docs
- Resolved TODO(WS-SEC) markers -> Renovate-maintained ranges
- Appended dated DECISIONS-LOG entry (2026-08-30) superseding the
  2026-08-26 LIVE STATE line

Left as revalidation items (not blind-edited): external-snapshotter
v8.2.0 vs 1.36, Cilium ext_authz/forward-auth relevance under 1.20.1,
conformance ImageVolume feature-gate assertions under 1.36.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-30 15:13:12 -05:00

5.6 KiB

bootstrap/flux — Flux bootstrap → clusters/dev (Phase 1, T-1.4)

Points Flux at the authored clusters/dev entrypoint and turns on SOPS decryption. Spec: ADR-0004 (Flux), docs/05 · Requirements: R-GITOPS-01 (pull-based reconcile), R-GITOPS-03 (drift correction), R-SEC-01 (SOPS decryption).

AUTHOR-ONLY. Nothing in this directory has been executed. No flux, kubectl, key-gen, or cluster contact was run. These are files + runbooks the PM/operator validates and runs.

Files

File Purpose
bootstrap.sh The single bootstrap entrypoint the Infra worker's final Ansible play calls. flux install → verify sops-age Secret → kubectl apply -k clusters/dev.
patches/platform-sops-decryption.yaml Strategic-merge patch enabling decryption.provider: sops + secretRef: sops-age on the platform Kustomization (realises the base TODO).
patches/apps-sops-decryption.yaml Same, for the app-wave Kustomizations. There is no single apps Kustomization any more — the apps tier is now apps-wave-0/1/2 + apps-vms, and SOPS decryption is layered on per wave (each wave Kustomization is patched by name in clusters/dev).
patches/flux-system-source-url.yaml Overrides the flux-system GitRepository url placeholder with the real bootstrap source (template — fill at bootstrap).

Why flux install + apply, not flux bootstrap git (Phase 1)

The reconciliation graph is already authored in clusters/base (read-only to this worker): flux-system.yaml is the flux-system GitRepository; platform.yaml, platform-config.yaml and the app-wave Kustomizations (apps-wave-0/1/2.yaml + apps-vms.yaml — the single apps.yaml was retired) are the Flux Kustomizations; clusters/dev is the overlay that patches cluster-vars (R-ENV-02) and layers SOPS decryption onto each Kustomization by name.

flux bootstrap git generates and owns its own gotk-sync GitRepository + Kustomization named flux-system and commits them into the repo — which would duplicate/conflict with the authored base. It also requires a writable remote git host, which does not exist in Phase 1 (spec docs/05 "Source-of-truth", CONTRIBUTING.md: bring-up is from a local/out-of-band bootstrap source; Gitea arrives in Phase 6). So Phase 1 uses flux install (controllers only) + kubectl apply -k clusters/dev (materialise the authored graph). This reconciles with clusters/base instead of regenerating it.

Bootstrap order (what runs, in sequence)

tofu apply → cloud-init → Ansible (k3s + Cilium)          [Infra worker, T-1.1..1.3]
        │
        ▼  (final Ansible play calls the entrypoints below)
1. bootstrap/sops/age-keygen.sh          # operator generates age key OUTSIDE repo
2. bootstrap/sops/age-key-secret.md      # operator installs private key as sops-age Secret
3. bootstrap/flux/bootstrap.sh           # flux install → verify sops-age → apply -k clusters/dev
        │
        ▼
Flux reconciles: GitRepository(flux-system) → Kustomization(platform) → Kustomization(platform-config)
→ Kustomization(apps-wave-0 → apps-wave-1 → apps-wave-2) + Kustomization(apps-vms),
with per-Kustomization SOPS decryption enabled (patches/) and cluster-vars overlay (R-ENV-02).

Interface note — for the Infra worker (T-1.3 Ansible)

Single entrypoint your final play calls: bootstrap/flux/bootstrap.sh (run inside the infra/devcontainer toolchain so pinned flux/kubectl are on PATH; FLUX_VERSION/SOPS_VERSION live in infra/devcontainer/versions.env).

Order your play must enforce (the age key is a hard dependency of a working decrypt):

  1. bootstrap/sops/age-keygen.sh (operator step — generates key outside repo).
  2. Install the sops-age Secret per bootstrap/sops/age-key-secret.md (the kubectl create secret template). bootstrap.sh fails fast if this Secret is absent.
  3. bootstrap/flux/bootstrap.sh (this entrypoint).

bootstrap.sh is idempotent-friendly (flux install and kubectl apply -k re-run safely) and never handles key material — it only verifies the Secret exists.

Handoff — clusters owner / PM (files this worker may not edit)

clusters/ is out of this worker's write scope, so three wirings are delivered as patches here and must be applied into the clusters/dev overlay (clusters/dev/kustomization.yaml patches:):

patches:
  - path: ../../bootstrap/flux/patches/flux-system-source-url.yaml    # real source URL
  - path: ../../bootstrap/flux/patches/platform-sops-decryption.yaml  # SOPS on platform
  - path: ../../bootstrap/flux/patches/apps-sops-decryption.yaml      # SOPS on apps

Alternatively the base TODOs in clusters/base/platform.yaml and each app-wave Kustomization (apps-wave-0/1/2.yaml, apps-vms.yaml) and the url in clusters/base/flux-system.yaml can be filled in directly (same effect) — the clusters owner's call. Either way the sops-age secretRef name and flux-system source name are kept in lockstep with the authored base.

Phase-6 cutover (WS-GITOPS — NOT now)

Once Gitea is stable, migrate the source to Gitea (docs/05, CONTRIBUTING.md). At that point flux bootstrap git becomes appropriate (there is a writable remote to own):

# ILLUSTRATIVE — Phase 6, not run in Phase 1.
flux bootstrap git \
  --url=https://gitea.home.keithjackson.dev/<org>/home-network-k3s-impl.git \
  --branch=main \
  --path=clusters/dev

Keep an off-cluster mirror so a source-of-truth outlives the cluster (docs/05 "Circularity & DR").