Architecture¶
Claimward separates a control plane (the server) from the data plane (the WireGuard tunnel between a device and the gateway). Authentication is delegated to your OIDC identity provider; Claimward never sees the user's password.
End-to-end enrollment flow¶
┌──────────┐ 1. OIDC Authorization Code + PKCE (browser) ┌──────────┐
│ client │ ─────────────────────────────────────────────▶│ IdP │
│ (app/CLI)│◀──────────────── id_token ─────────────────────│ (OIDC) │
└────┬─────┘ └──────────┘
│ 2. POST /api/v1/enroll
│ Authorization: Bearer <id_token>
│ { public_key, device }
▼
┌─────────────────────────┐ 3. verify token (issuer discovery)
│ claimward-vpn-server │ allocate IP from pool
│ (on the gateway host) │ wgctrl: add peer (AllowedIPs = clientIP/32)
└────┬────────────────────┘
│ 4. { assigned_ip, server_public_key, endpoint, allowed_ips, dns }
▼
┌──────────┐ 5. wireguard-go creates utunN, configures peer
│ client │ ════════════════ WireGuard tunnel ════════════════▶ private network
└──────────┘
- The client signs in with the configured provider — GitHub by default (OAuth device flow) or any OIDC issuer (PKCE) — and receives a bearer credential (a GitHub access token or an OIDC ID token).
- It generates (once) a WireGuard key pair and calls
POST /api/v1/enroll, sending its public key with that bearer. - The server verifies the bearer (GitHub API call or OIDC verification),
applies any org/email allowlist, allocates a VPN address, and programs
the gateway (
wgctrl) with a peer whose only allowed IP is that/32. - The server returns the tunnel parameters (assigned IP, its own public key, the public endpoint, routes, DNS, keepalive).
- The client brings up a userspace tunnel with
wireguard-go.
Leases & heartbeats¶
Each enrollment carries a lease (LEASE_TTL, default 24h). Clients
POST /api/v1/heartbeat to renew. A background reaper on the server removes
peers whose lease expired, so lost or revoked devices fall off the gateway
automatically. POST /api/v1/deregister removes a peer immediately.
Trust boundaries¶
- The server is the only component that talks to the IdP for verification and the only one with rights to change the gateway's peer set.
- On the desktop, tunnel setup needs root, so it is isolated in a small privileged helper; the UI app itself is unprivileged.
- The wire contract lives in one place —
claimward-vpn-client/pkg/protocol— and is imported by both client and server so they cannot drift.