Overview
The WebSocket Tunnel is the backbone of EdgeFlow's SaaS connectivity. It establishes a persistent, encrypted connection between each device and the cloud, enabling real-time bidirectional communication without requiring the device to have a public IP address or open inbound ports.
How It Works
Device (behind NAT/firewall) Cloud Server
│ │
│ 1. WebSocket CONNECT (wss://) │
│───────────────────────────────────>│
│ │
│ 2. Auth: DeviceID + APIKey │
│───────────────────────────────────>│
│ │
│ 3. "connected" confirmation │
│<───────────────────────────────────│
│ │
│ 4. Heartbeat (ping every 30s) │
│<──────────────────────────────────>│
│ │
│ 5. Cloud sends command │
│<───────────────────────────────────│
│ │
│ 6. Device sends response │
│───────────────────────────────────>│ Tunnel Agent
The Tunnel Agent runs on each device and manages the WebSocket connection lifecycle. It handles authentication, heartbeats, reconnection, and message routing.
Key Features
- TLS Encryption — All traffic encrypted via WSS (WebSocket Secure)
- Auto-Reconnect — Exponential backoff with jitter (up to 60 seconds)
- Heartbeat — 30-second ping/pong to detect stale connections
- Authentication Timeout — 10-second window for auth handshake
- Thread Safety — Concurrent read/write with mutex protection
- Graceful Shutdown — Clean WebSocket close frame on disconnect
Message Protocol
All tunnel messages use a JSON envelope:
{
"type": "command", // connect | connected | ping | pong | command | response
"id": "msg_abc123", // Unique message ID for request-response correlation
"device_id": "dev_xyz", // Device identifier
"api_key": "efk_...", // Only in "connect" messages
"version": "1.2.3", // Agent version
"action": "start_flow", // For command messages
"payload": {}, // Action-specific data
"status": "success", // For response messages
"data": {}, // Response data
"error": "", // Error message if failed
"timestamp": "2026-02-21T12:00:00Z"
} Message Types
| Type | Direction | Description |
|---|---|---|
connect | Device → Cloud | Authentication handshake with Device ID, API Key, and agent version |
connected | Cloud → Device | Confirmation that authentication succeeded |
ping | Device → Cloud | Heartbeat signal sent every 30 seconds |
pong | Cloud → Device | Heartbeat response confirming connection is alive |
command | Cloud → Device | Remote command with action and payload |
response | Device → Cloud | Command response with status, data, or error |
Connection States
┌────────────┐ connect() ┌──────────────┐
│Disconnected│───────────────────>│ Connecting │
└────────────┘ └──────┬───────┘
▲ │
│ max retries │ auth success
│ exceeded ▼
│ ┌──────────────┐
│ connection lost │ Connected │
│◄─────────────────────────│ (heartbeat) │
│ └──────┬───────┘
│ │
│ │ connection lost
│ ▼
│ ┌──────────────┐
└──────────────────────────│ Reconnecting │
max retries │ (backoff) │
└──────────────┘ Reconnection Strategy
When the connection drops, the Tunnel Agent uses exponential backoff to reconnect:
| Attempt | Delay | Max Delay |
|---|---|---|
| 1 | 1 second | 60 seconds |
| 2 | 2 seconds | |
| 3 | 4 seconds | |
| 4 | 8 seconds | |
| 5 | 16 seconds | |
| 6+ | 32-60 seconds |
After 5 consecutive failed attempts (configurable via EDGEFLOW_SAAS_MAX_RECONNECT_ATTEMPTS),
the agent stops retrying and logs an error.
Configuration
# Tunnel server address
EDGEFLOW_SAAS_URL=saas.edgx.cloud:443
# Enable TLS (wss://) — recommended for production
EDGEFLOW_SAAS_TLS=true
# Heartbeat interval (default: 30 seconds)
EDGEFLOW_SAAS_HEARTBEAT_INTERVAL=30s
# Max reconnection attempts (default: 5, 0 = unlimited)
EDGEFLOW_SAAS_MAX_RECONNECT_ATTEMPTS=5 Firewall Requirements
Since the device initiates the outbound WebSocket connection, you only need:
- Outbound port 443 (WSS/TLS) — to the SaaS server
- No inbound ports required on the device
- Works behind NAT, firewalls, and corporate proxies
Monitoring
Check the tunnel status at any time via the local API:
curl http://localhost:8080/api/v1/saas/status
{
"connected": true,
"device_id": "dev_abc123xyz",
"status": "online",
"connected_at": "2026-02-21T08:00:00Z",
"last_heartbeat": "2026-02-21T12:34:00Z",
"agent_version": "1.2.3",
"tunnel_url": "wss://saas.edgx.cloud:443/tunnel"
}