Skip to content

VPN Cluster Management API

FastAPI service for managing geographic VPN clustering across OpenWRT workers.

Overview

The VPN Cluster API provides programmatic control over a network of 16 OpenWRT workers, each capable of running VPN tunnels. The API allows creating "banks" of workers that route traffic through specific geographic regions (countries or US states).

Architecture

flowchart TB
    subgraph Host["Host Server"]
        API[FastAPI Service<br/>Port 9002]
    end

    subgraph Master["Master Router (17.0.0.1)"]
        HAPROXY[HAProxy<br/>Ports 8890-8999]
        LUCI[LuCI RPC]
    end

    subgraph Workers["OpenWRT Workers (16x)"]
        W1[Worker 1<br/>17.0.0.10]
        W2[Worker 2<br/>17.0.0.11]
        WDOTS[...]
        W16[Worker 16<br/>17.0.0.25]
    end

    subgraph VPN["ProtonVPN Profiles"]
        P1[~12,909 profiles]
        P2[127 countries]
        P3[US state routing]
    end

    API -->|RPC| LUCI
    API -->|Configure| HAPROXY
    HAPROXY -->|Load Balance| W1 & W2 & W16
    W1 & W2 & W16 -->|OpenVPN| VPN

Location

  • Source: /home/bisenbek/projects/tinymachines/llarp/api/
  • Symlink: extern/llarp -> /home/bisenbek/projects/tinymachines/llarp/

Quick Start

# Start the API (from llarp directory)
cd /home/bisenbek/projects/tinymachines/llarp
source ~/.pyenv/versions/nominates/bin/activate
python -m uvicorn api.main:app --host 0.0.0.0 --port 9002

# Check health
curl http://localhost:9002/health

# List workers
curl http://localhost:9002/api/v1/workers/

# Create a bank
curl -X POST http://localhost:9002/api/v1/banks/ \
  -H "Content-Type: application/json" \
  -d '{"name": "California", "workers": [1, 2, 3], "filter": "us:ca"}'

API Endpoints

Health

Method Endpoint Description
GET /health API health check
GET / Root info

Banks (Geographic VPN Pools)

Method Endpoint Description
GET /api/v1/banks/ List all banks
POST /api/v1/banks/ Create a new bank
GET /api/v1/banks/{tag} Get bank details
PUT /api/v1/banks/{tag} Update bank
DELETE /api/v1/banks/{tag} Delete bank
POST /api/v1/banks/{tag}/idle Pause bank
POST /api/v1/banks/{tag}/resume Resume bank

Workers

Method Endpoint Description
GET /api/v1/workers/ List all workers
GET /api/v1/workers/{n} Get worker details
GET /api/v1/workers/{n}/exit-ip Get worker's current exit IP
POST /api/v1/workers/{n}/vpn/start Start VPN on worker
POST /api/v1/workers/{n}/vpn/stop Stop VPN on worker
POST /api/v1/workers/{n}/vpn/restart Restart VPN on worker

Cluster

Method Endpoint Description
GET /api/v1/cluster/ Cluster status
GET /api/v1/cluster/health Cluster health
POST /api/v1/cluster/idle Pause all banks
POST /api/v1/cluster/resume Resume all banks
POST /api/v1/cluster/reset Reset cluster

Devices (Device Registry & Management)

Method Endpoint Description
GET /api/v1/devices/ List all devices in registry
GET /api/v1/devices/{id} Get device from registry
PUT /api/v1/devices/{id} Update device metadata
POST /api/v1/devices/{id}/refresh Refresh device status from live device
POST /api/v1/devices/refresh Refresh all devices
POST /api/v1/devices/{id}/ping Ping device and get latency
GET /api/v1/devices/{id}/status Get device system info
GET /api/v1/devices/{id}/external-ip Get device external IP with geolocation
POST /api/v1/devices/{id}/speedtest Run bandwidth test on device
POST /api/v1/devices/{id}/execute Execute command on device
POST /api/v1/devices/{id}/reboot Reboot device
POST /api/v1/devices/{id}/sync-time Sync device time via NTP
POST /api/v1/devices/ping-all Ping all devices in parallel

Browser Automation (Planned)

Method Endpoint Description
POST /api/v1/browser/screenshot Take screenshot via proxy
POST /api/v1/browser/scrape Scrape page via proxy
POST /api/v1/browser/pdf Generate PDF via proxy

Tor (Planned)

Method Endpoint Description
GET /api/v1/tor/workers List tor-enabled workers
POST /api/v1/tor/workers/{n}/tor/start Start tor on worker

Creating Banks

Banks are pools of workers assigned to a geographic region. When you create a bank:

  1. Workers are assigned from the available pool
  2. VPN profiles matching the filter are selected
  3. HAProxy is configured to load-balance across workers
  4. A unique port (8890-8999) is allocated

Bank Create Schema

{
  "name": "California Test",     // Display name
  "workers": [1, 2, 3],          // Worker numbers (1-16)
  "filter": "us:ca"              // Geographic filter
}

Filter Format

Filter Description
us United States (any state)
us:ca California
us:ny New York
de Germany
uk United Kingdom
us:tx:tor Texas with Tor exit

Bank Response

{
  "name": "California Test",
  "tag": "californiatest",
  "workers": [1, 2, 3],
  "worker_assignments": [
    {
      "worker_number": 1,
      "worker_ip": "17.0.0.10",
      "vpn_profile": "us-ca-324.protonvpn.udp.ovpn",
      "vpn_status": "up",
      "exit_ip": "146.70.195.107"
    }
  ],
  "filter": {"country": "us", "state": "ca", "type": null},
  "port": 8894,
  "status": "active",
  "endpoint": "http://17.0.0.1:8894",
  "health": {
    "workers_total": 3,
    "workers_up": 3,
    "workers_down": 0,
    "success_rate": 1.0
  }
}

Device Registry

The Device Registry provides comprehensive tracking of all 16 OpenWRT devices with cached metadata, WireGuard tunnel info, VPN status, and performance metrics.

DeviceRegistry Model

{
  "id": 1,
  "hostname": "lazarus-worker-01",
  "lan_ip": "17.0.0.10",
  "wg_interface": "wg01",
  "wg_tunnel_ip": "10.200.1.2",
  "wg_public_key": "...",
  "vpn_profile": "es-mad-001.protonvpn.udp.ovpn",
  "vpn_country": "Spain",
  "vpn_region": "Madrid",
  "vpn_provider": "ProtonVPN",
  "external_ip": "185.159.157.42",
  "status": "online",
  "last_seen": "2026-01-03T12:00:00Z",
  "uptime": 86400,
  "load_avg": 0.15,
  "memory_free_mb": 128.5,
  "memory_total_mb": 256.0,
  "ping_latency_ms": 2.5,
  "download_speed_mbps": 95.2,
  "upload_speed_mbps": 48.7,
  "tags": ["production", "europe"],
  "notes": "Primary EU exit node"
}

Device Utility Functions

# Ping a device
curl -X POST http://localhost:9002/api/v1/devices/1/ping?count=5

# Get device system info
curl http://localhost:9002/api/v1/devices/1/status

# Get external IP with geolocation
curl http://localhost:9002/api/v1/devices/1/external-ip

# Run speedtest on device
curl -X POST http://localhost:9002/api/v1/devices/1/speedtest

# Execute command on device
curl -X POST "http://localhost:9002/api/v1/devices/1/execute?command=uptime"

# Refresh device status from live device
curl -X POST http://localhost:9002/api/v1/devices/1/refresh

# Refresh all devices
curl -X POST http://localhost:9002/api/v1/devices/refresh

# Ping all devices
curl -X POST http://localhost:9002/api/v1/devices/ping-all

Device Registry Storage

The device registry is stored at /var/lib/vpn-banks/device-registry.json and provides persistent storage for device metadata, WireGuard configuration, and cached status information.

Worker Status

Each worker provides detailed status:

{
  "ip": "17.0.0.10",
  "worker_number": 1,
  "status": "online",
  "vpn_status": "up",
  "vpn_profile": "us-ca-324.protonvpn.udp.ovpn",
  "exit_ip": "146.70.195.107",
  "exit_country": "US",
  "exit_region": "California",
  "exit_city": "Los Angeles",
  "proxy_status": "running",
  "proxy_port": 3128
}

Configuration

Environment Variables (.env)

OPENWRT_USERNAME=root
OPENWRT_PASSWORD=<password>
MASTER_IP=17.0.0.1
API_PORT=9000
PROFILES_BASE=/home/bisenbek/projects/tinymachines/llarp/resources/profiles/intl-ovpn

VPN Profiles

Located at: /home/bisenbek/projects/tinymachines/llarp/resources/profiles/intl-ovpn/

  • ~12,909 ProtonVPN profiles
  • 127 countries
  • US profiles include state codes (us-ca, us-ny, etc.)

Integration with cbintel

Current Integration Points

  1. WireGuard tunnels (wg01-wg15) already route through workers
  2. VPN API provides bank management for geographic routing
  3. HAProxy load-balances across worker proxies

Proposed cbintel Refactoring

src/cbintel/
└── cluster/
    ├── __init__.py
    ├── api.py          # VPN Cluster API client
    ├── bank.py         # Bank management
    ├── worker.py       # Worker operations
    └── proxy.py        # Proxy configuration

Testing

# Health check
curl http://localhost:9002/health

# List workers with status
curl http://localhost:9002/api/v1/workers/ | python3 -m json.tool

# Create test bank
curl -X POST http://localhost:9002/api/v1/banks/ \
  -H "Content-Type: application/json" \
  -d '{"name": "Test", "workers": [1], "filter": "us:ca"}'

# Get worker exit IP
curl http://localhost:9002/api/v1/workers/1/exit-ip

# Clean up
curl -X DELETE http://localhost:9002/api/v1/banks/test

Files

File Purpose
src/cbintel/cluster/main.py FastAPI application
src/cbintel/cluster/config.py Settings (pydantic)
src/cbintel/cluster/routers/banks.py Bank endpoints
src/cbintel/cluster/routers/workers.py Worker endpoints
src/cbintel/cluster/routers/cluster.py Cluster endpoints
src/cbintel/cluster/routers/devices.py Device registry endpoints
src/cbintel/cluster/services/bank_service.py Bank business logic
src/cbintel/cluster/services/worker_service.py Worker operations
src/cbintel/cluster/services/device_service.py Device registry & utilities
src/cbintel/cluster/services/state_manager.py Persistent state management
src/cbintel/cluster/clients/luci_rpc.py OpenWRT RPC client
src/cbintel/cluster/models/device.py Device & DeviceRegistry models
src/cbintel/cluster/models/worker.py Worker models
src/cbintel/cluster/models/bank.py Bank models

References