9090), whether those drones are physical (SkyCore Drone OS) or SITL Docker containers it spawns. Everything else in the platform is a satellite service that handles one plane of responsibility the Gateway deliberately does not own end-to-end: live video, network isolation, low-latency manual control, shared storage, and the physical ground hardware.
This page is the map of those satellites — what each repo is, which plane it serves, and how it wires into core/gateway/dashboard by port and protocol. Deep-dives live in the sibling pages linked throughout. For the request-level walkthroughs (command path, telemetry, video, gamepad), see Cross-System Data Flows; for the repo inventory and ownership, see Repository Map.
The four planes
Control plane
Two independent paths reach a drone: the Gateway’s rosbridge channel (
:9090, telemetry + flight/mission commands) and the WS Proxy (:7070 → drone :5001, manual gamepad). They do not share a socket.Video plane
Out-of-band WebRTC. On-drone GStreamer publishes H264 to WHIP (
:7080), which registers a Janus VideoRoom publisher (:8188); the Dashboard subscribes over WebRTC.Network plane
The User VPN service runs three WireGuard planes and writes iptables rules so a user can only reach drones they own. This is how a physical drone’s
10.71.x address becomes routable.Shared infra
PostgreSQL (
user/drone/user_drone_access), Redis (pub/sub bus), and S3 (assets, VPN configs) are touched by the Gateway, WS Proxy, and User VPN alike.Platform map
Role-by-repo table
| Repo | Plane / role | Tech | Connects via | Deep-dive |
|---|---|---|---|---|
skyhub_gateway_service | Control-plane hub | Flask + Socket.IO | HTTP :5000, Socket.IO, rosbridge :9090, Docker API | /gateway/overview |
skyhub_janus | Video SFU (WebRTC) | C (meetecho janus-gateway) | WS API :8188, WebRTC media | /ecosystem/janus |
skyhub_whip | Video ingest (WHIP) | Node.js / Express | REST :7080/whip, Janus WS :8188 | /ecosystem/whip |
skyhub_sitl | Drone simulator | ArduPilot SITL + ROS2 + supervisord | rosbridge :9090, MAVROS UDP 14550, WHIP | /ecosystem/sitl |
skyhub_user_vpn | Network isolation | Python + WireGuard + iptables | WireGuard 51822/51823/51824, status API :5050 | /ecosystem/user-vpn |
skyhub_ws_proxy | Gamepad relay | FastAPI / uvicorn | WS :7070 → drone :5001 or Redis pub/sub | /ecosystem/ws-proxy |
skyhub_nexus | Battery-swap airhub (hardware) | Python + Arduino + pyusb | USB serial /dev/ttyACM0, IMAX USB | /ecosystem/nexus-and-vehicles |
skyhub_rover | Ground robot firmware | ESP32 / Arduino | WiFi AP/STA, ESP-NOW, gamepad :5001 | /ecosystem/nexus-and-vehicles |
skyhub_ugv | Ground base firmware | ESP32 / PlatformIO | WiFi, gamepad WS / JSON | /ecosystem/nexus-and-vehicles |
skyhub_observer | Planned (stub) | — | undefined | /ecosystem/planned-services |
skyhub_probe | Planned (stub) | — | would run SkyCore | /ecosystem/planned-services |
skyhub_carrier | Planned (stub) | — | undefined | /ecosystem/planned-services |
Video plane — Janus + WHIP
Janus (skyhub_janus) is the Selective Forwarding Unit built from meetecho’s janus-gateway C source. It exposes a WebSocket API on :8188 (used by WHIP to drive VideoRooms; skyhub_janus/templates/janus.transport.websockets.jcfg), with ICE via Google STUN stun.l.google.com:19302 (set into janus.jcfg via skyhub_janus/Dockerfile:87-88). The Gateway owns rooms: VideoService creates a VideoRoom via JANUS_URL and hands room_id / password / token to the drone over the /video_room_details rosbridge topic. The Dashboard subscribes to the same room over WebRTC (App State & Video).
WHIP (skyhub_whip) is the ingest front door — a Node.js fork of meetecho’s simple-whip-server. It binds the WHIP REST API on :7080 under /whip (skyhub_whip/src/config.js:12), not the 3000 its Dockerfile EXPOSEs — a common source of confusion. A drone or SITL GStreamer pipeline whipsinks its H264 stream to POST /whip/endpoint/<id>, and WHIP maps that ingest to a Janus VideoRoom publisher over ws://127.0.0.1:8188 (JANUS_ADDRESS). See Video Streaming for the on-drone side.
ArUco precision-landing markers are not burned into the video. They travel over Redis
{ip}:aruco_tracking and are rendered as a frontend canvas overlay, so the WebRTC stream stays clean.Network plane — User VPN
skyhub_user_vpn is a Python/Flask manager running with NET_ADMIN in host-network mode. On one host it runs three distinct WireGuard planes (skyhub_user_vpn/docker-compose.yml): core 10.69.0.0/16:51822 (iface wg0), user 10.70.0.0/16:51823 (users0), and drone 10.71.0.0/16:51824 (drones0). It reads the shared PostgreSQL user / drone / user_drone_access tables to generate peer configs (synced to the skyhub-{env}-user-vpn S3 bucket) and iptables allow-rules so a user’s traffic reaches only the drones they own (skyhub_user_vpn/sql_4_iptables.sql). A small status/firewall API is served on :5050 — the Gateway reads it via VPN_SERVICE_IP / VPN_SERVICE_PORT, then resolves a drone’s 10.71.x address in src/middleware/drone_vpn.py to open rosbridge. Details in User VPN & Network Isolation and Network Topology.
Control plane — WS Proxy (parallel to rosbridge)
skyhub_ws_proxy is a FastAPI relay on :7070 (skyhub_ws_proxy/main.py) with two modes:
ws /gamepad/{drone_id}— legacy direct bridge tows://{drone_ip}:5001/gamepad.ws /redispad/{drone_id}— preferred; decouples client from drone via Redis pub/sub: inbound{drone_ip}:gamepad_input, outbound{drone_ip}:outputand{drone_ip}:aruco_tracking.
GET /drone/{drone_id} (id, user_id, ip) and resolves drone IPs from the same PostgreSQL drone table. The Dashboard side is Vehicle Commands & Gamepad; the Redis channel contract is catalogued in Redis Channels & MAVLink Port Map.
Simulation — SITL
skyhub_sitl is a dockerized ArduPilot SITL + ROS2 Humble stack that stands in for a physical drone. Each container runs (via supervisord) sim_vehicle.py, MAVROS over UDP 14550, rosbridge_server on :9090, and a GStreamer→WHIP video node — presenting the exact same rosbridge/topic + WHIP video contract as SkyCore, so the Gateway treats SITL and real drones identically. Containers are named SKYHUB_SITL_<n> with port 9090+n and ROS_DOMAIN_ID=n isolation; WHIP_SERVER_URL defaults to http://172.17.0.1:7080. The Gateway spawns and connects to them via the Docker API on DOCKER_HOST_IP — see SITL Simulator and the orchestration in SITL Drone Lifecycle.
Ground hardware — Nexus, Rover & UGV
Nexus (skyhub_nexus) is the “airhub”: an autonomous battery-swap and charging station. Python drives an Arduino over USB serial (/dev/ttyACM0, skyhub_nexus/main.py:5) to actuate the swap mechanism and controls an IMAX B6 charger over USB (pyusb, imax_usb.py) for LiPo charge/discharge cycles. It is standalone hardware — not wired into the Gateway control plane in-repo; it operates the pad drones dock to.
Rover (skyhub_rover) and UGV (skyhub_ugv) are ESP32 firmware for WaveShare-class ground robots — WiFi AP/STA, ESP-NOW, IMU/servos, and an onboard gamepad control server on :5001. When integrated they are driven through the WS Proxy gamepad channel like a drone, or standalone via their own AP web UI.
All three are covered in Nexus AirHub & Ground Vehicle Firmware.
Planned / stub repos
skyhub_observer, skyhub_probe, and skyhub_carrier are placeholders — README + generic dev-principles only, no source yet. Their intent is inferred from their names: Observer (platform observability / spectator), Prob-E (a hybrid air+ground vehicle that would run SkyCore and rides on the Carrier), and Carrier (a ground transport base for Prob-E). Document them as planned, never as running services — see Planned / Stub Repos.
Port reference
| Port | Service | Protocol | Consumed by |
|---|---|---|---|
5000 | Gateway | HTTP + Socket.IO | Dashboard |
5001 | Drone gamepad server | WebSocket | WS Proxy (direct mode) |
5050 | User VPN status API | HTTP | Gateway |
5432 | PostgreSQL | TCP | Gateway, WS Proxy, User VPN |
6379 | Redis | pub/sub | Gateway, WS Proxy |
7070 | WS Proxy | WebSocket + HTTP | Dashboard |
7080 | WHIP | HTTP /whip | Drone / SITL GStreamer |
8188 | Janus | WebSocket API | WHIP, Dashboard (media) |
9090 | rosbridge | WebSocket | Gateway |
14550 | MAVROS ↔ SITL | UDP (MAVLink) | in-container only |
51822/51823/51824 | User VPN (core/user/drone) | WireGuard | drones, users |
For the transport-level view of every real-time channel (Socket.IO, rosbridge, WebRTC, Redis), see Real-time Transport Channels. For the big-picture architecture, see Platform Architecture Overview.

