Files
jakman85andClaude Opus 5 6b4da27f84
gate / gate (push) Has been cancelled
gate / conformance (push) Has been cancelled
feat(prod): SOPS decryption (same age key as dev) + GitOps source on main
Operator decisions, 2026-09-01:
  1. Prod uses the SAME SOPS age key as dev.
  2. Prod tracks the SAME BRANCH as dev (`main`). Dev is a temporary instance and will be
     decommissioned once prod is deemed stable.

SOPS: eight `decryption: {provider: sops, secretRef: sops-age}` patches mirroring dev
exactly — platform, apps-wave-0/1/2, oauth2-proxy, headlamp, crafty, velero. The sops-age
Secret is still created OUT OF BAND on prod (bootstrap/sops/install-age-key.sh); the
overlay references it by name only, never inlines key material.

RECORDED CONSEQUENCE of the shared branch: once prod is live, EVERY MERGE TO main
RECONCILES BOTH CLUSTERS. There is no promotion step and no soak window — dev and prod
move together. That is a deliberate consequence of dev being temporary, not an oversight,
but it removes the "prove it on dev first" safety net that the last few days have leaned
on heavily. Worth revisiting if dev outlives its expected life.

*** BOOTSTRAP BLOCKER recorded, NOT papered over *** — the source URL cannot work on a
cold prod cluster, for two independent reasons found while wiring this:

1. SELF-HOSTING CIRCULARITY. Gitea runs ON THE DEV CLUSTER — the one slated for
   decommission. Prod syncing from it means prod's source of truth lives inside the
   cluster being retired. Gitea must move to prod (or off-cluster) BEFORE dev is torn
   down. This makes Gitea migration the FIRST task of the decommission plan, not the last.

2. SPLIT-HORIZON DNS SENDS IT TO THE WRONG PLACE. platform/coredns/coredns-custom.yaml
   answers the ENTIRE home.keithjackson.dev zone with ${GATEWAY_VIP} via a template
   plugin. On prod that is 192.168.2.20 — PROD's own gateway, where no Gitea listens. Prod
   Flux would resolve gitea.home.keithjackson.dev to itself and never reach a repository.
   This is the same split-horizon mechanism that makes dev work, inverted against us.

Bring-up therefore needs a TEMPORARY source (the bootstrap host-Gitea, or an off-cluster
mirror) with a cutover to the in-cluster URL once Gitea actually runs on prod — precisely
the sequence docs/05 describes for dev's own Phase-6 cutover. The branch is settled and
correct; only the host is blocked. Documented at the patch site and in
PROD-PROVISIONING.md §7.

Gate: MERGE GATE: PASS.

Serves: R-SEC-01, R-GITOPS-01, R-ENV-02, R-ENV-03. Task: T-7.1.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-01 16:27:32 -05:00
..

infra/ — node provisioning and the toolchain

OpenTofu (Proxmox VE) + cloud-init + Ansible bring up the 3-node k3s/Cilium dev cluster. Spec: ../home-network-k3s/docs/02-environments-and-provisioning.md.

Path What
tofu/ The "Create" stage: 3 cloud-init'd VMs on the Proxmox box (bpg/proxmox provider)
cloud-init/ First-boot baseline shipped as a Proxmox user-data snippet
ansible/ The "Baseline"/"Configure" stages: k3s HA, Cilium, kube-vip, the VPN egress gateway
devcontainer/ The pinned toolchain (tofu, ansible, sops, age, flux, kubectl, helm and the whole CI gate toolchain)
cluster.sh RETIRED — drove the old libvirt VMs; does not work against Proxmox
PHASE1-STATUS.md Current state of the cluster and how to reconnect to it
PHASE1-SCOPE.md Historical Phase-1 scoping doc (libvirt era — superseded)

Where the cluster actually lives

The dev cluster is not on this workstation. It runs on a Proxmox VE host; the nodes sit on the Proxmox LAN and are reached across a pfSense NAT:

Nodes k3s-dev-1/2/3 at 192.168.1.50/.51/.52 — 6 vCPU, 24 GiB, Debian 13, k3s v1.36.4
kube-API kube-vip VIP 192.168.1.49:6443, reached from the workstation via NAT 192.168.2.50:6443
Node SSH pfSense NAT 192.168.2.102:2250/2251/2252 → node :22 (Ansible only)
Ingress VIP 192.168.1.53 (Cilium L2, shared Gateway); .53–.60 reserved for LB VIPs

The earlier libvirt/KVM cluster on 10.73.0.0/24 is retired. Any doc or command referencing virsh, qemu:///system, or 10.73.0.x for the cluster is historical.