Files
home-network-k3s/agents/task-board.md
T
jakman85andClaude Opus 4.8 95b4b6af21 Amend ADR-0013: VPN protocol WireGuard -> OpenVPN-UDP (Gluetun lacks PIA+WG)
Gluetun does not support Private Internet Access over WireGuard (upstream:
native support "cannot be added... slow work in progress"; confirmed live
rejecting the provider on v3.40/v3.41/latest). PIA in Gluetun is OpenVPN-only.

Amend ADR-0013 to PIA over OpenVPN-UDP. The egress ARCHITECTURE is unchanged
(single shared gateway, central kill-switch/fail-closed, native PF, Cilium
egress steering, active/standby) and R-NET-05/06/07 remain met; only the tunnel
protocol changes. OpenVPN-UDP satisfies "non-TCP", and Gluetun's HTTP control
API (health + forwarded-port lookup) is what the shared-gateway topology needs.
thrnz/docker-wireguard-pia retained as a documented WireGuard fallback.

Propagated the protocol correction to all active specs: ADR-0013 (+amendment
note), decisions/README, docs/00, docs/01, docs/03, docs/07, agents/task-board.
Superseded ADR-0008/0012 left immutable as historical record.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-22 09:02:31 -05:00

108 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Task Board
The live who's-doing-what. Agents **claim** an item (set Owner + Status) before starting, respect
`Blocked-by`, and mark `done` only after its **automated, reproducible behavior test passes green**
([`definition-of-done.md`](definition-of-done.md), [`../docs/14-testing-and-conformance.md`](../docs/14-testing-and-conformance.md)).
Seeded from [`../roadmap/phases.md`](../roadmap/phases.md) + [`../roadmap/workstreams.md`](../roadmap/workstreams.md).
> **This board is the one working surface agents may edit** (claim items, update status, add
> items/escalations). The specs, ADRs, and roadmap are **not** editable by agents (R-TEST-06) —
> raise spec problems here as an `OQ-*` escalation for the human. Every "**Prove …**" task below is
> an automated conformance test (co-owned with **WS-TEST**), not a manual demo.
**Status values:** `todo` · `in-progress` · `blocked` · `review` · `done`
**Add new items** at the bottom of the relevant phase; keep IDs stable.
---
## Phase 0 — Foundations
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-0.1 | Create implementation repo structure (platform/apps/catalog/clusters) | WS-GITOPS | R-GITOPS-02 | — | — | todo |
| T-0.2 | CI: lint + schema validation + build/dry-run gates | WS-GITOPS | R-GITOPS-01 | T-0.1 | — | todo |
| T-0.3 | Author ServiceDefinition schema + renderer contract | WS-GITOPS | R-GITOPS-02 | T-0.1 | — | todo |
| T-0.4 | Conformance harness: **Chainsaw** runner + reproducibility contract + JUnit + coverage report + CI merge gate | WS-TEST | R-TEST-01/02/03/04 | T-0.1 | — | todo |
| T-0.5 | L0 **Kyverno** policy gate (default-deny present, no plaintext secrets, schema-valid ServiceDefinitions) | WS-TEST | R-TEST-01, R-NET-01, R-SEC-01 | T-0.4 | — | todo |
## Phase 1 — dev/test Cluster Bring-up
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-1.1 | OpenTofu libvirt module for 3 node VMs (+ Longhorn data disk) | WS-INFRA | R-ENV-01 | — | — | todo |
| T-1.2 | cloud-init baseline (users, SSH, sysctls, virt/Longhorn prereqs) | WS-INFRA | R-ENV-01 | T-1.1 | — | todo |
| T-1.3 | Ansible: k3s HA install, Cilium CNI, disable flannel/kube-proxy/Traefik | WS-INFRA | R-PLAT-01 | T-1.2 | — | todo |
| T-1.4 | Bootstrap Flux + SOPS age key | WS-GITOPS/WS-SEC | R-GITOPS-01, R-SEC-01 | T-1.3 | — | todo |
| T-1.5 | Prove node destroy/rebuild from IaC (no data loss) | WS-INFRA | R-ENV-01, R-STATE-01 | T-1.4 | — | todo |
## Phase 2 — Platform Core (net/storage/TLS/DNS/ingress)
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-2.1 | Longhorn (3-replica StorageClass) + `bulk` external-mount indirection (media lives off-cluster, backing TBD — OQ-2 resolved) | WS-STORAGE | R-STATE-02/04 | T-1.4 | — | todo |
| T-2.2 | Default-deny network baseline (per-namespace) + Hubble | WS-NET | R-NET-01/03 | T-1.3 | — | todo |
| T-2.3 | Render `dependencies` → CiliumNetworkPolicy from ServiceDefinition | WS-NET/WS-GITOPS | R-NET-02 | T-0.3, T-2.2 | — | todo |
| T-2.4p | **Prereq:** operator creates scoped Cloudflare API token (`Zone·DNS·Edit`, `keithjackson.dev`); store SOPS-encrypted | WS-SEC | R-TLS-01, R-SEC-01 | — | — | todo |
| T-2.4 | cert-manager + Let's Encrypt DNS-01 wildcard `*.home.keithjackson.dev` (via Cloudflare token) | WS-SEC | R-TLS-01/02/03 | T-1.4, T-2.4p | — | todo |
| T-2.5 | ExternalDNS (Cloudflare token) + Gateway API listeners (VIP via **Cilium L2 announcement**) + split-horizon (PiHole) | WS-NET | R-DNS-01/02/03 | T-2.4 | — | todo |
| T-2.6 | Prove: default-deny (DNS-only probe), PVC survives rebuild, trusted cert, auto-DNS | WS-NET/STORAGE/SEC | R-NET/STATE/TLS/DNS | T-2.1..2.5 | — | todo |
| T-2.7 | Deploy **Kyverno in-cluster as admission enforcer** (same policies as the CI L0 gate) | WS-TEST/WS-SEC | R-SEC-02, R-NET-01, R-SEC-01 | T-1.4, T-0.5 | — | todo |
## Phase 3 — Egress & VPN (single WAN; no multi-WAN — OQ-1)
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-3.0 | L3 fault-injection harness — **in-cluster Chainsaw Job** (CI-triggered, scoped test SA) using `script`/`try`/`finally` (force tunnel down, kill node, inject bad image, trigger restore) | WS-TEST | R-TEST-05, R-SEC-02 | T-0.4 | — | todo |
| T-3.1 | Egress classes; Cilium Egress Gateway wiring (steer `vpn` pods → gateway) | WS-NET | R-NET-04/05 | T-2.2 | — | todo |
| T-3.2p | **Prereq:** provide **PIA username/password**; store SOPS-encrypted (PIA purchased) | WS-SEC | R-NET-05 | — | — | todo |
| T-3.2 | **Single shared in-cluster PIA/Gluetun gateway** (OpenVPN-UDP, active/standby) + built-in kill-switch + native PF (`qbittorrent-natmap` port-sync) (ADR-0013) | WS-NET | R-NET-05/06 | T-3.1, T-3.2p | — | todo |
| ~~T-3.3~~ | ~~Edge-router multi-WAN split~~ — **out of scope** (single WAN, dumb SOHO edge; OQ-1) | WS-NET | R-NET-04 | — | — | won't-do |
| T-3.4 | Prove: VPN IP egress, **kill-switch fail-closed (central)**, egress-class flip via git | WS-NET | R-NET-05/06/07 | T-3.0, T-3.2 | — | todo |
## Phase 4 — Observability, Backup/DR & Updates
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-4.1 | kube-prometheus-stack + Loki + Alloy + Alertmanager(email) | WS-OBS | R-OBS-01/02/03 | T-2.1 | — | todo |
| T-4.2 | Platform-health alerts (node/Longhorn/Flux/cert/VPN/backup) | WS-OBS | R-OBS-04 | T-4.1 | — | todo |
| T-4.3 | Velero + Longhorn off-cluster backup schedule (**needs backup-target choice** — S3/NFS endpoint) | WS-STORAGE | R-STATE-03 | T-2.1, OQ-BKP | — | todo |
| T-4.4 | DR drill: restore namespace + volumes from off-cluster backup | WS-STORAGE | R-STATE-03 | T-4.3 | — | todo |
| T-4.5 | Flux image-automation + Renovate + per-service update policy | WS-UPDATE | R-UPD-01/02/03 | T-0.3, T-4.1 | — | todo |
| T-4.6 | Prove: auto stable update on schedule, bad-update auto-rollback+alert, pin holds | WS-UPDATE | R-UPD-* | T-4.5 | — | todo |
## Phase 5 — IaaS Proof (KubeVirt)
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-5.1 | Install KubeVirt + CDI; Longhorn-backed VM disks | WS-VM | R-PLAT-03 | T-2.1, T-2.2 | — | todo |
| T-5.2 | `kind: vm` ServiceDefinition path; run one representative VM | WS-VM/WS-GITOPS | R-PLAT-03/04 | T-5.1, T-0.3 | — | todo |
| T-5.3 | Prove: VM lifecycle via kubectl/virtctl, rebuild w/o data loss, default-deny+egress, guest auto-update | WS-VM | R-PLAT-03, R-NET-*, R-UPD-* | T-5.2 | — | todo |
## Phase 6 — Service Onboarding + C2
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-6.1 | Onboard **pihole** (DNS underpinning) | WS-APPS | R-DNS-03 | T-2.5 | — | todo |
| T-6.1a | Onboard **gitea** early (bootstrapping-critical; self-hosts GitOps source + CI/CD) | WS-APPS | R-STATE-03 | T-4.4 | — | todo |
| T-6.1b | Migrate GitOps source local→Gitea; move CI to **Gitea Actions** (Kyverno gate + Chainsaw Job trigger; PR merge gate); off-cluster repo mirror | WS-GITOPS | R-GITOPS-01, R-TEST-03 | T-6.1a | — | todo |
| T-6.1c | Onboard **Keycloak** (IdP) + PostgreSQL (Longhorn, DR-critical) + **oauth2-proxy** forward-auth at Gateway; declarative realm/clients/groups; MFA; break-glass path | WS-SEC | R-IDP-01..06 | T-2.5, T-2.6 | — | todo |
| T-6.1d | Render ServiceDefinition `auth` block → Keycloak OIDC client / oauth2-proxy protection | WS-SEC/WS-GITOPS | R-IDP-02/03 | T-0.3, T-6.1c | — | todo |
| T-6.2 | Onboard **plex** (bulk tier + public exposure) | WS-APPS | R-STATE-04 | T-2.6, T-6.1 | — | todo |
| T-6.3 | Onboard **qbittorrent, jackett** (VPN + kill-switch) | WS-APPS | R-NET-05/06 | T-3.4 | — | todo |
| T-6.4 | Onboard **sonarr, radarr** (VPN + arr↔downloader allows) | WS-APPS | R-NET-02/05 | T-6.3 | — | todo |
| T-6.5 | Onboard **seerr** (allows to arr + plex) | WS-APPS | R-NET-02 | T-6.2, T-6.4 | — | todo |
| T-6.6 | Onboard **nextcloud** (DR-critical + backups) | WS-APPS | R-STATE-03 | T-4.4 | — | todo |
| T-6.7 | Onboard **minecraft, unifi, semaphore, kasm** | WS-APPS | app reqs | T-2.6 | — | todo |
| T-6.8 | C2 UI: Homepage root + Grafana + Headlamp + Flux status + auth | WS-C2 | R-C2-01/02 | T-4.2, T-6.* | — | todo |
## Phase 7 — Production Readiness (CLOSED until operator opens it)
| ID | Task | WS | Serves | Blocked-by | Owner | Status |
|---|---|---|---|---|---|---|
| T-7.1 | Bare-metal provisioning checklist (design-only) | WS-INFRA | R-ENV-03 | — | — | todo |
| T-7.2 | prod overlay (no base edits) + cutover plan | WS-INFRA/WS-GITOPS | R-ENV-02 | Phases 1–6 done | — | blocked |
| T-7.3 | Full prod DR drill | WS-STORAGE | R-STATE-03 | T-7.2 | — | blocked |
---
## Open questions
| OQ | Question | Blocks | Status |
|---|---|---|---|
| OQ-1 | WAN count + edge router | — | ✅ resolved: 1 WAN, dumb SOHO edge; multi-WAN out of scope; VPN + segmentation in-cluster |
| OQ-2 | Bulk-media home | — | ✅ resolved: media outside cluster, backing TBD later (not blocking) |
| OQ-3 | VPN provider/protocol | — | ✅ resolved: **PIA** via Gluetun, single shared in-cluster tunnel, **OpenVPN-UDP** + native PF (ADR-0013 amended 2026-08-22; WireGuard not supported for PIA in Gluetun, supersedes 0012) |
| OQ-4 | DNS-01 challenge access | — | ✅ resolved: scoped **Cloudflare API token** (Zone·DNS·Edit), SOPS-encrypted; drives cert-manager + ExternalDNS. Operator provides token (T-2.4p) |
| **OQ-BKP** | **Off-cluster DR backup target** (S3/NFS endpoint) | T-4.3 | ⛔ **OPEN** — needed by Phase 4, not Phase 2 |