Isle VPN
What we built, what it gives you, and why we passed on Tailscale and Headscale. Written for a reader with a networking background; the networking and topology model is the context.
The short version
We run our own WireGuard and OpenVPN deployments under one app family and we keep the control plane on the isle itself. Tailscale is out because its coordination server is proprietary SaaS. Headscale is licence-clean but is a central coordination authority, which is the one thing this design refuses to have. Everything a managed overlay is liked for at the user level, except automatic NAT traversal, is covered by machinery we already control. NAT traversal is the gap, and it is a planned item rather than a surprise.
Where a VPN sits in the addressing
An app on an isle is exposed rung by rung, each rung a deliberate enablement, default most restrictive. The VPN is a rung of its own between the archipelago and the mesh.
Two consequences matter. The .vpn option exists on a device only when a gateway-kind VPN app is installed there, so exposure cannot outrun the tunnel. And the isle side is the configuration authority: the VPN app's config API binds to isle-local addresses and refuses anything arriving over the tunnel interface. Polari mirrors state and files proposals; an operator applies them on the isle with isle vpn apply. Nothing reachable through the VPN can change the VPN.
Two providers, ten kinds
Isle Link is WireGuard-based: Noise-framework key-based tunnels, kernel data plane, driven through wg and netlink and never embedded, so our GPLv3 tree stays clear of the GPLv2 tools. Isle Bridge is OpenVPN 2.x-based: x509 identity with a CA and revocation list, TCP/443 traversal, and layer-2 tap bridging, driven through the management interface. The rule of thumb: Link when both ends run ours, Bridge when joining a network or an OpenVPN-only box. Every row carries one of two labels so nobody mistakes a blind relay for a hub.
| Kind | What it does | Label | Placement |
|---|---|---|---|
| Link Node | The device gets its own /32 on a Link network; nothing behind it is shared | endpoint | container |
| Link Gateway | Carries the isle subnet; makes the .vpn rung available | endpoint | router extension |
| Link Relay | Forwards encrypted datagrams between peers that cannot reach each other; holds no keys | blind | container |
| Link Hub | Membership authority; routes between members; can carry other isles' subnets | sees traffic | KVM guest |
| Link Exit | A hub plus WAN masquerade; off until the knob is turned | sees traffic | KVM guest |
| Bridge Client | Joins a Bridge server with a certificate; works over TCP/443 | endpoint | container |
| Bridge Server | Certificate-issuing hub with live per-client control and revocation | sees traffic | KVM guest |
| Bridge Span | Stretches the isle VLAN across sites at layer 2 (a tap bridged into the isle bridge) | sees traffic | router extension |
| Bridge Exit | A Bridge server plus WAN masquerade; off until the knob is turned | sees traffic | KVM guest |
| Bridge Peer | An OpenVPN-only box joined as a gateway peer | endpoint | container |
Placement: one rule per body
- KVM guest for every kind that sees traffic and holds authority: hub, server, exit. Own failure domain, own bridge, WAN masquerade on its own interface, keys and CA on its own disk. Never inside the router, never a container sharing the host network namespace. Link guests run OpenWrt; Bridge guests run Debian with the CA built on first boot and the management interface bound to localhost.
- Router extension for every kind that carries the isle's own subnet or VLAN: gateway, span. The subnet, DHCP, DNS and firewall zones live on the OpenWrt router guest, so the tunnel that carries them lives there too. WireGuard in its own zone with the listen port opened on the WAN-facing zone only, or an OpenVPN tap bridged into the isle VLAN.
- Container for endpoints, clients, peers and the blind relay. No subnet, nobody else's keys, fine on rented hardware.
Private keys are generated on the device at apply time and never rendered or pushed. Hooks are templated toggles (forward, masquerade, route-add), never free-text scripts. Exits render with masquerade off.
The three levels and who serves them
| Level | Definition | VPN kinds used | Never |
|---|---|---|---|
| Isle | Own LAN, fully trusted. A VPN extends it to a device elsewhere or joins two sites as one isle. | All ten | — |
| Archipelago | Isles federated as one network while the measured path meets the floor: round trip ≤ 150 ms, loss ≤ 1 percent, ≥ 1000 kbps (placeholders). Membership is measured, not declared. | All ten; gateway-to-gateway links through a blind relay by default | Routing hubs on hardware you do not own |
| Mesh | Zero-trust state relays. Consumers receive state and send returns through the proposal seam; they are not peers. | Relay bodies, TCP/443 rendezvous, opt-in exits | Any membership authority |
Each level has its own topology display on the Polari side, pairing the level's definition and the kinds usable there with the live rows crossing it.
How this stands in for a managed overlay
What people like about a managed overlay is not the protocol; Tailscale runs WireGuard, same as Isle Link. The value is the coordination layer, and each of its functions maps onto machinery we already have:
| Function | Managed overlay | Isle VPN |
|---|---|---|
| Identity and join | Identity-provider login to the coordination server | A join request that a person on the isle approves; consent is a row both sides keep, revocable per child |
| Key exchange | Node keys registered with the coordinator | Keys made on the device; public keys ride the isle's push; proposals carry peers, never private material |
| Relay behind NAT | Encrypted HTTPS relays | Link Relay: a blind UDP forwarder with no keys, safe on a rented box |
| Names | Magic DNS | The gateway's DNS serves <app>.vpn to members; .isle never crosses |
| Access control | An ACL policy file at the coordinator | Access-rule rows rendered to nftables on the router |
| Subnet routes, exits | Advertised routes, an exit-node flag | The gateway carries the isle subnet; exit kinds masquerade only when the knob is on |
| Client onboarding | App login | The rendered config imported by the stock WireGuard app from a QR code; OpenVPN for Android (GPLv2) for Bridge |
| Observability | An admin console | wg show counters and the OpenVPN management interface mirrored as rows; per-level topology pages |
| Direct-path discovery | STUN, hole punching, relay fallback | Not built. Relay fallback exists; direct-path discovery is the open item below |
Why not Tailscale
The client and relay code are BSD-3-Clause and would be fine. The coordination server the client depends on, for login, key registration and ACL distribution, is proprietary software run as a service by Tailscale Inc. Polari is GPLv3 and a proprietary dependency is a hard blocker. Using the open client without that control plane means replacing it, which is the Headscale question.
Why not Headscale
Headscale is BSD-3-Clause and GPLv3-compatible; we could ship it. It is a central coordination server: one authority holding identity, keys and policy for the whole network. Two of our rulings run the other way. The isle is the authority for its own VPN and refuses configuration from over the tunnel, and federation between isles goes through blind relays by default, with routing hubs only on hardware the household owns. It would also be a third stack to drive next to WireGuard and OpenVPN, with an overlapping feature set. It stays on the books as a possible adapter for anyone who wants the tailnet model wholesale; it is not a provider.
The honest gap
A managed overlay finds a direct UDP path with STUN and hole punching and falls back to a relay only when it must. We have the relay fallback and we already record link measurements per destination. The missing piece is small: probe direct reachability, prefer it when it meets the floor, drop to the blind relay when it does not. It belongs in the VPN plan as its own phase and does not change the placement or authority model above.
Reference
pol vpn kinds the ten kinds and the rule of thumb
pol vpn placements where each kind runs and at which levels
pol vpn topology <level> isle | archipelago | mesh — definition, floor, kinds, live rows
pol vpn qr <peer> a rendered config as a QR code for the stock WireGuard app
isle vpn install <kind> on the isle: container, --in <guest>, or --on-router
isle vpn apply <proposal> apply what Polari proposed
Polari