Mesh VPN
Phase С.1 of the GA roadmap: a built-in WireGuard mesh that replaces the legacy Pritunl-Docker dependency. Every node is both a server and a peer; Control Panel issues per-device keypairs and distributes peer lists. Phones, laptops, and other nodes all live as <device>.quazzar.mesh inside the fabric.
Adding a device
- Open VPN → Mesh (or browse to
/vpn/mesh). - Click Add device.
- Pick the device type (iPhone / Android / Laptop / Other), label it, click Generate.
- Scan the QR with the official WireGuard app (or download the
.conffor laptop). The status pill flips green within ~10 seconds when the peer first handshakes.
The device is now reachable as <label-slug>.quazzar.mesh from any other peer in the same tenant.
How it works
| Layer | Where |
|---|---|
Per-node WG interface (wg-quazzar0) | internal/vpn/wireguard.go + internal/vpn/wgctrl.go |
Per-tenant /16 + per-device /32 allocation | CP internal/mesh/allocator.go |
| Peer-list distribution | CP fans mesh.peers.updated over the existing CC-agent stream |
| Peer apply | OS-side internal/mesh/Service calls wgctrl.ApplyPeers (no wg-quick restart) |
| Mesh DNS | <device>.quazzar.mesh (resolution via the existing CoreDNS — plugin wiring is a follow-up; the IPs themselves are reachable today) |
Phone fallback
When you pair a phone via the existing pair-flow QR (Phase А.1, shipped 0.7.1), the phone receives a mesh config alongside the pair tunnel. The RelayClient keeps the relay path as primary, but if it degrades the phone falls back to https://<node>.quazzar.mesh:8443/... over WireGuard. Always reachable as long as one path is up.
Plans
| Tier | Mesh on / off | Device cap | Diagnostics |
|---|---|---|---|
| Free (Community) | ✅ | ≤ 3 | basic state |
| Pro | ✅ | ≤ 20 | + per-peer stats |
| Team | ✅ | ≤ 100 | + audit log of provisions |
| Enterprise | ✅ | unlimited | + SAML-gated provisioning |
License gate: mesh_vpn (always on; cap enforced at provision time). Surfaced in /api/license/orbit-features.
API
GET /api/mesh/state → { active, listen_port, peer_count, mesh_ip, dns_status }
GET /api/mesh/devices → owner-scoped list
POST /api/mesh/devices { label } → 201 { config, qr_svg } (rate-limited 5/min/user)
DELETE /api/mesh/devices/:id → 204 (owner or admin)
POST /api/mesh/refresh → 204 (admin) — force a SyncFromCP
GET /api/mesh/peers → admin diagnostics — live wgctrl peer dumpCP-side authority:
POST /cp/api/v1/mesh/devices — proxied through OS
DELETE /cp/api/v1/mesh/devices/:id
GET /cp/api/v1/mesh/peers/:node_id — node fetches its current peer list (CC-agent JWT)Migration from Pritunl
Existing Pritunl users are unaffected. The Pritunl manager keeps running side-by-side. The VPN page surfaces a recommendation banner: “Mesh VPN is the recommended replacement. Migrate when you’re ready →” — clicking walks you through revoking Pritunl + provisioning mesh devices for the same users. No automatic migration in this release; a follow-up cleanup release deprecates Pritunl after the canary fleet has stabilised.
Performance
- Kernel WireGuard: ≤ 50 MB throughput overhead on a 1 Gbit link.
- Userspace fallback (mobile + Mac): plenty for phone-scale traffic.
- Peer-list fan-out: bounded by the existing CC-agent stream; one fanout per peer change.