bf099f6a10
P0-P7 probe ladder on real ends (demo-felhom PVE host <-> throwaway Hetzner). Verdict: GO at H1 — plain host-side WG, box-dials-out to a small public endpoint; Headscale is a separable fleet layer, not a traversal necessity. Line is plain-NAT (not CGNAT) with zero IPv6 — recorded honestly; CGNAT vantage deferred to Peti VM 110. Real 2 GiB worst-case PBS backup over the tunnel at the full home uplink, pin intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
73 lines
4.6 KiB
Markdown
73 lines
4.6 KiB
Markdown
# felhom.eu — task reports
|
||
|
||
> **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](scripts/CHANGELOG.md).
|
||
|
||
---
|
||
|
||
# REPORT — SPIKE: offsite-backup connectivity (WireGuard, home-NAT traversal) (2026-07-03)
|
||
|
||
Docs-only run; evidence doc: `documentation/audits/SPIKE-connectivity-wireguard-2026-07-03.md`.
|
||
No production code, no Headscale on the real hub, no agent change, no persistent felhom-pve config.
|
||
|
||
## Verdict
|
||
|
||
**GO at the lowest rung (H1): plain WireGuard, host-side, box-dials-out to a small public
|
||
endpoint.** No coordinator, no relay, no mesh. Headscale (P5: 0.29.2 stood up throwaway) is a
|
||
*separable fleet-management layer* — TLS control plane + DB + tailscaled-per-box + Tailscale-hosted
|
||
DERP by default — not a traversal necessity for hub-and-spoke backup.
|
||
|
||
## Probe ladder results (both ends real: demo-felhom PVE host ⟷ throwaway Hetzner CX23)
|
||
|
||
- **P0** — the operator's line is **plain single-NAT with a fixed public IPv4**
|
||
(`37.191.56.193`, catv.fixed.one.hu, AS21334), *not* CGNAT (no 100.64/10 hop; source IP at the
|
||
server = egress IP). **Zero IPv6** (no address, no egress, 15-min RA capture: 0 packets).
|
||
- **P1 (H0 v6-direct)** — N/A on this line; keep the production endpoint dual-stack anyway.
|
||
- **P2 (H1)** — plain WG v4 up on the **first try**; RTT ~30–40 ms; **11.4-min fully-idle hold**
|
||
(keepalive-only, NAT mapping unmoved, instant ping after); PBS 4.2.2 API through the tunnel with
|
||
the **exact-cert pin intact** (`pin.go` model) + negative wrong-pin proof; `:8007` tunnel-only
|
||
by firewall (public probe: unreachable).
|
||
- **P3** — uplink is the bottleneck: direct 42 Mbit/s up / 200–230 down; tunnel upload ~39 Mbit/s
|
||
(~5% overhead); PBS TLS benchmark through tunnel **4.94 MB/s = the full uplink**; **real 2 GiB
|
||
worst-case (urandom) backup: 4.26 MiB/s sustained, 8 min, zero stalls**. First backup 50–100 GB
|
||
≈ 3–6 h (overnight window). Oddity recorded: tunnel *download* (565 Mbit/s, 0 retrans) beat
|
||
direct TCP (230, 285 retrans) — UDP encapsulation dodges whatever shapes inbound TCP.
|
||
- **P4 (H2)** — outbound UDP fine on **51820 and 443**; no TCP/443 fallback machinery needed.
|
||
- **P6** — plain-WG isolation is topological: per-peer `/32` AllowedIPs + forwarding off at the
|
||
endpoint; only the PBS port admitted from the WG interface.
|
||
- **P7** — hypothesis **refuted with evidence**: kernel WG link creation **succeeds** inside the
|
||
unprivileged guest (no `/dev/net/tun` needed — that's confirmed absent and only blocks
|
||
tailscaled). Host placement therefore stands on architecture: backups originate host-side, the
|
||
tunnel must survive guest death (cloudflared precedent), the guest keeps zero infra creds.
|
||
|
||
## Architecture recommendation (input to the next task — the production spec)
|
||
|
||
Host-side agent-managed WG service (cloudflared pattern); per-box keypair minted at install →
|
||
pubkey to hub, private key 0600 in agent state dir + **joins the escrowed IdentityBundle** for DR;
|
||
endpoint = small public dual-stack VM (€4–8/mo class), two shapes deferred to the spec: PBS-on-VM
|
||
vs **VM-as-rendezvous-relay with DooPlex PBS dialing out too** (both ends outbound-only,
|
||
ciphertext-only transit — also solves DooPlex's no-public-UDP). Trust model preserved: box only
|
||
dials out; operator-inbound (B) and game-ingress (C) explicitly out of scope.
|
||
|
||
## Honest caveats
|
||
|
||
CGNAT traversal is argued from mechanism, **not proven on this line** (it's plain-NAT); second-ISP
|
||
confirmation deferred to Peti's VM 110 (currently unreachable). Single ISP/evening; no multi-day
|
||
hold; no home-IP-rotation event observed; Headscale assessed server-side only (no tailscaled on
|
||
the production host); no restore-direction PBS run.
|
||
|
||
## Cleanup (verified)
|
||
|
||
demo-felhom: interface/keys/config/dataset gone (keys **shredded**), iperf3 purged, WG module
|
||
unloaded, `/etc/wireguard` untouched-empty, guest probe link deleted — residue-free. Hetzner
|
||
throwaway: WG + headscale + PBS torn down, secrets shredded, datastore deleted, box **powered
|
||
off** (parked for the Peti follow-up or destruction; jarrs.eu box never touched).
|
||
|
||
## Repo changes (docs-only)
|
||
|
||
- NEW `documentation/audits/SPIKE-connectivity-wireguard-2026-07-03.md` (P0–P7 verbatim + twelve
|
||
§7 answers + recommendation).
|
||
- `documentation/runbooks/day0-install.md` — the "LAN-only until Headscale/WireGuard lands"
|
||
backlog line now points at the spike (decision made; spec is the remaining gate).
|
||
- `CONTEXT.md` — transport decision recorded; next task = production connectivity spec.
|
||
- `scripts/CHANGELOG.md` — docs entry.
|