# WireGuard Mesh Overview

Cross-project overview of how WireGuard connects the Hydra streaming infrastructure. This is the central entry point — individual project docs cover their specific areas.

## Topology

Hub-and-spoke WireGuard mesh with a single hub in Brussels (OVHcloud). All inter-site traffic routes through the hub.

```
                        ┌─────────────────────────────┐
                        │   Hub (141.227.136.12)       │
                        │   10.10.0.1/24               │
                        │   OVHcloud Brussels           │
                        │                              │
                        │   Also runs:                 │
                        │   - hydraneckwebrtc controller│
                        │   - hydraneckwebrtc worker   │
                        │   - coturn TURN server       │
                        └──────────┬───────────────────┘
                                   │
              ┌────────────────────┼────────────────────┐
              │                    │                     │
     ┌────────┴────────┐  ┌───────┴────────┐  ┌────────┴────────┐
     │  Venues          │  │  Air Units      │  │  Neck Air       │
     │  10.10.1-49.x    │  │  10.10.100.x    │  │  10.10.50-99.x  │
     │  + LAN subnet    │  │  Windows bodies  │  │  + LAN subnet   │
     │  (Omada/Gateway) │  │  (render nodes)  │  │  (Mikrotik)     │
     └──────────────────┘  └─────────────────┘  └─────────────────┘
```

## Address Scheme

| Type | WG tunnel range | LAN range | Capacity |
|------|----------------|-----------|----------|
| Hub | 10.10.0.1/24 | -- | 1 |
| Venues | 10.10.1-49.1/32 | 10.0.X.0/24 | 49 |
| Neck Air | 10.10.50-99.1/32 | 10.0.X.0/24 | 50 |
| Hydra Air | 10.10.100.1-254/32 | -- (no LAN) | 254 |

## How Each Project Touches WireGuard

### hydraguard (mesh manager)

Manages the hub-and-spoke mesh from a single `mesh.yaml` inventory.

| What | Where |
|------|-------|
| Repo | `github.com/cederikdotcom/hydraguard` |
| Runs on | Hub server (`141.227.136.12`) |
| Source of truth | `/root/.hydraguard/mesh.yaml` |
| Generated config | `/etc/wireguard/wg0.conf` |
| API | `http://hydraguard.experiencenet.com:8081` (keep stopped when not enrolling) |
| Binary | `/usr/local/bin/hydraguard` |
| Docs | [runbook.md](runbook.md), [DEPLOYMENT.md](../DEPLOYMENT.md), [OPERATIONS.md](../OPERATIONS.md) |

Key commands: `hydraguard status`, `hydraguard air add <id>`, `hydraguard apply`, `hydraguard export`

### hydracluster (node provisioning)

Provisions WireGuard on body machines via recipes. Calls hydraguard API to enroll peers.

| What | Where |
|------|-------|
| Repo | `github.com/cederikdotcom/hydracluster` |
| Runs on | `hydracluster.experiencenet.com` |
| Recipes | `recipes/hydraguard-air-windows.yaml`, `recipes/hydraguard-air-linux.yaml` |
| Config key | `hydraguard.url` + `hydraguard.token` in hydracluster config |
| Integration | `pkg/api/handlers_body.go` → `provisionHydraGuardAir()` calls `/api/v1/air/provision` |

When a node is assigned the `hydraguard-air` role:
1. Hydracluster calls hydraguard API to create the peer (gets WireGuard config back)
2. The recipe is sent to the hydranode agent on the body
3. Recipe installs WireGuard, writes config, starts the tunnel service

### hydraneckwebrtc (WebRTC relay)

Runs on the same box as the hub. Reaches body machines via WireGuard tunnel addresses (10.10.100.x) to pair with Sunshine on port 47990 and proxy WebRTC streams.

| What | Where |
|------|-------|
| Repo | `github.com/cederikdotcom/hydraneckwebrtc` |
| Runs on | Hub server (`141.227.136.12`) — same box as hydraguard |
| Connects to bodies via | WireGuard tunnel (10.10.100.x:47990 for Sunshine API) |
| TURN relay | coturn on same box, port 3478, forces `iceTransportPolicy: relay` |

The worker is on the hub's WireGuard interface (10.10.0.1), so it has direct mesh access to all peers without needing a separate WireGuard peer entry.

### hydranode (node agent)

Runs on every body machine. Executes the `hydraguard-air` recipe steps that install and configure WireGuard.

| What | Where |
|------|-------|
| Repo | `github.com/cederikdotcom/hydranode` |
| Runs on | Every body machine (Windows/Linux) |
| WireGuard config (Windows) | `C:\ProgramData\hydraguard-air\hydraguard-air.conf` |
| Tunnel service (Windows) | `WireGuardTunnel$hydraguard-air` |
| Recipe execution | `pkg/body/recipe.go` → `runPendingProvisions()` |

## Peer Lifecycle

```
1. Enroll body in hydracluster (web UI or API)
2. Assign hydraguard-air role
3. Hydracluster calls hydraguard API → peer added to mesh.yaml
4. Hydracluster sends recipe to hydranode agent
5. Hydranode installs WireGuard + writes config + starts tunnel
6. Peer handshakes with hub → body reachable at 10.10.100.x
7. hydraneckwebrtc can now create streaming sessions to the body
```

## Key Files

### On the hub (`ubuntu@141.227.136.12`)

| File | Purpose |
|------|---------|
| `/root/.hydraguard/mesh.yaml` | Peer inventory (source of truth) |
| `/root/.hydraguard/api.yaml` | API server config (token, listen, auto_apply) |
| `/etc/wireguard/hub.key` | Hub WireGuard private key |
| `/etc/wireguard/wg0.conf` | Generated WireGuard config (from mesh.yaml) |
| `/root/.hydraneckwebrtc/config.yaml` | Worker config (sessions, TURN, controller URL) |
| `/root/.hydraneckwebrtc-controller/config.yaml` | Controller config (domain, admin token) |
| `/root/.hydraneckwebrtc-turn-credential` | coturn TURN credential |

### On body machines (Windows)

| File | Purpose |
|------|---------|
| `C:\ProgramData\hydraguard-air\hydraguard-air.conf` | WireGuard tunnel config |
| `C:\hydranode\hydranode.exe` | Node agent binary |

### In hydracluster

| File | Purpose |
|------|---------|
| `recipes/hydraguard-air-windows.yaml` | Recipe: install WG, write config, start tunnel, configure Sunshine |
| `recipes/hydraguard-air-linux.yaml` | Same for Linux bodies |
| `recipes/hydraguard-gateway-linux.yaml` | Recipe for on-prem LAN gateways |

## Cross-Project Troubleshooting

### Body can't reach the hub

1. Check peer in mesh: `hydraguard status` on the hub
2. Verify keys match: body's public key must match what's in `mesh.yaml`
3. Check the body's tunnel: `hydracluster exec <nodeId> '& "C:\Program Files\WireGuard\wg.exe" show hydraguard-air'`
4. If endpoint shows old IP, restart the tunnel on the body:
   ```powershell
   & 'C:\Program Files\WireGuard\wireguard.exe' /uninstalltunnelservice hydraguard-air
   Start-Sleep 3
   & 'C:\Program Files\WireGuard\wireguard.exe' /installtunnelservice 'C:\ProgramData\hydraguard-air\hydraguard-air.conf'
   ```

### hydraneckwebrtc can't reach a body ("Sunshine unreachable")

1. Ping the body from the hub: `ping 10.10.100.x`
2. If ping works but port 47990 fails: Windows Firewall on the body is blocking. Check the `HydraGuard WG` firewall rule allows `remoteip=10.10.0.0/16`
3. If ping fails: body's WireGuard tunnel is down or key mismatch (see above)
4. Check body's Sunshine is running: `hydracluster exec <nodeId> 'Get-Service SunshineService | Format-Table Name,Status'`

### Peer keys got out of sync

This can happen when the hydraguard API regenerates keypairs (issue #64). Symptoms: handshake never completes, `transfer: 0 B received`.

Fix:
1. Get the body's actual public key: `hydracluster exec <nodeId> '& "C:\Program Files\WireGuard\wg.exe" show hydraguard-air'`
2. Update mesh.yaml on the hub to match
3. Regenerate wg0.conf: `hydraguard export > /etc/wireguard/wg0.conf`
4. Hot-reload: `wg set wg0 peer <correct-key> allowed-ips 10.10.100.x/32` (remove old key too)

### Recipe provisioning doesn't run on Linux nodes

The hydranode agent reports the full distro name (e.g. `Ubuntu 24.04`) but recipes use `os: linux`. Fixed in hydracluster v1.9.32 — ensure the hydracluster server is on v1.9.32+.
