Commit Graph
12 Commits
Author SHA1 Message Date
jakman85andClaude Opus 4.8 df947b33d0 infra(tofu): fix backend.tf HCL header + document encryption-adoption migration
backend.tf began with a stray YAML `---` doc separator (invalid HCL) that broke
`tofu` parsing. Remove it. Also document the one-time `unencrypted` fallback method
needed to adopt encryption on pre-existing plaintext state (add for the migrate,
remove after), so the record is complete. State migration to R2 verified: 4/4
resources read back through the encrypt-only method (proves ciphertext at rest).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-27 16:31:23 -05:00
jakman85andClaude Opus 4.8 485b9e3888 infra(tofu): scaffold off-cluster R2 remote state (encrypted)
Local-only tfstate meant losing the workstation would strand a rebuild (apply
would try to recreate the 3 live nodes). Move state to Cloudflare R2, which sits
OUTSIDE the Proxmox->VM->cluster->Gitea chain, so a full cluster rebuild can still
read it; encrypted at rest via OpenTofu native AES-GCM.

Adds a partial s3 backend (R2 skip-flags), gitignored backend.hcl/.r2.env for the
account-specific + secret bits, their .example templates, and a migration+DR runbook.
Locking (use_lockfile) deferred to an OpenTofu >=1.10 bump (1.9.0 lacks it).
Migration (tofu init -migrate-state) runs once the operator provisions the R2 bucket.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-27 15:57:12 -05:00
jakman85andClaude Opus 5 12c40d8c9c docs: reconcile the documentation with the deployed reality
The docs had drifted badly behind ~85 commits of delivery. The root README still
said "Phase 0 — Foundations. No cluster exists yet"; the infra docs still
described a libvirt/KVM cluster that has been retired for two days; and
DECISIONS-LOG.md — the PM's authoritative record — stopped at 2026-08-25 AM.
Anyone following these docs would have been working from a false map.

Verified against the live cluster (3/3 nodes, 7/7 Flux Kustomizations Ready,
10/10 HelmReleases Ready) rather than inferred from commit messages.

README.md
  - Phase 0 -> a per-phase status table: 0-6 delivered and deployed, 7 not open.
  - Layout now shows the wave split, platform-config, apps/vms, infra, bootstrap.
  - Real conformance number (36/51, 70.6%) and how to regenerate it.
  - Devcontainer workflow; note that the gate was red and why.

infra/PHASE1-STATUS.md — rewritten. Was a `virsh`/libvirt resume guide for a
  cluster that no longer exists. Now: the Proxmox substrate, the pfSense NAT
  paths, both kubectl access paths (Keycloak OIDC normal / cert break-glass),
  the toolchain, off-repo state that must not be lost, and the fact that Flux's
  git source is an app inside the cluster (`git push origin main` IS the deploy).

infra/PHASE1-SCOPE.md — historical banner. The exit criteria and sizing
  rationale are still worth keeping; the libvirt execution steps are not.

infra/README.md — Proxmox, plus a map of the subtree and where the cluster
  actually lives (it is not on this workstation).

infra/devcontainer/README.md — the "run tofu and ansible on the HOST" limitation
  is OBSOLETE: it was a dmacvicar/libvirt polkit/PID problem, and bpg/proxmox
  talks to an HTTP API. Everything runs in the container now. Documents the full
  CI-gate toolchain and what each bind mount is load-bearing for (the age keyring
  and the read-only spec repo especially — without the latter, spec-immutability
  silently skips instead of verifying).

infra/tofu/README.md — was "Authoring only — nothing here has been applied". It
  is applied and live. Disk sizes corrected 40/100 -> 64/128 GiB (confirmed
  against the running nodes), pfSense NAT marked done, and a warning that
  terraform.tfstate is absent on a fresh workstation — an apply with empty state
  would try to recreate the three live nodes.

infra/tofu/variables.tf — section comment disagreed with its own defaults.

infra/cluster.sh — retired-with-instructions header. It silently drives libvirt
  domains that do not exist; behaviour unchanged, but it no longer lies.

apps/vms/unifi/README.md — was "NOT WIRED IN", 60Gi, and listed a file that has
  since been dropped. Now: live status, 40Gi, resolved open questions marked
  resolved, and the two constraints the next person will otherwise rediscover —
  KubeVirt's 2048B inline userData cap, and that apps-vms must never carry
  postBuild.substituteFrom (envsubst corrupts the cloud-init).

DECISIONS-LOG.md — backfilled the gap as one structured entry: the Proxmox
  migration, NAT + kube-vip, the twice-proven oauth2-proxy cold-start deadlock,
  the Flux source cutover, CI going live in-cluster, ten app onboardings and the
  renderer bugs they exposed, the wave split (verified: DBs adopted, not
  recreated — original ages intact, prune restored), KubeVirt storage, the UniFi
  VM, and kubectl SSO. Plus verified live state and the carried-forward open list.

Two corrections to the record while verifying:
  - kubectl OIDC is LIVE (`kubectl auth whoami` -> oidc:<user>,
    [oidc:platform-admins]), so the rolling restart 3472f1d deferred has
    happened.
  - UniFi OS Server is NOT installed in the guest. The VM is healthy (Ubuntu
    24.04.4, guest agent connected, live-migratable) but :11443 answers nothing,
    and the CNP explicitly permits cluster -> :11443, so it is genuinely not
    listening rather than a policy artifact.

Docs and comments only; no manifest semantics change.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-27 00:00:05 -05:00
jakman85andClaude Opus 4.8 28d200caf2 tofu(proxmox): use debian generic image + serial console to fix first-boot kernel panic (bpg#1639)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 11:45:29 -05:00
jakman85andClaude Opus 4.8 fca8d8f800 tofu(proxmox): download base image as import content-type for disk import_from
Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 10:59:01 -05:00
jakman85andClaude Opus 4.8 a2392a4a43 tofu(proxmox): API-only native cloud-init; move node baseline to Ansible node_baseline role
Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 10:55:48 -05:00
jakman85 8a8f28bf80 infra(proxmox): bump node disks to 64GB OS + 128GB Longhorn data (per operator) 2026-08-25 10:31:39 -05:00
jakman85andClaude Opus 4.8 4bb9c8fcbd infra(tofu): docs + example for the Proxmox migration
Update tofu README (Proxmox targets table, operator prerequisites:
enable Snippets content on `local`, endpoint scheme fix, pfSense NAT),
terraform.tfvars.example (Proxmox vars + env-only auth, token never a
tofu var), and the devcontainer versions.env provider note.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 10:23:28 -05:00
jakman85andClaude Opus 4.8 53ca698edc infra(tofu): migrate node provisioning from libvirt to bpg/proxmox
Replace the dmacvicar/libvirt provider + resources with the modern
bpg/proxmox provider. The dev cluster moves off the memory-constrained
local libvirt host onto the Proxmox VE box.

- versions.tf: pin bpg/proxmox ~> 0.66; provider reads auth from env
  (PROXMOX_VE_*, token never a tofu var) and normalizes a schemeless
  PROXMOX_VE_ENDPOINT (prepend https://) to avoid the :8006 HTTP 301.
- variables.tf: Proxmox placement vars (node=proxmox, datastore=Storage,
  image_datastore=local, bridge=vmbr0); LAN 192.168.1.0/24 gw .1 DNS .18;
  node_ips .50/.51/.52; sizing 6 vCPU / 16384 MiB / 40+100 GiB; cpu_type
  host; Cilium VIP range .53-.60.
- main.tf: proxmox_virtual_environment_download_file (cloud image -> local),
  per-node user-data snippet (proxmox_virtual_environment_file), and
  proxmox_virtual_environment_vm per node via name-keyed for_each (imported
  OS disk virtio0, blank data disk virtio1, native initialization ip_config
  + dns + user_data_file_id).
- remove moved.tf (libvirt-specific count->for_each state migration).

Authoring only; no apply. Traces R-ENV-01, R-PLAT-01.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-25 10:23:13 -05:00
jakman85andClaude Opus 4.8 95e3eafac6 iac: harden node lifecycle — tofu for_each + self-contained ansible rejoin
Implements T-1.5 findings F1/F2 (author-first, no cluster change).

F1 (tofu): refactor libvirt node resources count -> for_each keyed by
node name so single-node -replace/destroy cannot cascade to siblings.
Add ordered-list/primary helper locals; update outputs.tf off the map;
add moved.tf (12 moved blocks). tofu plan proves a pure state-address
migration: 0 to add, 0 to change, 0 to destroy (all 12 'has moved to').
State-persist via a 0-change apply is operator-permission-gated.

F2 (ansible): k3s_server secondary fetches the join token via
delegate_to the primary (published fact as fast path), so a single-node
rejoin with --limit k3s-dev-N is self-contained and no longer depends on
a fact an earlier play had to set. Syntax-check + CI gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 19:42:12 -05:00
jakman85andClaude Opus 4.8 f2a7e42068 Phase 1 (author-only): node provisioning IaC — tofu + cloud-init + Ansible (T-1.1/1.2/1.3)
OpenTofu libvirt module (k3s-dev NAT 10.73.0.0/24, 3x 6GiB/4vCPU VMs, 20G OS +
40G thin data, host-passthrough), cloud-init baseline (k3s/Cilium/Longhorn
prereqs, static net), Ansible k3s-HA + Cilium (kube-proxy replacement, Gateway
API, Hubble; flannel/kube-proxy/Traefik disabled). PM fix: escape ${...} in a
cloud-init template comment (tofu templatefile parse). tofu fmt/init/validate =
Success; ansible --syntax-check + yamllint pass. NOTHING executed (no apply).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 01:21:00 -05:00
jakman85andClaude Opus 4.8 5ba9238cc1 Scope Phase 1: dev/test cluster bring-up on local KVM
Add infra/PHASE1-SCOPE.md (grounded in local recon: 24c/22GiB-avail/137G,
AMD-V+libvirt ready; OpenTofu/age/sops/flux missing) + infra/ and bootstrap/
structure stubs. Maps T-1.1..T-1.5; resource/network plan; risks; acceptance
gates; operator decisions. No spec edits; gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-08-16 00:48:48 -05:00