Skip to content

REST API

The daemon exposes its full surface over a versioned REST API on :8080. The web UI and gwctl both consume this API — there’s no private back-channel.

Base URL: https://<gateway>:8080/api/v1

Authentication is one of:

  • JWT session cookie (set by POST /api/v1/auth/login).
  • API key in Authorization: Bearer <key> header. Keys are configured in the bootstrap auth.api_keys list with optional roles (admin / operator / viewer / diagnostics).
  • Local-network bypass for the CIDR in auth.local_net — clients within it get admin for read-only methods (GET/HEAD/OPTIONS). State-changing verbs still require a credential, so a LAN client can’t mutate state without one. When auth.enabled: false (the install default) the whole middleware is bypassed and every request is admin.

For mutual TLS, terminate at a reverse proxy in front of the daemon and require client certs there.

Rate limiting applies per token / per IP.

All endpoints return JSON. Success:

{ "data": { ... } }

Error:

{ "error": { "code": "policy_conflict", "message": "...", "details": { ... } } }
Method Path Notes
GET /auth/status Whether auth is enabled and configured
POST /auth/setup First-run admin creation
POST /auth/login Username + password → JWT cookie
POST /auth/change-password Rotate the admin password
GET /auth/tokens List active JWT sessions
GET /auth/keys List API keys
POST /auth/keys Create API key (role-scoped)
DELETE /auth/keys/{id} Revoke

Logout is client-side — drop the JWT cookie; there’s no /auth/logout endpoint.

Method Path Notes
GET /health Unauthenticated: {"status":"ok"}
GET /status Version, ready, uptime
GET /observer Subsystem health snapshot
GET /drift Recent drift events
GET /platform Hardware, resources
Method Path
GET/POST /devices
GET/PUT/DELETE /devices/{id}
GET/POST /device-groups
GET/POST /trust-classes
Method Path Notes
GET/POST /tunnels List / create
GET/PUT/DELETE /tunnels/{id} Detail / update / delete
POST /tunnels/{id}/up Bring the tunnel up (alias: /connect)
POST /tunnels/{id}/down Bring it down (alias: /disconnect)
GET /tunnels/{id}/config Render the WireGuard / OpenVPN config
POST /tunnels/generate-keys Make a fresh WireGuard keypair
POST /tunnels/import-wireguard Import a .conf blob
POST /tunnels/upload-ovpn Import an .ovpn blob
GET /tunnels/egress Show which tunnel each policy uses
GET/POST /bridges List / register
GET /bridges/status Health snapshot
GET/PUT/DELETE /bridges/{id} Detail / update / delete
POST /bridges/{id}/toggle Enable / disable
POST /bridges/{id}/test One-shot health probe
GET/POST/PUT/DELETE /proxies SOCKS5 proxy CRUD
Method Path Notes
GET /dns/status Resolver health, upstream RTTs
GET /dns/cache Cache entries
POST /dns/cache/flush Flush (optional pattern filter)
GET /dns/log Recent query log
GET /dns/clients Per-client query counters
GET/POST/PUT/DELETE /dns/filter-rules Filter rule CRUD
GET/POST/PUT/DELETE /dns/zones Custom zones
GET/POST/PUT/DELETE /dns/records Per-zone records
GET/POST/DELETE /dns/rewrites NXDOMAIN / A-rewrite rules
GET/POST/DELETE /dns/allowlist Per-source allow entries
POST/DELETE /dns/blocklist Per-source block entries
GET/POST/PUT/DELETE /dns/forwarders Upstream-resolver pool
GET/POST/PUT/DELETE /dns/presets Curated preset blocklists
GET/PUT /dns/rotation Upstream rotation mode
GET /dns/threat-feeds Threat-feed status
GET /dns/pihole/status Pi-hole backend status (if configured)

Routing policies live under /routing/. Profiles, exceptions and privacy classes are top-level resources.

Method Path Notes
GET/POST /routing/policies List or create a routing policy
PUT/DELETE /routing/policies/{id} Update or delete
POST /routing/policies/{id}/toggle Toggle a policy on/off
POST /routing/policies/apply Compile + push to the dataplane
GET/POST/PUT/DELETE /profiles Reusable route profiles (CRUD)
GET/POST/DELETE /exceptions Time-bounded overrides
GET /exceptions/active Only currently-active exceptions
GET/POST/PUT/DELETE /privacy-classes Privacy-class CRUD
POST /policy/evaluate Dry-run the evaluator for a device (body: {mac} or {device_id})
POST /policy/conflicts Detect conflicting policies
GET /policy/leak-matrix Posture × tunnel-state grid
GET /policy/fail-matrix Per-policy fail-policy summary
GET /render Compiled dataplane state
Method Path Notes
GET /privacy/status Posture snapshot
GET/PUT /privacy/dns-mode off / device / system
GET /privacy/leak-monitor Leak-monitor configuration
POST /privacy/force-dns Toggle / set per-device force-DNS
POST /privacy/block-doh Toggle DoH block
POST /privacy/block-ipv6 Toggle IPv6 block
POST /privacy/block-webrtc Toggle WebRTC STUN block
POST /privacy/leak-check One-shot leak probe
Method Path Notes
GET /splitting/status Overall engine state
GET/POST/DELETE /splitting/endpoints Endpoint pool CRUD
GET /splitting/cover/stats Cover-traffic counters
GET /splitting/dns/status DNS-split status
GET /splitting/transport Active transport details
POST /splitting/activate Activate splitting for a policy
Method Path Notes
GET /mesh/status Coordinator reachability + local state
GET /mesh/config Current mesh configuration
GET/DELETE /mesh/peers / /mesh/peers/{id} Peer list and removal
POST /mesh/up, /mesh/down Bring the local node up / down
POST /mesh/create-key Mint a pre-auth key (Headscale)
POST /mesh/register Register this node with the coordinator
GET /mesh/derp-mode DERP-relay mode
Method Path Notes
GET /federation/status Federation health
GET /federation/peers List known federation peers (read-only)
GET/POST/DELETE /nodes Peer / node lifecycle (create, list, remove)
POST /nodes/exchange Exchange signing keys with a new peer
POST /nodes/{id}/approve Approve a pending peer
GET/POST/PUT/DELETE /federation-roles Per-kind role table (note: hyphenated, top-level)
POST /federation/sync Force a pull from primary
GET /federation/manifest Authoritative manifest the primary advertises
GET /federation/inventory Per-peer inventory snapshot
GET /federation/recent-events Recent federation event log
Method Path Notes
GET /alerts All alerts (active + resolved)
GET /alerts/active Only active
POST /alerts/{id}/acknowledge Mark acknowledged
POST /alerts/{id}/resolve Mark resolved
GET /alerts/rules List alert rules
PUT /alerts/rules/{id} Update a rule
GET/PUT /alerts/webhook Default webhook config
POST /alerts/webhook/test Send a synthetic test webhook
GET /anomalies Anomaly detector output
GET /anomalies/stats Anomaly counters
GET /audit Audit log
Method Path Notes
GET /backup/status Last run, schedule, target health
POST /backup/export Encrypted export to response body
POST /backup/export-encrypted Same, returns binary blob
POST /backup/import Decrypt + apply (body = ciphertext)
POST /backup/run Trigger a scheduled run on demand
PUT /backup/config Update schedule / remote target
GET /snapshots List local snapshots
POST /rollback Apply a snapshot (body: {snapshot})
Method Path
GET/PUT /settings
GET/PUT/DELETE /settings/{key}
Method Path Notes
GET /config/export Plaintext JSON — secrets redacted via IsSensitiveKVKey filter
POST /config/export-encrypted Same as above plus secrets; encrypted, body {passphrase}
POST /config/import Apply a plaintext config export

See Backup → Config export vs. full backup for the differences against /backup/export.

Method Path Notes
GET /references/{type}/{id} “What depends on this object?” Used by the UI before destructive deletes.

Supported {type} values: profile, privacy_class (or privacy-class), trust_class (or trust-class), tunnel, proxy. Response shape: {type, id, referenced_by: {kind: count, ...}, total}. Counts cover the devices, device_groups, and routing_policies tables depending on type.

Method Path Notes
GET /diagnostics/captures List active + recent captures
GET /diagnostics/capture/presets 14 named BPF filters
POST /diagnostics/capture Start a new capture
GET /diagnostics/capture/{id} Parsed packet summaries
GET /diagnostics/capture/{id}/stream NDJSON live packet stream
DELETE /diagnostics/capture/{id} Stop a running capture
POST /captive/check Run the captive-portal detector once
GET /bluetooth/ids Bluetooth IDS event ring + learning state

Capture limits: max 3 concurrent, max 20 retained sessions, max 50 000 packets, max 3600 s. See Troubleshooting → Capture targeted traffic for end-to-end examples.

Method Path Notes
GET/PUT /system/deadman Dead-man’s switch config (enabled, hours)
POST /system/wipe Manual wipe — {level: "soft"|"medium"|"hard"}
GET /storage/encrypted LUKS vault status
POST /storage/encrypted/setup Create the LUKS container
POST /storage/encrypted/teardown Destroy it (after umount)
GET/PUT /pq-crypto Post-quantum TLS posture
GET /secrets/encryption-status At-rest encryption posture
POST /secrets/set-passphrase Rotate the secrets passphrase
POST /secrets/clear-passphrase Decrypt + remove the passphrase
GET /tproxy/status Transparent-proxy chain state

The wipe / deadman / encrypted-storage triplet is documented end-to-end on the threat model page.

Method Path Notes
GET /bluetooth/status Adapter + PAN state
POST /bluetooth/enable / /disable Toggle the radio
GET /bluetooth/devices Paired devices
DELETE /bluetooth/devices/{mac} Remove a paired device
POST /bluetooth/scan Active discovery (returns nearby set)
GET /bluetooth/nearby Last-known scan results
POST /bluetooth/pair Pair with a discovered MAC
POST /bluetooth/pan/connect / /disconnect Bring PAN up / down
POST /bluetooth/nap/start / /nap/stop Run the host as a Network Access Point
GET/PUT /bluetooth/settings Discoverability, name, defaults
PUT /bluetooth/name Set adapter name
GET /bluetooth/rssi/{mac} Per-device RSSI history
GET/PUT /bluetooth/failover WAN-failover config (uses paired phone)
GET /bluetooth/ids IDS events (also listed under Diagnostics)
Method Path Notes
GET /ai/status Subsystem reachable? Model loaded?
GET/PUT /ai/config Endpoint, model selection
POST /ai/validate Validate a proposed policy
POST /ai/policy Apply policy-specific assistant prompt
POST /ai/service Start / stop the local Ollama service
GET /ai/models/running Currently loaded models
POST /ai/models/load / /unload / /pull Model lifecycle
DELETE /ai/models/{name} Remove a downloaded model from disk

Create a policy that routes a single device through a tunnel:

Terminal window
curl -X POST https://gateway.lan:8080/api/v1/routing/policies \
-H "Authorization: Bearer $GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "alice-via-eu",
"priority": 100,
"enabled": true,
"source_type": "device",
"source_value": "laptop-alice",
"hops": [{"type":"vpn","target":"wg-eu-01"}],
"fail_policy": "fail-closed"
}'

A policy is one source_type + source_value pair plus an ordered hops array. source_type{all, interface, vlan, subnet, device, device_group, destination, domain}. Hop types: vpn, interface, proxy, mesh_peer. An empty hops array with fail_policy: fail-closed blocks the matched traffic.

Then push to the dataplane:

Terminal window
curl -X POST https://gateway.lan:8080/api/v1/routing/policies/apply \
-H "Authorization: Bearer $GATEWAY_API_KEY"
  • gwctl CLI — the same API, easier to script interactively.
  • Concepts — the vocabulary the payloads above use.
  • Use cases — concrete deployment shapes built on this API.