# FUTURE / MAYBE — deferred ideas, revisit when the trigger is met

Not anti-goals (those live in [DO_NOT_DO.md](DO_NOT_DO.md)) and not committed roadmap (that's the `wN/mN` milestones). These are ideas that are **correct in principle but premature now** — each has a concrete **trigger** that should bring it back onto the board. When running `/pm-brainstorm` or `/pm`, don't schedule these unless their trigger is met; when a trigger _is_ met, promote the item to a real milestone/task.

## Deferred

- **Hetzner server-quota raise + full m19 target shape (3×CP · 3×platform · OpenBao 3/3).** The account's server limit (5) forced the w1/m19.1 interim: 1 CP + 2 platform + 1 tenant, self-managed, the bootstrap node destroyed (`bex-infra`, since renamed `bex-bootstrap`). Parked here by user decision 2026-07-11 — the interim shape serves prod. When picked up: request the limit (~10) in Hetzner Cloud Console → revert two TEMP numbers in [`infra/clusterapi/overlays/hetzner-caph/cluster.yaml`](../infra/clusterapi/overlays/hetzner-caph/cluster.yaml) (KCP `replicas: 1`→`3`, platform pool max `"2"`→`"3"` — both carry `TEMP (m19.1)` comments, grep for them) → push (CI applies; KCP webhook allows only odd counts) → machines self-materialize → `openbao-1` schedules and only needs an **unseal** (2026-07-11 update: the first prod init already happened via the 2 running pods — `BAO_ALLOW_INIT=1` + `BAO_ADDR` port-forward path; keys in `.env` + GH secrets via `gh-secrets.sh`; k8s auth + snapshot role configured; backup chain verified end-to-end; env-vars API live) → close out `w1/m19` t009's quorum/HA acceptance lines. **What the interim costs until then:** single etcd copy (machine loss = restore from nightly snapshot), no safe CP rolls (k8s upgrades blocked), OpenBao on a 2-member raft (both pods must be up for writes; third member joins at quota), one fewer platform-node failure domain. **Trigger: user decides to raise the quota, or any CP-touching change becomes necessary (k8s upgrade, cert config).**

- **Traefik LB survives cluster rebuilds (stable origin IP).** The 2026-07-10 rebuild changed the Traefik LB IP (LB deleted with the cluster; Hetzner LB IPs return to the pool — the old origin IP was re-assignable to strangers while DNS still pointed at it). Two candidate fixes: Terraform-owned LB that the CCM adopts by name (`load-balancer.hetzner.cloud/name` annotation) so rebuilds re-attach instead of re-create, or Cloudflare Tunnel (no public origin at all). **Trigger: the next planned rebuild/DR drill, or onboarding a domain we can't edit quickly.**

- **Node bring-up efficiency: baked snapshot image + trimmed preKubeadmCommands.** From the m19.1 `/simplify` review (2026-07-11): every node provision downloads runc+containerd+kubelet (~150MB, github.com/pkgs.k8s.io dependency in the autoscaler hot path), workers needlessly pre-pull the full CP image set (`kubeadm config images pull`, ~200MB), and the apt list carries unused packages (`at jq unzip mtr apt-transport-https`). Fixes: bake a Hetzner snapshot image (packer or one-off + `HCloudMachineTemplate.imageName`), drop the images-pull line from the two worker templates, trim apt. **Deliberately deferred**: any `KubeadmConfigTemplate` change rolls the worker pools, and today the three tenant Apps' images live only in the tenant node's containerd (`ctr` imports — no registry), so a roll knocks the apps into `ImagePullBackOff`. **Trigger: app images move into a pullable registry (zot wired end-to-end or GHCR), or the quota-raise revert already forces a roll — batch these in then.** (Minor sibling: cache the ~65MB clusterctl download in app-cluster.yml with actions/cache.)

- **Off-provider backup replica.** All backups (etcd snapshots, pg dumps, OpenBao snapshots) live in Hetzner Object Storage — same provider as the cluster; a provider-level failure or account lockout takes prod and its backups together. Cheap fix: nightly sync of the backup prefixes to a second S3-compatible provider (R2/B2, cents/month). Surfaced during the m19 rebuild's failure-domain review 2026-07-11. **Trigger: first paying tenant, or any compliance conversation.**

- **Pricing & spend estimation (price sheet on the tier catalog, $ estimates, invoices/payments).** w8 deliberately meters **quantities** (instance-seconds by tier, egress bytes, build minutes — Render's exact three meters) without dollars: self-hosted bex has no universal price sheet, and w6 already declared billing/payments out of scope ("bex has no billing system"; Render's flat-subscription + inactivity-waiver model is documented in `.pm/w6/RESEARCH-workspaces.md` finding 6). Deferred 2026-07-09 from `/pm-brainstorm w8`. **Trigger: a hosted bex offering becomes roadmap-worthy** — then price the tier catalog (`lego/types/tiers`), estimate spend over w8's `usage_hourly` rows, and start the subscription model from RESEARCH-workspaces.md rather than from scratch.

- **microVM runtime tier for tenant workloads (Kata / gVisor `RuntimeClass`).** The isolation ladder's next rung after w7's namespace tier ([DO_NOT_DO.md](DO_NOT_DO.md) §vcluster: "namespace (default) → microVM (Kata/Firecracker/gVisor, hard)"). Kernel-sharing is w7's accepted residual risk: NetworkPolicy + Pod Security baseline bound the blast radius, but a kernel exploit still crosses tenants on the same node. Deferred 2026-07-09 (w7 creation) because it's hard — node images must ship the runtime, CAPH node provisioning changes, and per-pod overhead fights bex's dense bin-pack economics — and the pre-GA threat model doesn't demand it. **Trigger: public GA / open signup with untrusted tenants** — revisit as an operator `RuntimeClass` knob per App or per plan tier.

## Done (trigger met, work landed)

- **Cilium WireGuard node-to-node encryption** — deferred 2026-07-09 as a structural no-op on one node; the trigger ("cluster gains a second node") was met by the w1/m19 rebuild, which shipped multi-node with `encryption.enabled=true / type=wireguard / nodeEncryption=true` live (pinned to the CAPH private interface, `devices=enp7s0`). See `.github/workflows/app-cluster.yml` + m19 t006 op log.
