Server API Reference
Server API Reference
Section titled “Server API Reference”The Bifrost server exposes a REST API for management, monitoring, and configuration.
Default Address: http://localhost:7082
Health & Status
Section titled “Health & Status”Health Check
Section titled “Health Check”Check if the server is healthy and operational.
GET /api/v1/healthResponse:
{ "status": "healthy", "time": "2024-01-15T10:00:00Z"}| Field | Type | Description |
|---|---|---|
status |
string | healthy or degraded |
time |
string | ISO 8601 timestamp |
Version
Section titled “Version”Get server version information.
GET /api/v1/versionResponse:
{ "version": "1.0.0", "git_commit": "abc123", "build_time": "2024-01-15T10:00:00Z", "go_version": "go1.22.0", "platform": "linux/amd64"}Status
Section titled “Status”Get server running status.
GET /api/v1/statusResponse:
{ "status": "running", "time": "2024-01-15T10:00:00Z", "version": "1.0.0", "backends": 3}Statistics
Section titled “Statistics”Get aggregated server statistics.
GET /api/v1/statsResponse:
{ "total_connections": 5000, "active_connections": 25, "bytes_sent": 104857600, "bytes_received": 209715200, "backends": { "total": 3, "healthy": 3 }, "time": "2024-01-15T10:00:00Z"}Backend Management
Section titled “Backend Management”List Backends
Section titled “List Backends”Get all configured backends with their status and statistics.
GET /api/v1/backendsResponse:
[ { "name": "direct", "type": "direct", "healthy": true, "stats": { "active_connections": 5, "total_connections": 100, "bytes_sent": 1024000, "bytes_received": 2048000 } }, { "name": "wireguard-us", "type": "wireguard", "healthy": true, "stats": { "active_connections": 10, "total_connections": 500, "bytes_sent": 5120000, "bytes_received": 10240000 } }]Get Backend
Section titled “Get Backend”Get a specific backend by name.
GET /api/v1/backends/{name}Path Parameters:
| Parameter | Type | Description |
|---|---|---|
name |
string | Backend name |
Response:
{ "name": "wireguard-us", "type": "wireguard", "healthy": true, "stats": { "active_connections": 10, "total_connections": 500, "bytes_sent": 5120000, "bytes_received": 10240000 }}Add Backend
Section titled “Add Backend”Add a new backend to the proxy server.
POST /api/v1/backendsRequest Body:
{ "name": "my-proxy", "type": "httpproxy", "enabled": true, "httpproxy": { "address": "proxy.example.com:8080", "username": "user", "password": "pass" }}Supported Backend Types:
direct- Direct internet connectionwireguard- WireGuard VPN tunnelopenvpn- OpenVPN tunnelhttpproxy- HTTP/HTTPS proxysocks5- SOCKS5 proxy
Response (201 Created):
{ "status": "created", "backend": "my-proxy", "type": "httpproxy"}Remove Backend
Section titled “Remove Backend”Remove a backend from the proxy server.
DELETE /api/v1/backends/{name}Response:
{ "status": "removed", "backend": "my-proxy"}Get Backend Stats
Section titled “Get Backend Stats”Get detailed statistics for a specific backend.
GET /api/v1/backends/{name}/statsResponse:
{ "active_connections": 10, "total_connections": 500, "bytes_sent": 5120000, "bytes_received": 10240000, "errors": 0, "latency_ms": 45}Test Backend
Section titled “Test Backend”Test connectivity through a specific backend.
POST /api/v1/backends/{name}/testRequest Body (optional):
{ "target": "google.com:443", "timeout": "10s"}Response:
{ "status": "success", "backend": "wireguard-us", "target": "google.com:443", "duration": "45ms", "healthy": true}Configuration
Section titled “Configuration”Get Config (Sanitized)
Section titled “Get Config (Sanitized)”Get the current configuration with sensitive values redacted.
GET /api/v1/configResponse: Sanitized ServerConfig object with passwords, keys, and tokens masked.
Get Full Config
Section titled “Get Full Config”Get the complete configuration for editing (use with caution).
GET /api/v1/config/fullResponse: Full ServerConfig object including sensitive values.
Get Config Metadata
Section titled “Get Config Metadata”Get metadata about configuration sections, including hot-reload capabilities.
GET /api/v1/config/metaResponse: a JSON array with one entry per top-level config section.
[ { "section": "routes", "hot_reloadable": true, "description": "Routing rules" }, { "section": "rate_limit", "hot_reloadable": true, "description": "Rate limiting and bandwidth configuration" }, { "section": "server", "hot_reloadable": false, "description": "Server listeners and timeouts" }, { "section": "backends", "hot_reloadable": false, "description": "Backend connection configurations" }]Exactly four sections are hot-reloadable: routes, rate_limit (including
bandwidth), access_control and cache (rules and presets only — storage
changes still require a restart). Every other section reports
hot_reloadable: false.
This endpoint is derived from the same table the save path uses, so a client that renders “applies immediately” vs “needs a restart” from it cannot disagree with the save response. Prefer it over a hard-coded client-side list.
One caveat: cache reports hot_reloadable: true, but the reload only touches
the cache when a cache manager exists. Turning cache.enabled on from a server
that started with caching disabled still needs a restart.
Save Config
Section titled “Save Config”Save configuration changes.
PUT /api/v1/configRequest Body:
{ "config": { "server": { ... }, "backends": [ ... ], "routes": [ ... ] }, "create_backup": true}Response:
{ "success": true, "message": "Configuration saved successfully", "requires_restart": true, "changed_sections": ["server", "access_control"], "hot_reloaded_sections": ["access_control"], "restart_required_sections": ["server"]}| Field | Type | Description |
|---|---|---|
changed_sections |
string[] | Sections that differ from the running config |
hot_reloaded_sections |
string[] | Changed sections applied to the running server immediately |
restart_required_sections |
string[] | Changed sections written to disk that take effect after a restart |
requires_restart |
boolean | True exactly when restart_required_sections is non-empty |
backup_path |
string | Path of the backup, when create_backup was set |
errors |
object[] | {section, field, message} entries; a reload section means the save succeeded but applying it did not |
A save that touches both kinds of section still hot-applies the hot-reloadable
part — in the example above the new access_control blocklist is enforced
immediately even though the listener change needs a restart. If the reload
itself fails, hot_reloaded_sections is empty, every changed section is listed
under restart_required_sections, and an error with "section": "reload" is
returned; the configuration is still saved to disk.
Validate Config
Section titled “Validate Config”Validate configuration without saving.
POST /api/v1/config/validateRequest Body: ServerConfig object
Response:
{ "valid": true, "errors": [], "warnings": ["Backend 'old-vpn' is enabled but not configured"]}Validation covers the auth providers as well as the config shape. An enabled
provider whose type cannot authenticate in this build is reported invalid here
and refused by PUT /api/v1/config — so a configuration that would fail at the
next restart is never saved. Disabled providers are not checked, which keeps
“disable it and save” a way out of a bad configuration.
{ "valid": false, "message": "Configuration validation failed", "errors": [ { "section": "auth", "message": "provider \"corp-ntlm\": auth provider type \"ntlm\" cannot authenticate anyone and is refused: NTLM response verification is not implemented..." } ]}Reload Config
Section titled “Reload Config”Reload configuration from file (hot-reload for supported sections).
POST /api/v1/config/reloadResponse:
{ "message": "Config reloaded successfully", "time": "2024-01-15T10:00:00Z"}Authentication Plugins
Section titled “Authentication Plugins”List Auth Plugins
Section titled “List Auth Plugins”Report every auth plugin registered in this server binary, together with whether it can actually authenticate. The Web UI uses this to label providers instead of hard-coding the list, which cannot be correct for build-dependent plugins.
GET /api/v1/auth/pluginsResponse:
{ "plugins": [ { "name": "native", "type": "native", "description": "Built-in user database with bcrypt passwords", "availability": { "state": "available" } }, { "name": "system", "type": "system", "description": "System/PAM authentication (macOS via dscl; on Linux requires building with CGO_ENABLED=1 -tags pam...)", "availability": { "state": "build_disabled", "reason": "this binary was built without the PAM backend, so system authentication fails closed on Linux..." } }, { "name": "ntlm", "type": "ntlm", "description": "NTLM authentication (NON-FUNCTIONAL: fails closed...)", "availability": { "state": "unimplemented", "reason": "NTLM response verification is not implemented: there is no credential source..." } } ]}Availability states:
| State | Meaning | Configuration |
|---|---|---|
available |
Works normally | Accepted |
build_disabled |
This binary lacks the support the plugin needs (a build tag, cgo, a platform library), so it fails closed here but works in a build that includes it | Accepted, with a warning at startup and in the UI |
unimplemented |
Cannot authenticate anyone in any build | Refused at config validation |
negotiate never appears here: SPNEGO/Negotiate is HTTP middleware configured
under auth.negotiate.*, not a provider. A provider of type: negotiate is
refused with an error pointing at the correct section.
Routing Rules
Section titled “Routing Rules”List Routes
Section titled “List Routes”Get all configured routing rules.
GET /api/v1/routesResponse:
[ { "name": "internal", "domains": ["*.internal.company.com", "intranet.*"], "backend": "wireguard-corp", "enabled": true, "priority": 100 }, { "name": "streaming", "domains": ["*.netflix.com", "*.youtube.com"], "backends": ["direct", "wireguard-us"], "enabled": true, "priority": 50 }]Add Route
Section titled “Add Route”Add a new routing rule.
POST /api/v1/routesRequest Body:
{ "name": "social-media", "domains": ["*.twitter.com", "*.facebook.com", "*.instagram.com"], "backend": "direct", "enabled": true, "priority": 75}Response (201 Created):
{ "status": "created", "route": "social-media", "domains": ["*.twitter.com", "*.facebook.com", "*.instagram.com"], "backend": "direct"}Remove Route
Section titled “Remove Route”Remove a routing rule.
DELETE /api/v1/routes/{name}Response:
{ "status": "removed", "route": "social-media"}Request Log
Section titled “Request Log”Get Recent Requests
Section titled “Get Recent Requests”Get recent request log entries (requires enable_request_log: true in config).
GET /api/v1/requestsGET /api/v1/requests?limit=50GET /api/v1/requests?since=123GET /api/v1/requests?offset=100Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
int | 100 | Maximum entries to return |
since |
int | - | Get entries since this ID |
offset |
int | 0 | Skip this many entries |
Response:
{ "enabled": true, "requests": [ { "id": 1, "timestamp": "2024-01-15T10:00:00Z", "method": "GET", "host": "api.example.com", "path": "/v1/users", "url": "https://api.example.com/v1/users", "user_agent": "Mozilla/5.0...", "client_ip": "192.168.1.100", "username": "admin", "backend": "direct", "status_code": 200, "bytes_sent": 1024, "bytes_recv": 2048, "duration_ms": 150, "protocol": "HTTP" } ]}Get Request Stats
Section titled “Get Request Stats”Get request log statistics.
GET /api/v1/requests/statsResponse:
{ "enabled": true, "count": 1500, "max_size": 10000, "total_requests": 48213, "total_bytes_sent": 10485760, "total_bytes_recv": 83886080, "requests_by_method": { "GET": 1200, "POST": 300 }, "requests_by_status": { "200": 1400, "404": 100 }, "top_hosts": [ { "host": "api.example.com", "count": 900 }, { "host": "cdn.example.com", "count": 600 } ]}Two scopes are mixed in this response, because the request log is a fixed-size ring buffer:
total_requests,total_bytes_sentandtotal_bytes_recvare running totals since the server started or since the log was last cleared. They keep counting after entries are evicted from the buffer.count,requests_by_method,requests_by_statusandtop_hostsdescribe only the entries still retained in the buffer — at mostmax_sizeof them.
top_hosts is ordered by descending count and holds at most 10 hosts. Every
field is always present; the maps and the list are never null.
Clear Request Log
Section titled “Clear Request Log”Clear all request log entries. This also resets total_requests,
total_bytes_sent and total_bytes_recv.
DELETE /api/v1/requestsResponse:
{ "message": "Request log cleared"}Connection Tracking
Section titled “Connection Tracking”Get Active Connections
Section titled “Get Active Connections”Get all currently active connections.
GET /api/v1/connectionsResponse:
{ "connections": [ { "id": "20240115100000-1", "client_ip": "192.168.1.100", "client_port": "54321", "host": "api.example.com:443", "backend": "direct", "protocol": "CONNECT", "start_time": "2024-01-15T10:00:00Z", "bytes_sent": 1024, "bytes_recv": 2048 } ], "count": 1, "time": "2024-01-15T10:00:05Z"}Get Connected Clients
Section titled “Get Connected Clients”Get unique clients with aggregated statistics.
GET /api/v1/connections/clientsResponse:
{ "clients": [ { "client_ip": "192.168.1.100", "connections": 5, "bytes_sent": 10240, "bytes_recv": 20480, "first_seen": "2024-01-15T09:00:00Z" } ], "count": 1, "time": "2024-01-15T10:00:05Z"}Cache Management
Section titled “Cache Management”Get Cache Stats
Section titled “Get Cache Stats”Get cache statistics.
GET /api/v1/cache/statsResponse:
{ "enabled": true, "hit_rate": 0.85, "total_requests": 12450, "cache_hits": 10582, "cache_misses": 1868, "storage_type": "tiered", "rules_count": 5, "memory": { "entries": 4521, "size_bytes": 1073741824, "max_size_bytes": 2147483648 }, "disk": { "entries": 892, "size_bytes": 107374182400, "max_size_bytes": 536870912000 }}List Cache Entries
Section titled “List Cache Entries”Get cached entries with optional filtering.
GET /api/v1/cache/entriesGET /api/v1/cache/entries?domain=*.steamcontent.comGET /api/v1/cache/entries?limit=10&offset=0Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
domain |
string | - | Filter by domain pattern |
limit |
int | 100 | Maximum entries (max 1000) |
offset |
int | 0 | Skip entries for pagination |
Response:
{ "entries": [ { "key": "ab12cd34...", "url": "http://cdn.steamcontent.com/depot/123/chunk/abc", "host": "cdn.steamcontent.com", "size": 1048576, "content_type": "application/octet-stream", "created_at": "2024-01-15T10:30:00Z", "expires_at": "2025-01-15T10:30:00Z" } ], "total": 892, "offset": 0, "limit": 10}Get Cache Entry
Section titled “Get Cache Entry”Get metadata for a specific cache entry.
GET /api/v1/cache/entries/{key}Delete Cache Entry
Section titled “Delete Cache Entry”Delete a specific cache entry.
DELETE /api/v1/cache/entries/{key}Response:
{ "message": "Cache entry deleted", "key": "ab12cd34..."}Clear All Cache
Section titled “Clear All Cache”Clear the entire cache.
DELETE /api/v1/cache/entries?confirm=trueResponse:
{ "message": "Cache cleared", "time": "2024-01-15T10:00:00Z"}Purge Domain
Section titled “Purge Domain”Purge all cache entries for a domain.
DELETE /api/v1/cache/domain/{domain}Response:
{ "message": "Domain purged from cache", "domain": "cdn.steamcontent.com", "deleted": 150}List Cache Rules
Section titled “List Cache Rules”Get all caching rules.
GET /api/v1/cache/rulesResponse:
{ "rules": [ { "name": "steam", "domains": ["*.steamcontent.com", "content*.steampowered.com"], "enabled": true, "ttl": "8760h0m0s", "priority": 100, "preset": "steam" } ], "count": 5}Add Cache Rule
Section titled “Add Cache Rule”Add a custom caching rule.
POST /api/v1/cache/rulesRequest Body:
{ "name": "my-cdn", "domains": ["cdn.example.com", "assets.example.com"], "enabled": true, "ttl": "168h", "priority": 50}Response:
{ "message": "Cache rule created", "rule": "my-cdn"}Update Cache Rule
Section titled “Update Cache Rule”Update an existing cache rule.
PUT /api/v1/cache/rules/{name}Request Body:
{ "enabled": false}Delete Cache Rule
Section titled “Delete Cache Rule”Delete a cache rule.
DELETE /api/v1/cache/rules/{name}List Cache Presets
Section titled “List Cache Presets”Get available caching presets.
GET /api/v1/cache/presetsResponse:
{ "presets": [ { "name": "steam", "description": "Steam game downloads and updates", "domains": ["*.steamcontent.com", "content*.steampowered.com"], "ttl": "8760h0m0s", "priority": 100 }, { "name": "epic-games", "description": "Epic Games Store downloads", "domains": ["*.epicgames.com", "download.epicgames.com"], "ttl": "8760h0m0s", "priority": 100 } ], "count": 10}Enable/Disable Preset
Section titled “Enable/Disable Preset”POST /api/v1/cache/presets/{name}/enablePOST /api/v1/cache/presets/{name}/disableResponse:
{ "message": "Preset enabled", "preset": "steam"}Mesh Networking
Section titled “Mesh Networking”List Mesh Networks
Section titled “List Mesh Networks”Get all mesh networks.
GET /api/v1/mesh/networksResponse:
{ "networks": [ { "id": "net-abc123", "name": "Home Network", "cidr": "10.100.0.0/24", "peer_count": 5, "created": "2024-01-01T00:00:00Z" } ]}Create Mesh Network
Section titled “Create Mesh Network”Create a new mesh network.
POST /api/v1/mesh/networksRequest Body:
{ "id": "net-xyz789", "name": "Office Network", "cidr": "10.200.0.0/24"}Response:
{ "id": "net-xyz789", "name": "Office Network", "cidr": "10.200.0.0/24", "peer_count": 0, "created": "2024-01-15T10:00:00Z"}Get Mesh Network
Section titled “Get Mesh Network”Get details of a specific mesh network.
GET /api/v1/mesh/networks/{networkID}Delete Mesh Network
Section titled “Delete Mesh Network”Delete a mesh network.
DELETE /api/v1/mesh/networks/{networkID}Register Peer
Section titled “Register Peer”Register a peer to a mesh network.
POST /api/v1/mesh/networks/{networkID}/peersRequest Body:
{ "network_id": "net-abc123", "peer": { "id": "peer-xyz789", "name": "laptop-work", "public_key": "base64-encoded-key", "endpoints": [ { "address": "192.168.1.100", "port": 51820, "type": "lan", "priority": 100 } ] }}Response:
{ "success": true, "virtual_ip": "10.100.0.5", "peers": [ { "id": "peer-abc123", "name": "desktop-home", "virtual_ip": "10.100.0.2", "endpoints": [...] } ]}List Network Peers
Section titled “List Network Peers”Get all peers in a mesh network.
GET /api/v1/mesh/networks/{networkID}/peersGet Peer Details
Section titled “Get Peer Details”Get details of a specific peer.
GET /api/v1/mesh/networks/{networkID}/peers/{peerID}Update Peer
Section titled “Update Peer”Update peer information.
PATCH /api/v1/mesh/networks/{networkID}/peers/{peerID}Request Body:
{ "endpoints": [...], "metadata": {"location": "office"}}Deregister Peer
Section titled “Deregister Peer”Remove a peer from the mesh network.
DELETE /api/v1/mesh/networks/{networkID}/peers/{peerID}Peer Heartbeat
Section titled “Peer Heartbeat”Update peer heartbeat (keep-alive).
POST /api/v1/mesh/networks/{networkID}/peers/{peerID}/heartbeatPAC File
Section titled “PAC File”Bifrost automatically generates PAC (Proxy Auto-Configuration) files based on routing rules.
Get PAC File
Section titled “Get PAC File”GET /proxy.pacGET /wpad.datResponse (application/x-ns-proxy-autoconfig):
function FindProxyForURL(url, host) { if (shExpMatch(host, "*.internal.company.com")) { return "PROXY bifrost.example.com:7080; DIRECT"; } return "DIRECT";}Browser Configuration:
- macOS: System Preferences → Network → Advanced → Proxies → Automatic Proxy Configuration
- Windows: Settings → Network → Proxy → Use setup script
- Firefox: Settings → Network Settings → Automatic proxy configuration URL
- Chrome: Uses system settings or Proxy SwitchyOmega extension