WireGuard Overlay Transport
WireGuard Overlay Transport
Section titled “WireGuard Overlay Transport”The WireGuard overlay is an optional encrypted data-plane tunnel between a Tarsus agent and its assigned Thorax (the controller). When enabled, the agent’s command and result gRPC traffic to Thorax travels inside a WireGuard tunnel instead of over a plain TCP socket. mTLS is still applied inside the tunnel — WireGuard is an additional layer, never a replacement for application authentication.
The feature is off by default. With both switches off, Tarsus and Thorax behave exactly as they do today (direct mTLS over TCP).
Architecture
Section titled “Architecture”Direct (default): Tarsus ── gRPC/mTLS ── TCP ───────────────► Thorax
WireGuard overlay (opt-in): Tarsus ── gRPC/mTLS ── virtual TCP ── WireGuard ── UDP ──► Thorax └────────── encrypted overlay ─────┘Key properties:
- mTLS is always present. The overlay carries the same authenticated gRPC services
behind the same
AuthInterceptor; client certificates are validated inside the tunnel. - Mandible stays on the direct path. Only the agent → Thorax leg is tunnelled. Registration, heartbeat, and dispatch coordination with Mandible always use direct mTLS-TCP, so the overlay can never deadlock bootstrap.
- The controller is the hub. Thorax holds a persistent WireGuard identity (a keypair
plus an overlay IP, e.g.
10.99.0.1) and serves every agent assigned to it over one smoltcp stack and one UDP socket. Each agent gets its own per-peer tunnel. - Agents learn the controller identity during registration. When an agent registers with a WireGuard public key, Mandible allocates it an overlay IP from the tenant’s range and returns the controller’s public key, overlay IP, and UDP endpoint in the registration response. The agent then brings the tunnel up and routes its Thorax gRPC over the overlay IP.
Enabling the controller (Thorax)
Section titled “Enabling the controller (Thorax)”Set the WireGuard section in Thorax configuration (or the equivalent environment variables). The controller loads — or generates and persists — a keypair on first start, so its identity is stable across restarts.
| Environment variable | Default | Description |
|---|---|---|
THORAX__WIREGUARD__ENABLED | false | Master switch. When false, Thorax runs no overlay and advertises no controller identity. |
THORAX__WIREGUARD__OVERLAY_IP | 10.99.0.1 | The controller’s address on the overlay network. |
THORAX__WIREGUARD__OVERLAY_PREFIX_LEN | 16 | Overlay subnet prefix length. |
THORAX__WIREGUARD__UDP_BIND_ADDR | 0.0.0.0:51820 | host:port the WireGuard data-plane UDP socket binds to. |
THORAX__WIREGUARD__UDP_ADVERTISE_ADDR | (falls back to UDP_BIND_ADDR) | The host:port advertised to agents. Set this to the controller’s publicly reachable UDP endpoint when the bind address is 0.0.0.0 or behind NAT. |
THORAX__WIREGUARD__PRIVATE_KEY_PATH | /var/lib/mantis/thorax-wireguard.key | Where the controller’s keypair is persisted so identity survives restarts. |
The overlay gRPC listener reuses the controller’s direct gRPC port — it lives inside
the smoltcp stack, so there is no second OS port to open. Only the WireGuard UDP socket
(UDP_BIND_ADDR) needs to be reachable by agents.
Enabling the agent (Tarsus)
Section titled “Enabling the agent (Tarsus)”| Environment variable | Default | Description |
|---|---|---|
TARSUS__TRANSPORT__ENABLED | false | Enable the WireGuard overlay on the agent. |
TARSUS__TRANSPORT__PRIVATE_KEY_PATH | - | WireGuard private key file. If unset, a keypair is generated under data_dir and reused across restarts. |
When transport.enabled is on, the agent:
- Generates (or loads) its WireGuard keypair.
- Completes certificate enrollment and registration on the direct path first.
- Sends its WireGuard public key on the registration request and stores the controller identity returned in the response.
- Brings up the overlay and routes only its assigned Thorax gRPC over it. The Mandible endpoint and any other address stay on the direct path.
See [transport] in the Tarsus
configuration reference for the full agent-side options.
Fail-closed mode
Section titled “Fail-closed mode”By default, if the overlay cannot be established the agent falls back to the direct mTLS-TCP path. To forbid that fallback — requiring all agent → Thorax traffic to traverse the encrypted overlay — set the agent’s transport floor:
[security]minimum_transport_level = "wireguard"With this set:
TARSUS__TRANSPORT__ENABLED=falseis blocked (an attacker with environment access cannot downgrade transport security; a security-audit warning is logged).- If the controller identity is absent or the overlay collapses mid-session, the agent refuses to dial Thorax directly, returns a hard error, and rebuilds the overlay with backoff. The session never silently degrades to plaintext-transport.
This [security] section cannot be overridden by environment variables.
Overlay IP allocation
Section titled “Overlay IP allocation”Overlay IPs are allocated per tenant from the tenant’s WireGuard quota range
(wireguard_tenant_quotas.ip_range), falling back to the global overlay_range
(10.99.0.0/16) when a tenant has no custom range. The prefix length the agent receives
follows the effective range, so a tenant with a /24 range gets a /24 overlay
prefix.
Monitoring
Section titled “Monitoring”Lens. Navigate to Network → WireGuard Tunnels for a tenant-scoped (or, for admins, global) dashboard of agent overlay status: allocated overlay IP, NAT type, connection mode, handshake health, and key-rotation state.
Metrics. Mandible exports mantis_wireguard_overlay_allocations_total, incremented
each time an agent is issued an overlay IP plus controller identity. Thorax emits tracing
spans at overlay bring-up (success/failure); Tarsus logs fail-closed-denied events to the
security_audit tracing target and overlay-established events at INFO level to the
standard log.
Key rotation
Section titled “Key rotation”WireGuard static keys rotate over the live overlay without dropping the tunnel. Set
TARSUS__TRANSPORT__KEY_ROTATION_INTERVAL_SECS (default 0 = off) and the agent
periodically generates a fresh keypair and re-registers with it. Mandible keeps the
agent’s previous key valid as the pending key for a short overlap window, and the
controller serves both keys during the overlap (the old tunnel drains while the new
becomes active), so command/result traffic is never interrupted. The overlap clears
automatically once it expires, after which the controller stops accepting the old key.
Operators can also inspect/drive rotation through the agent key-rotation endpoints
(/api/v1/agents/{agent_id}/wireguard/key-rotation).
Current limitations
Section titled “Current limitations”The overlay ships with direct-UDP as the primary transport and an optional WebSocket relay fallback. The following are intentionally not yet part of the overlay data plane and are tracked as follow-up work:
- Agent ↔ agent mesh connectivity (only agent ↔ controller is supported).
- Registration with Mandible over the overlay (Mandible is always direct).
When agents cannot reach the controller’s UDP endpoint directly (e.g. behind symmetric
NAT or strict firewalls), configure THORAX__WIREGUARD__RELAY_URL to enable automatic
WebSocket relay fallback. Agents will prefer the direct UDP path and fall back to the
relay when direct connectivity is unavailable.
Reassignment
Section titled “Reassignment”If an agent’s controller (Thorax) goes offline, Mandible reassigns the agent’s target to a healthy Thorax. On its next heartbeat the agent is told to re-register (Mandible detects that the agent’s known controller differs from its current assignment); the agent re- registers, receives the new controller’s identity, and re-keys its WireGuard overlay to the new controller in place — without exiting the process.
