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:
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:
curl -X POST https://gateway.lan:8080/api/v1/routing/policies \
-H " Authorization: Bearer $GATEWAY_API_KEY " \
-H " Content-Type: application/json " \
"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:
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.