SkyHub is a drone-fleet operations platform. A browser Dashboard operates drones; a Flask Gateway Service brokers every command and telemetry stream; and each drone runs the SkyCore Drone OS (ROS 2 + MAVLink on a Jetson) — or is a SITL simulation the Gateway spawns on demand. Around that control loop sit a video plane (Janus + WHIP), a network plane (WireGuard User VPN behind a single jumphost), a separate low-latency manual-control path (WS Proxy + Redis), and shared infrastructure (PostgreSQL, Redis, S3). The whole stack spans six repositories — see the Repository Map for who owns what. This page is the map. It shows how the pieces connect and points you at the four deep-dive pages in this group when you need the detail. The Dashboard operating a drone

The big picture

The Gateway Service is the control-plane hub. It is the only component that authenticates users, spawns SITL, and opens rosbridge WebSockets to drones, and it owns the primary read/write database access. Almost every arrow either starts or ends at the Gateway. Video and manual gamepad control are the two deliberate exceptions — they travel out-of-band so a slow API call can never stall a live feed or a joystick.
The four planes are worth naming, because each has its own transport, latency budget, and failure mode:

Control plane

Dashboard → Gateway over HTTP (JWT) + Socket.IO; Gateway → drone over rosbridge WebSocket on port 9090. Carries commands (arm, takeoff, set_mode, mission push) and all telemetry.

Video plane

On-drone GStreamer publishes H264 to the WHIP server, which registers a publisher in a Janus VideoRoom; the Dashboard subscribes over WebRTC. Fully out-of-band from the API.

Network plane

Per-user WireGuard tunnels make physical drones (10.71.x) reachable, with iptables isolation so users only reach the drones they own. One EC2 jumphost is the sole public ingress.

Manual-control plane

Gamepad frames travel Dashboard → WS Proxy → Redis pub/sub → drone at 60 Hz — a separate low-latency path that never touches the Gateway’s rosbridge channel.

The Gateway is the control-plane hub

Every operator action funnels through the Gateway Service. It:
  • Authenticates users over HTTP with JWT (10-min access, 12-hr refresh tokens).
  • Owns the databaseUser, Drone, Mission, Asset, UserDroneAccess all live in one PostgreSQL instance the Gateway reads and writes.
  • Dispatches drone commands by translating REST calls (POST /api/v1/drone/action/{arm|takeoff|set_mode|push_mission|...}) into rosbridge call_service/publish frames on a pooled, auto-reconnecting WebSocket (src/rosbridge/connection.py, src/service/drone_control_service.py).
  • Streams telemetry back to the browser over Socket.IO — the drone’s MAVROS topics arrive on the rosbridge connection and are re-emitted as telemetry_data events.
  • Spawns and tears down SITL Docker containers on a remote Docker host (src/service/sitl_drone_service.py), gated by ENABLE_SITL.
  • Creates Janus video rooms and pushes the room credentials to the drone so it knows where to publish.
For the exact request and telemetry sequences, see Cross-System Data Flows.

SITL and physical drones are interchangeable

A design keystone: the Gateway speaks the same rosbridge/topic contract to a real drone and to a SITL container. A SITL container runs sim_vehicle.py + MAVROS + rosbridge_server + a GStreamer→WHIP video node — the identical interface a SkyCore drone exposes. The only differences the Gateway cares about are where to connect and lifecycle:
AspectPhysical droneSITL container
rosbridge targetdrone.ip (a 10.71.x WireGuard address)DOCKER_HOST_IP / office server over WireGuard
ReachabilityPer-user WireGuard tunnel + iptablesDocker host on the infra VPN (10.69.x)
LifecycleBoots, calls GET /drone/activateGateway creates/destroys via the Docker API
Everything elserosbridge :9090, MAVROS topics, WHIP videoIdentical
Because of this, code paths for commands, telemetry, missions, and video work unchanged whether you flew a physical aircraft or clicked “add SITL”. See SITL Drone Lifecycle and the SITL Simulator for the container internals.

Three real-time transport channels

Do not conflate these — they are independent connections with different protocols and different clients. Tuning or debugging one has no effect on the others.

Socket.IO (telemetry)

Browser ↔ Gateway. JWT in the ?token= query param. Rooms are named drone_{id}_dashboard. Carries GPS, altitude, logs, diagnostics.

rosbridge WebSocket

Gateway ↔ drone on port 9090. A persistent, auto-reconnecting, subscription-replaying connection per drone, pooled in DroneControlService.

WebRTC + WS Proxy

Video rides WebRTC through Janus; gamepad rides a raw WebSocket to the WS Proxy and Redis pub/sub. Both bypass the Gateway.
Real-time Transport Channels covers the heartbeats, throttling (TELEMETRY_THROTTLE_RATE, default 200 ms), reconnect/backoff, and the room-naming details for each.

Three auth models coexist

Different actors authenticate different ways. A change to one must not accidentally widen another.
ModelUsed byMechanism
JWT BearerDashboard / operatorsHS256 access + refresh tokens; Authorization: Bearer, or ?token= for the Socket.IO handshake
VPN source-IP trustDrone / gamepad callbackscheck_vpn_ip (src/middleware/drone_vpn.py) trusts any source in the 10.71.x drone subnet or a whitelisted X-Drone-IP. No cryptographic device identity — network trust only
Token / signatureActivation, video rooms, Stripe10-digit activation header token, per-room video token, and Stripe-Signature webhook verification
The VPN-IP model is why the whole platform must stay behind the VPN and jumphost: those drone-callback endpoints (/drone/pull, /executions/*, /authenticate_upload) carry no JWT. See Authentication & Security Model and the VPN Middleware & Jumphost page.

The jumphost: keystone and single point of failure

Production funnels almost all external traffic through one t4g.nano EC2 instance — the WireGuard server. That single box is simultaneously the public TLS ingress (nginx for prod.skyhub.ai), the jumphost that multiplexes rosbridge connections to every drone, the Docker API proxy to the on-prem SITL host, and the OTLP forwarder to SigNoz. There is no ALB. If it dies, essentially all external access is down. Any refactor must preserve this routing contract or replace the whole box deliberately.
The jumphost’s nginx reverse proxy fronts these ports (all on the WireGuard EC2 host):
PortRoutes toPurpose
443 / 80S3 UI bucketDashboard SPA
5000gateway.skyhub-prod.internal:5000REST API + Socket.IO (/socket.io/)
7070ws_proxy.skyhub-prod.internal:7070Gamepad WebSocket
8188janus.skyhub-prod.internal:8188Janus WebRTC signalling
9090http://$http_x_drone_ip:$http_x_drone_portDynamic rosbridge proxy via x-drone-ip / x-drone-port headers
2375office server <office-docker-host>:2375Remote Docker API for SITL
4317office server <office-docker-host>:4318OTLP traces/logs → SigNoz
UDP 20000-21100JanusWebRTC media relay
The :9090 header-driven proxy is what lets the Gateway reach any drone with a single connection string — it sets x-drone-ip/x-drone-port (src/rosbridge/connection.py:162) and nginx forwards accordingly. See Network & VPN Topology and VPC, WireGuard Jumphost & nginx Routing for the full port map and the WireGuard address plan.

Shared infrastructure

Three data stores are touched by multiple services, so schema or availability changes ripple widely:
  • PostgreSQL — the single skyhub database (user, drone, user_drone_access, missions, assets, executions). Read/written by the Gateway, the WS Proxy, and the User VPN service. In production it runs as an ECS Fargate container on EFS — not RDS.
  • Redis — the pub/sub bus for the gamepad/manual-control path ({drone_ip}:gamepad_input, {ip}:output, {ip}:aruco_tracking) and an optional Socket.IO message queue for multi-worker scaling. A container, not ElastiCache.
  • S3skyhub-prod-assets (video/image/log assets), skyhub-prod-user-vpn (per-user WireGuard configs), and the UI bucket.
Production runs in AWS eu-central-1 on ECS Fargate behind Cloud Map service discovery (skyhub-prod.internal). See the AWS Infrastructure Overview for the full deployment.

Where to go next

Cross-System Data Flows

Step-by-step traces: user command, telemetry stream, video pipeline, SITL spawn, mission upload, gamepad control, and drone activation.

Real-time Transport Channels

The Socket.IO, rosbridge, and WebRTC/WS-Proxy channels in depth — heartbeats, throttling, reconnect, and rooms.

Authentication & Security Model

The three auth models, where each applies, and the trust boundaries a change must respect.

Network & VPN Topology

The three WireGuard planes, the jumphost, per-user drone isolation, and how a drone becomes reachable.
To dive into a specific system instead: Gateway Service, SkyCore Drone OS, Dashboard, or the Ecosystem services (Janus, WHIP, SITL, User VPN, WS Proxy). New to the domain? Start with Core Concepts & Glossary.