HYB-1: harden bare-k3s single-VPS germination + Cloudflare-tunnel edge #51

Merged
plat merged 1 commit from feat/hyb-1-bare-k3s-tunnel-hetzner into main 2026-07-01 05:10:12 +00:00
Owner

Roadmap HYB-1 (plat/mitosis#25). Locked decisions: edge = Cloudflare Tunnel, VPS = Hetzner.

What changed

  1. bin/up bare-server two-step — the printed fallback recipe is now copy-paste-correct for a real k3s node: registries.yaml written to /etc/rancher/k3s/registries.yaml before k3s starts (the only time k3s wires containerd's mirror), the k3s install line with --disable=traefik --disable=servicelb --tls-san <domain> --write-kubeconfig-mode 644, and a default-deny (SSH-only) firewall note since the tunnel is outbound-only (no inbound 80/443). YAML renders flush-left so it pastes cleanly.
  2. bin/germinate success card — the reach-it card now forks on CUTOVER_TUNNEL. When set it prints tunnel/DNS guidance (point the tunnel ingress at http://traefik.kube-system.svc; DNS handled by the tunnel) and drops the self-signed-CA-trust step (a public issuer serves trusted certs), honestly noting DOM-1a (plat/gitops#3) is still open. The local (non-tunnel) /etc/hosts + CA-trust path is unchanged.
  3. Cutover coherence — the card's traefik.kube-system.svc target matches the existing ensure_kube_system_traefik_alias (bin/lib.sh) added under CUTOVER_TUNNEL=1 at germinate ~477. No in-cluster cloudflared deployment is added (dashboard-managed by design).
  4. docs/deploy/hetzner-vps.md — new client-followable runbook: Hetzner CX server + cloud-init (SSH-only firewall, two-step k3s), Cloudflare tunnel to traefik.kube-system.svc, DOMAIN=<real> CUTOVER_TUNNEL=1 CLAUDE_CODE_OAUTH_TOKEN=<tok> nix run .#germinate, reach the platform. Honest about manual steps and the DOM-1a public-TLS dependency.

Validation

Validated on the vxrail with a fresh cluster valid8d (CLUSTER=valid8d DOMAIN=valid8d.local PORTS=none CUTOVER_TUNNEL=1 nix run .#up) — see PR comment for the germination result and the tunnel-guidance card output.

Closes plat/mitosis#25.

🤖 Generated with Claude Code

Roadmap **HYB-1** (plat/mitosis#25). Locked decisions: edge = **Cloudflare Tunnel**, VPS = **Hetzner**. ## What changed 1. **`bin/up` bare-server two-step** — the printed fallback recipe is now copy-paste-correct for a real k3s node: `registries.yaml` written to `/etc/rancher/k3s/registries.yaml` **before** k3s starts (the only time k3s wires containerd's mirror), the k3s install line with `--disable=traefik --disable=servicelb --tls-san <domain> --write-kubeconfig-mode 644`, and a default-deny (SSH-only) firewall note since the tunnel is outbound-only (no inbound 80/443). YAML renders flush-left so it pastes cleanly. 2. **`bin/germinate` success card** — the reach-it card now forks on `CUTOVER_TUNNEL`. When set it prints **tunnel/DNS guidance** (point the tunnel ingress at `http://traefik.kube-system.svc`; DNS handled by the tunnel) and drops the self-signed-CA-trust step (a public issuer serves trusted certs), honestly noting DOM-1a (plat/gitops#3) is still open. The local (non-tunnel) `/etc/hosts` + CA-trust path is unchanged. 3. **Cutover coherence** — the card's `traefik.kube-system.svc` target matches the existing `ensure_kube_system_traefik_alias` (bin/lib.sh) added under `CUTOVER_TUNNEL=1` at germinate ~477. No in-cluster cloudflared deployment is added (dashboard-managed by design). 4. **`docs/deploy/hetzner-vps.md`** — new client-followable runbook: Hetzner CX server + cloud-init (SSH-only firewall, two-step k3s), Cloudflare tunnel to `traefik.kube-system.svc`, `DOMAIN=<real> CUTOVER_TUNNEL=1 CLAUDE_CODE_OAUTH_TOKEN=<tok> nix run .#germinate`, reach the platform. Honest about manual steps and the DOM-1a public-TLS dependency. ## Validation Validated on the vxrail with a fresh cluster `valid8d` (`CLUSTER=valid8d DOMAIN=valid8d.local PORTS=none CUTOVER_TUNNEL=1 nix run .#up`) — see PR comment for the germination result and the tunnel-guidance card output. Closes plat/mitosis#25. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Roadmap HYB-1 (plat/mitosis#25). Edge = Cloudflare Tunnel; VPS = Hetzner.

- bin/up: rewrite the bare-server fallback recipe to be copy-paste-correct for
  a real k3s node — registries.yaml written to /etc/rancher/k3s BEFORE k3s
  starts, k3s install with --disable=traefik --disable=servicelb --tls-san
  <domain> --write-kubeconfig-mode 644, and a default-deny (SSH-only) firewall
  note (the tunnel is outbound-only, so no inbound 80/443).
- bin/germinate: fork the YOUR PLATFORM reach-it card on CUTOVER_TUNNEL. When
  set, print tunnel/DNS guidance (point the tunnel at traefik.kube-system.svc;
  DNS handled by the tunnel) and drop the self-signed-CA-trust step (a public
  issuer serves trusted certs), honestly noting DOM-1a (plat/gitops#3) is still
  open. Local (non-tunnel) path unchanged.
- docs/deploy/hetzner-vps.md: client-followable runbook — Hetzner CX server +
  cloud-init (SSH-only firewall, two-step k3s), Cloudflare tunnel to
  traefik.kube-system.svc, germinate with CUTOVER_TUNNEL=1, reach the platform.
  Honest about what is manual and about the DOM-1a TLS dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
plat force-pushed feat/hyb-1-bare-k3s-tunnel-hetzner from 6bdebf50c9 to 270136ac4b 2026-07-01 04:53:25 +00:00 Compare
Author
Owner

Validated on the vxrail — fresh k3d cluster valid8d (CLUSTER=valid8d DOMAIN=valid8d.local PORTS=none CUTOVER_TUNNEL=1 nix run .#up), platgold2 untouched.

  • Germination healthy: mcp + agents dispatcher are Available; ci-builder + mcp 0.23.15 + agents 0.7.3 all built on attempt 1; up complete.

  • Card prints TUNNEL guidance under CUTOVER_TUNNEL=1 (not /etc/hosts):

    Reach it — this platform is fronted by a Cloudflare tunnel (public edge): Point the tunnel's ingress at the in-cluster edge: http://traefik.kube-system.svc … DNS is handled by the tunnel — no /etc/hosts entries needed.

    The 127.0.0.1 git.<domain> /etc/hosts line count in the log is 0.

  • Cutover alias coherent: germinate added kube-system/traefik ExternalName -> traefik.traefik.svc.cluster.local, exactly the target the card tells the tunnel to hit.

  • bash -n clean on both files; shellcheck bin/up clean; shellcheck bin/germinate shows only pre-existing info/warnings on untouched lines (203/234/362) — none introduced here.

Rebased onto latest main (includes SEC-1 #52): the FORK_KEY_ACK custody gate + backup-note card lines coexist with the CUTOVER_TUNNEL branch (both preserved). Note: HYB-2 (real public tunnel + Hetzner box) is out of scope here.

**Validated on the vxrail** — fresh k3d cluster `valid8d` (`CLUSTER=valid8d DOMAIN=valid8d.local PORTS=none CUTOVER_TUNNEL=1 nix run .#up`), platgold2 untouched. - **Germination healthy:** `mcp + agents dispatcher are Available`; ci-builder + mcp `0.23.15` + agents `0.7.3` all built on attempt 1; `up complete`. - **Card prints TUNNEL guidance** under `CUTOVER_TUNNEL=1` (not `/etc/hosts`): > Reach it — this platform is fronted by a Cloudflare tunnel (public edge): Point the tunnel's ingress at the in-cluster edge: `http://traefik.kube-system.svc` … DNS is handled by the tunnel — no /etc/hosts entries needed. The `127.0.0.1 git.<domain>` /etc/hosts line count in the log is **0**. - **Cutover alias coherent:** germinate added `kube-system/traefik ExternalName -> traefik.traefik.svc.cluster.local`, exactly the target the card tells the tunnel to hit. - `bash -n` clean on both files; `shellcheck bin/up` clean; `shellcheck bin/germinate` shows only pre-existing info/warnings on untouched lines (203/234/362) — none introduced here. Rebased onto latest `main` (includes SEC-1 #52): the FORK_KEY_ACK custody gate + backup-note card lines coexist with the CUTOVER_TUNNEL branch (both preserved). Note: HYB-2 (real public tunnel + Hetzner box) is out of scope here.
plat merged commit 6725c32c6e into main 2026-07-01 05:10:12 +00:00
Sign in to join this conversation.
No description provided.