Skip to content

REST API Reference

Both server and client expose REST APIs for management and monitoring.

If API token is configured:

Terminal window
curl -H "Authorization: Bearer your-token" http://localhost:7082/api/v1/status

Or via query parameter:

Terminal window
curl "http://localhost:7082/api/v1/status?token=your-token"

Default: http://localhost:7082

GET /api/v1/health

Response:

{
"status": "healthy",
"time": "2024-01-01T00:00:00Z"
}
GET /api/v1/version

Response:

{
"version": "1.0.0",
"git_commit": "abc123",
"build_time": "2024-01-01T00:00:00Z",
"go_version": "go1.22.0",
"platform": "linux/amd64"
}
GET /api/v1/status

Response:

{
"status": "running",
"time": "2024-01-01T00:00:00Z",
"version": "1.0.0",
"backends": 3
}
GET /api/v1/stats

Response:

{
"total_connections": 5000,
"active_connections": 25,
"bytes_sent": 104857600,
"bytes_received": 209715200,
"backends": {
"total": 3,
"healthy": 3
},
"time": "2024-01-01T00:00:00Z"
}
GET /api/v1/backends

Response:

[
{
"name": "direct",
"type": "direct",
"healthy": true,
"stats": {
"active_connections": 5,
"total_connections": 100,
"bytes_sent": 1024000,
"bytes_received": 2048000
}
}
]
GET /api/v1/backends/{name}
GET /api/v1/backends/{name}/stats

Get sanitized config (safe to display):

GET /api/v1/config

Get full config for editing:

GET /api/v1/config/full

Get config section metadata (hot-reload info):

GET /api/v1/config/meta

Save configuration:

PUT /api/v1/config
Content-Type: application/json
{
"config": { ... },
"create_backup": true
}

Validate config without saving:

POST /api/v1/config/validate
Content-Type: application/json
{ ... config object ... }

Reload configuration:

POST /api/v1/config/reload

Get recent requests (requires enable_request_log: true in config):

GET /api/v1/requests
GET /api/v1/requests?limit=50
GET /api/v1/requests?since=123 # Get requests since ID

Response:

{
"enabled": true,
"requests": [
{
"id": 1,
"timestamp": "2024-01-01T00:00:00Z",
"method": "GET",
"host": "example.com",
"path": "/api/data",
"backend": "direct",
"status_code": 200,
"duration_ms": 150,
"bytes_sent": 1024,
"bytes_recv": 2048
}
]
}

Get request log stats:

GET /api/v1/requests/stats

Clear request log:

DELETE /api/v1/requests

Get all active connections:

GET /api/v1/connections

Response:

{
"connections": [
{
"id": "20240115100000-1",
"client_ip": "192.168.1.100",
"client_port": "54321",
"host": "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 unique connected clients with aggregated stats:

GET /api/v1/connections/clients

Response:

{
"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"
}

Get cache statistics:

GET /api/v1/cache/stats

Response:

{
"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 cached entries:

GET /api/v1/cache/entries
GET /api/v1/cache/entries?domain=*.steamcontent.com
GET /api/v1/cache/entries?limit=10&offset=0

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 single entry metadata:

GET /api/v1/cache/entries/{key}

Delete single entry:

DELETE /api/v1/cache/entries/{key}

Clear all cache:

DELETE /api/v1/cache/entries?confirm=true

Purge entries for a domain:

DELETE /api/v1/cache/domain/{domain}

List caching rules:

GET /api/v1/cache/rules

Response:

{
"rules": [
{
"name": "steam",
"domains": ["*.steamcontent.com", "content*.steampowered.com"],
"enabled": true,
"ttl": "8760h0m0s",
"priority": 100,
"preset": "steam"
}
]
}

Add custom rule:

POST /api/v1/cache/rules
Content-Type: application/json
{
"name": "my-cdn",
"domains": ["cdn.example.com"],
"enabled": true,
"ttl": "168h",
"priority": 50
}

Update rule:

PUT /api/v1/cache/rules/{name}

Delete rule:

DELETE /api/v1/cache/rules/{name}

List available presets:

GET /api/v1/cache/presets

Response:

{
"presets": [
{
"name": "steam",
"description": "Steam game downloads and updates",
"domains": ["*.steamcontent.com", "content*.steampowered.com"],
"ttl": "8760h0m0s",
"priority": 100
}
]
}

Enable/disable preset:

POST /api/v1/cache/presets/{name}
Content-Type: application/json
{"enabled": true}

Bifrost automatically generates a PAC file based on your routing rules:

GET /proxy.pac
GET /wpad.dat

The PAC file contains JavaScript that browsers use for automatic proxy configuration. Example output:

function FindProxyForURL(url, host) {
if (shExpMatch(host, "*.internal.company.com")) {
return "PROXY bifrost.example.com:7080; DIRECT";
}
return "DIRECT";
}

Using the PAC file:

  • 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 use Proxy SwitchyOmega extension

Default: http://localhost:7383

GET /api/v1/status

Response:

{
"status": "running",
"server_status": "connected",
"time": "2024-01-01T00:00:00Z",
"version": "1.0.0",
"debug_entries": 150
}

Get all debug entries:

GET /api/v1/debug/entries

Get last N entries:

GET /api/v1/debug/entries/last/100

Clear entries:

DELETE /api/v1/debug/entries

Get errors only:

GET /api/v1/debug/errors

List routes:

GET /api/v1/routes

Test a domain:

GET /api/v1/routes/test?domain=example.com

Response:

{
"domain": "example.com",
"action": "server"
}

Get VPN connection status:

GET /api/v1/vpn/status

Response:

{
"status": "connected",
"enabled": true,
"tunnel_type": "WireGuard",
"interface_name": "bifrost0",
"local_ip": "10.0.0.2",
"gateway": "10.0.0.1",
"dns_servers": ["1.1.1.1", "8.8.8.8"],
"mtu": 1420,
"port": 51820,
"encryption": "ChaCha20-Poly1305",
"bytes_sent": 1048576,
"bytes_received": 2097152,
"connected_since": "2024-01-15T10:00:00Z"
}
POST /api/v1/vpn/enable
POST /api/v1/vpn/disable

Response:

{
"status": "ok"
}

Get active VPN connections:

GET /api/v1/vpn/connections

Response:

[
{
"id": "conn-123",
"remote_addr": "example.com:443",
"local_addr": "10.0.0.2:54321",
"protocol": "tcp",
"started_at": "2024-01-15T10:00:00Z",
"bytes_sent": 1024,
"bytes_received": 2048
}
]

Get split tunnel configuration:

GET /api/v1/vpn/split/rules

Response:

{
"mode": "exclude",
"apps": [
{"name": "Slack", "path": "/Applications/Slack.app"}
],
"domains": ["*.local", "localhost"],
"ips": ["192.168.0.0/16", "10.0.0.0/8"]
}
POST /api/v1/vpn/split/apps
Content-Type: application/json
{
"name": "Discord",
"path": "/Applications/Discord.app"
}
DELETE /api/v1/vpn/split/apps/{name}
POST /api/v1/vpn/split/domains
Content-Type: application/json
{
"pattern": "*.internal.company.com"
}
POST /api/v1/vpn/split/ips
Content-Type: application/json
{
"cidr": "172.16.0.0/12"
}

List available servers:

GET /api/v1/servers

Response:

[
{
"id": "us-west",
"name": "US West",
"address": "us-west.example.com:7080",
"protocol": "HTTP",
"is_default": true,
"latency_ms": 45,
"status": "online"
}
]
POST /api/v1/servers/{id}/select

Response:

{
"status": "ok"
}

Real-time updates are available via WebSocket:

const ws = new WebSocket('ws://localhost:7082/api/v1/ws');
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(data);
};

Events:

  • connection.new - New connection established
  • connection.close - Connection closed
  • backend.health - Backend health changed
  • config.reload - Configuration reloaded