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
Section titled “WAN uplinks, balancing and failover (v2.15.0)”
Method Path Purpose
GET/PUT /system/wan-uplinks Read or replace uplinks, wan_failover_mode (manual / auto) and wan_balance. Per-uplink fields include weight and balance.
GET /system/wan-manager Active/standby decisions, reasons, flap pins, timing, egress legs and balanced state.
GET /system/wan-health Per-uplink internet-reachability state.

Start in manual mode before enabling automatic moves. Balancing defaults to off and requires wan_failover_mode: auto plus multiple healthy balancing-eligible uplinks. PUT returns validation warnings; it does not rewrite the current wan_iface. It replaces the uplink list: send the full intended list with explicit enabled and balance values. See WAN balancing and failover.

Accounts, DHCP and interface controls (v2.16.0)

Section titled “Accounts, DHCP and interface controls (v2.16.0)”

All paths below are relative to /api/v1. Mutations require an appropriate authenticated role; UI role gating does not replace API authorization.

Method Path Purpose
GET/POST /auth/users List / create managed user accounts; admin-only.
DELETE /auth/users/{id} Delete a managed user and revoke its tokens.
GET/POST /dhcp/reservations List / create a MAC-to-IP reservation.
DELETE /dhcp/reservations/{mac} Remove a reservation.
GET/PUT /interfaces/{name}/config Per-interface address, gateway and DNS configuration.
POST /interfaces/{name}/dhcp/{start,stop,renew} Control the interface DHCP client; LAN and WAN uplinks are refused.

Audit-log pagination uses limit and before, with X-Total-Count in the response. Status surfaces snapshot age; routing policies expose routing, services expose installed, and DHCP leases expose reserved. cpu_temp is nullable on hosts without a thermal sensor. See Settings for live versus restart-bound changes.

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.