Skip to main content

Cloud Infrastructure

The cloud layer is optional. Every core ADOS function works without internet. But when you want remote monitoring, fleet management, or observer access from across the internet, three relay layers provide increasing levels of real-time capability.

Three layers

Layer 1: Convex HTTP (baseline)

The simplest relay. The agent POSTs a JSON status payload to the Convex backend every 5 seconds. Mission Control uses Convex’s reactive queries to display the data in real time.
DetailValue
Endpointhttps://convex.altnautica.com/api/agent/status
FrequencyEvery 5 seconds
PayloadJSON: mode, armed, battery, GPS, altitude, speed, connection state
BandwidthUnder 1 Kbps
Latency5-10 seconds (poll interval)
This layer requires only outbound HTTPS from the agent. No port forwarding, no MQTT, no special setup. If the agent can reach the internet, status shows up in the GCS.

Convex tables

Two custom tables power the cloud relay:
  • cmd_droneStatus: Stores the latest status for each device. Upserted on every POST. Reactive query in the browser delivers changes immediately.
  • cmd_droneCommands: Command queue. Mission Control enqueues commands (arm, disarm, mode change). The agent polls this table and ACKs each command after execution.

Layer 2: MQTT (real-time telemetry)

For higher-frequency data, the agent publishes to MQTT topics via a Mosquitto broker behind a Cloudflare Tunnel. Mission Control subscribes from the browser using MQTT.js over WebSocket.
DetailValue
Brokermqtt.altnautica.com (WebSocket on port 443, Cloudflare Tunnel)
Topicsados/{deviceId}/status, ados/{deviceId}/telemetry
Frequency2 Hz (configurable)
PayloadJSON telemetry (attitude, GPS, battery, sensors)
Bandwidth5-15 Kbps
Latency100-300 ms
AuthUsername ados, hashed password
The MQTT-to-Convex bridge runs alongside the broker. It subscribes to all ados/+/status and ados/+/telemetry topics, debounces 3 seconds per device, and POSTs the latest data to Convex. This keeps the Convex tables fresh for clients that use reactive queries instead of direct MQTT.

Why MQTT, not just Convex polling

Convex reactive queries are great for UI updates but the minimum granularity is tied to the 5-second HTTP POST cycle. MQTT gives true 2 Hz telemetry with ~200 ms latency. For a remote operator watching a live mission, the difference between 5-second updates and 500 ms updates is significant. MQTT also handles unreliable connections better. QoS 1 ensures delivery even if the TCP connection momentarily drops.

Layer 3: WebRTC video (peer-to-peer)

Live video does not go through a cloud media server. Instead, the browser and agent establish a direct WebRTC peer-to-peer connection. The MQTT broker acts as the signaling relay. The signaling flow:
  1. Browser publishes an SDP offer to ados/{deviceId}/webrtc/offer
  2. Agent’s WebrtcSignalingRelay service receives the offer via MQTT
  3. Agent creates a WebRTC answer using the local MediaMTX WHEP endpoint
  4. Agent publishes the SDP answer to ados/{deviceId}/webrtc/answer
  5. Browser receives the answer, ICE candidates are exchanged
  6. WebRTC media stream flows directly between browser and agent (peer-to-peer)
Once the peer connection is established, video flows directly between the two peers. The MQTT broker is only involved during the signaling handshake, not during streaming.
P2P WebRTC requires both sides to be able to reach each other after STUN-negotiated NAT traversal. About 85-90% of networks support this. The remaining 10-15% (symmetric NAT on some cellular carriers) need a TURN relay, which is not yet deployed. Those users see a clear error in the transport switcher.

Infrastructure layout

All cloud services are co-located on a single Linux server:
ServicePortAccess
Convex backend3210convex.altnautica.com via Cloudflare Tunnel
Mosquitto MQTT1883 (TCP), 9001 (WebSocket)mqtt.altnautica.com via Cloudflare Tunnel
MQTT-to-Convex bridgeInternalSubscribes to Mosquitto, POSTs to Convex
Everything routes through Cloudflare Tunnels. No inbound ports are open on the server. The Tunnel client (cloudflared) maintains outbound connections to Cloudflare’s edge.

Self-hosting

You can run the entire cloud stack on your own hardware using Docker Compose.

Convex backend

git clone https://github.com/altnautica/ADOSMissionControl
cd ADOSMissionControl
npx convex dev  # Starts a local Convex dev server
Or deploy to Convex cloud (free tier available) and point your agent at it.

MQTT broker + bridge

The MQTT broker and bridge are in ADOSMissionControl/tools/mqtt-bridge/:
cd tools/mqtt-bridge
docker compose up -d
This starts Mosquitto with WebSocket support and the MQTT-to-Convex bridge. Edit .env to point the bridge at your Convex deployment.

Agent configuration

Point your agent at your own cloud:
# /etc/ados/config.yaml
cloud:
  convex_url: "https://your-convex-instance.com"
  mqtt_broker: "mqtt://your-broker.example.com:1883"
  mqtt_ws_url: "wss://your-broker.example.com:9001"

Cloudflare Tunnel setup

If you want to expose your self-hosted services without port forwarding:
1

Install cloudflared

curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
2

Create a tunnel

cloudflared tunnel login
cloudflared tunnel create ados-relay
3

Configure hostname routing

Create ~/.cloudflared/config.yml:
tunnel: YOUR_TUNNEL_ID
credentials-file: /root/.cloudflared/YOUR_TUNNEL_ID.json
ingress:
  - hostname: convex.yourdomain.com
    service: http://localhost:3210
  - hostname: mqtt.yourdomain.com
    service: http://localhost:9001
  - service: http_status:404
4

Start the tunnel

cloudflared tunnel run ados-relay

Bandwidth and cost

ComponentBandwidthMonthly cost
Convex (self-hosted)Under 1 GB/monthFree (your hardware)
Convex (cloud free tier)Under 1 GB/monthFree
MQTT telemetry~100-500 MB/month per droneFree (self-hosted)
Cloudflare TunnelUnlimitedFree tier
STUN (Google/Cloudflare)NegligibleFree
Video (P2P WebRTC)0 server cost (peer-to-peer)Free
The entire cloud relay stack can run at zero monthly cost for small deployments.

What is next