Dynamic Proxy Integration Guide
The Machine Broker includes a dynamic reverse proxy feature that allows control applications to register HTTP proxy routes on-the-fly. This enables simplified DNS and SSL management by using a wildcard certificate.
Overview
- Control apps can register subdomain routes (e.g.,
hvac→192.168.1.50:8080) - Requests to
hvac.eng.sttark.comare proxied to the registered target - Wildcard SSL certificate handles all subdomains automatically
- Health monitoring tracks the status of proxy targets
- Authentication: All
*.eng.sttark.comrequests require Google OAuth (@sttark.com accounts) or a valid API key
Architecture
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Browser │────────▶│ Machine Broker │────────▶│ Target Server │
│ (@sttark.com │ │ │ │ 10.0.1.5 │
│ Google login) │◀────────│ *.eng.sttark.com │◀────────│ :8080 │
│ │ │ (SSL + OAuth) │ │ (VPC only) │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
▲
│
┌─────────┴─────────┐
│ Control App │
│ (registers route)│
└───────────────────┘
Authentication
All requests to *.eng.sttark.com are authenticated before being proxied to the target.
Browser Access (Google OAuth)
When accessing any *.eng.sttark.com URL in a browser, you are redirected to Google login. Only @sttark.com Google accounts are accepted. After login, a session cookie is set that works across all subdomains for 30 days.
Programmatic Access (API Key)
For scripts and automated clients, include an X-API-Key header to bypass the OAuth flow:
curl -H "X-API-Key: YOUR_API_KEY" https://hvac.eng.sttark.com/
WebSocket connections can also pass the API key:
const ws = new WebSocket('wss://hvac.eng.sttark.com/ws', {
headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
Target Machine Security
Target machines are on a VPC (private network) with a default-deny firewall. They are not accessible directly from the internet on any port -- only the broker can reach them via the VPC.
WebSocket Protocol
Registering a Proxy Route
Connect to /control and send a register_proxy message:
const ws = new WebSocket('wss://machinebroker.sttark.com/control?apiKey=YOUR_API_KEY');
ws.onopen = () => {
// Register a proxy route
ws.send(JSON.stringify({
type: 'register_proxy',
subdomain: 'hvac',
target: 'http://192.168.1.50:8080'
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'proxy_registered') {
console.log(`Proxy registered: ${msg.url}`);
// Output: Proxy registered: https://hvac.eng.sttark.com
}
};
Response
{
"type": "proxy_registered",
"action": "proxy_registered",
"subdomain": "hvac",
"target": "http://192.168.1.50:8080",
"url": "https://hvac.eng.sttark.com",
"replaced": false,
"timestamp": "2026-02-04T15:30:00.000Z"
}
Removing a Proxy Route
ws.send(JSON.stringify({
type: 'unregister_proxy',
subdomain: 'hvac'
}));
Listing Proxy Routes
ws.send(JSON.stringify({
type: 'list_proxies'
}));
Response:
{
"type": "success",
"action": "list_proxies",
"proxies": [
{
"subdomain": "hvac",
"target": "http://192.168.1.50:8080",
"url": "https://hvac.eng.sttark.com",
"health": {
"status": "healthy",
"lastCheck": "2026-02-04T15:30:00.000Z",
"responseTimeMs": 45
},
"stats": {
"requestCount": 1234,
"lastAccessed": "2026-02-04T15:29:55.000Z",
"createdAt": "2026-02-04T10:00:00.000Z"
}
}
],
"summary": {
"total": 1,
"healthy": 1,
"unhealthy": 0,
"unknown": 0
},
"timestamp": "2026-02-04T15:30:00.000Z"
}
Triggering Health Check
// Check specific subdomain
ws.send(JSON.stringify({
type: 'check_proxy_health',
subdomain: 'hvac'
}));
// Check all proxies
ws.send(JSON.stringify({
type: 'check_proxy_health'
}));
REST API Endpoints
All endpoints require API key authentication via X-API-Key header.
List All Proxies
curl -H "X-API-Key: YOUR_API_KEY" \
https://machinebroker.sttark.com/api/proxies
Get Single Proxy
curl -H "X-API-Key: YOUR_API_KEY" \
https://machinebroker.sttark.com/api/proxies/hvac
Get Proxy Health
curl -H "X-API-Key: YOUR_API_KEY" \
https://machinebroker.sttark.com/api/proxies/hvac/health
Trigger Health Check
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
https://machinebroker.sttark.com/api/proxies/hvac/check
Health Monitoring
Health Statuses
| Status | Description |
|---|---|
healthy |
Target responded with 2xx/3xx status |
unhealthy |
Target failed multiple consecutive checks |
unknown |
New route, not yet checked |
WebSocket Health Updates
Connect to /health to receive real-time health updates:
const ws = new WebSocket('wss://machinebroker.sttark.com/health');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'proxy_health_update') {
console.log(`${msg.subdomain} is now ${msg.health.status}`);
}
};
Health Check Configuration
Environment variables:
PROXY_HEALTH_INTERVAL=30000 # Check every 30 seconds
PROXY_HEALTH_TIMEOUT=5000 # 5 second timeout
PROXY_HEALTH_UNHEALTHY_THRESHOLD=3 # 3 failures = unhealthy
Target URL Formats
The target parameter supports various formats:
// IP with port
{ target: 'http://192.168.1.50:8080' }
// Domain name
{ target: 'http://internal-server.local' }
// HTTPS target
{ target: 'https://secure-backend.example.com' }
// With path (optional)
{ target: 'http://192.168.1.50:8080/api/v1' }
Subdomain Rules
- Lowercase alphanumeric and hyphens only
- Cannot start or end with a hyphen
- Reserved subdomains:
www,api,mail,ftp,admin,broker
Valid examples: hvac, plc-1, camera2, building-a
Invalid examples: HVAC, plc_1, -invalid, my.sub
Backward Compatibility
This feature is fully backward compatible:
- Existing device connections (
/devices) work unchanged - Existing control connections (
/control) work unchanged - Proxy messages are optional - controls that don't use them work normally
- The proxy domain (
*.eng.sttark.com) is separate from the broker domain
Deployment
Prerequisites
- DNSimple account with API access
- DNS records pointing to your server:
machinebroker.sttark.com→ your server IPeng.sttark.com→ your server IP*.eng.sttark.com→ your server IP (wildcard A record)
- Google Cloud OAuth credentials (project
frontier-account):- Redirect URI:
https://eng.sttark.com/oauth2/callback - Authorized JS origins:
https://eng.sttark.com,https://machinebroker.sttark.com
- Redirect URI:
Environment Variables
# Required for wildcard SSL
DNSIMPLE_API_KEY=your-api-key
DNSIMPLE_ACCOUNT_ID=your-account-id
# Proxy configuration
PROXY_ENABLED=true
PROXY_DOMAIN=eng.sttark.com
# Google OAuth (for *.eng.sttark.com authentication)
OAUTH2_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
OAUTH2_CLIENT_SECRET=your-google-client-secret
OAUTH2_COOKIE_SECRET= # Generate with: openssl rand -base64 32 | tr -- '+/' '-_'
Ansible Deployment
The Ansible playbook automatically:
- Installs certbot with DNSimple plugin
- Obtains wildcard SSL certificate via DNS-01 challenge
- Configures nginx for both broker and proxy domains
- Installs and configures oauth2-proxy for Google OAuth
- Sets up auto-renewal
cd deploy/ansible
ansible-playbook -i inventory.ini site.yml
Target Machine Setup
Target machines should be provisioned with a default-deny firewall:
./deploy/scripts/setup-all.sh --target
This creates a Linode on the VPC, applies firewall rules (SSH + VPC only), and attaches the Linode Cloud Firewall.
If the target machine owner is setting up their Linode manually, without using this repository's scripts, hand them docs/TARGET_SERVER_GUIDE.md. It contains the full VPC, host firewall, Linode Cloud Firewall, broker handoff, and validation steps needed to secure a target server for *.eng.sttark.com.
Example: Complete Integration
const WebSocket = require('ws');
class ProxyManager {
constructor(brokerUrl, apiKey) {
this.ws = new WebSocket(`${brokerUrl}/control?apiKey=${apiKey}`);
this.proxyDomain = 'eng.sttark.com';
this.ws.on('message', (data) => {
const msg = JSON.parse(data);
this.handleMessage(msg);
});
}
registerProxy(subdomain, targetIp, targetPort) {
this.ws.send(JSON.stringify({
type: 'register_proxy',
subdomain,
target: `http://${targetIp}:${targetPort}`
}));
}
handleMessage(msg) {
switch (msg.type) {
case 'proxy_registered':
console.log(`✓ Proxy ready: ${msg.url}`);
break;
case 'proxy_health_update':
const icon = msg.health.status === 'healthy' ? '✓' : '✗';
console.log(`${icon} ${msg.subdomain}: ${msg.health.status}`);
break;
}
}
}
// Usage
const manager = new ProxyManager('wss://machinebroker.sttark.com', 'your-api-key');
manager.registerProxy('hvac', '192.168.1.50', 8080);
// Now https://hvac.eng.sttark.com proxies to 192.168.1.50:8080