/api/v1 prefix, plus a single Socket.IO namespace for telemetry. This page is the map of that surface — what the gateway does, how it is structured, and where new logic belongs. Deep-dives on each area live in the sibling pages linked throughout.
What the gateway is responsible for
Auth & accounts
Login/refresh/register/activate, JWT issuance, users, VPN config, and Stripe billing.
Drone control
Drone CRUD plus ~20
/drone/action/* MAVROS commands (arm, takeoff, modes, params, missions, geofences).Real-time telemetry
Socket.IO rooms fed by pooled rosbridge connections, with server-side dashboard yaw computation.
SITL orchestration
Spawns and tears down the 3-container SITL stack over local or remote Docker.
Media & flight data
Janus video rooms, S3 assets/HLS, execution tracking, log analysis, archives, and email reports.
Integrations
Stripe webhooks, AWS ECR/STS bootstrap for drone self-update, and Isaac Sim EC2 control.
Tech stack
| Concern | Technology | Notes |
|---|---|---|
| Language | Python 3.10.12 | |
| Web framework | Flask 3.0.2 | Blueprint-based routing |
| Real-time | Flask-SocketIO 5.3.6 | async_mode="gevent"; JWT handshake via ?token= |
| WSGI server | gunicorn 22.0.0 + gevent-websocket | GeventWebSocketWorker, 1 worker / 8 threads, binds main:app |
| Auth | Flask-JWT-Extended 4.5.3, Flask-Bcrypt | HS256 access (10 min) + refresh (12 hr) tokens |
| API docs | flasgger 0.9.7.1 | Swagger 2.0 UI at /api/docs/, spec at /apispec.json |
| Persistence | Flask-SQLAlchemy + Flask-Migrate (Alembic) | PostgreSQL |
| Cloud | boto3 1.35.10 | S3, ECR, STS, SSM, EC2 |
| Billing | stripe SDK | Checkout, portal, signature-verified webhooks |
| Observability | OpenTelemetry (OTLP) | Traces/logs to SigNoz — see OpenTelemetry & SigNoz |
Dockerfile:
Dockerfile
The default deployment runs a single gunicorn worker. Two pieces of state are process-local as a result: the JWT logout
BLOCKLIST (an in-memory set() in src/routes/auth_routes.py) and, if you scale to multiple workers, Socket.IO room fan-out — which is why SOCKETIO_MESSAGE_QUEUE (an optional Redis URL) exists. See Authentication & JWT Lifecycle.The API surface
The gateway exposes a Swagger 2.0 spec generated by flasgger. Browse it live atGET /api/docs/ (raw spec at GET /apispec.json); both are public.
Every route is registered under url_prefix="/api/v1/" in src/main.py:222-233, and each rule itself begins with /, so the effective base path is /api/v1. The Socket.IO handlers are wired separately (src/main.py:240) and are not a blueprint.
12 blueprints
Blueprints are registered insrc/main.py; each imports its collaborating service singleton from src/application/app.py and stays thin. Endpoint counts below are the @*_routes.route(...) decorators in each file (113 total).
| Blueprint | File | Endpoints | Primary auth model |
|---|---|---|---|
auth_routes | src/routes/auth_routes.py | 9 | Public (login/register) + JWT + activation token-in-path |
drone_routes | src/routes/drone_routes.py | 30 | JWT; /drone/activate (10-digit header token), /drone/pull (VPN IP) |
execution_routes | src/routes/execution_routes.py | 15 | Mixed: VPN-IP drone callbacks + JWT UI reads |
geofence_routes | src/routes/geofence_routes.py | 11 | JWT |
billing_routes | src/routes/billing_routes.py | 10 | JWT + public pricing + Stripe-Signature webhook |
mission_routes | src/routes/mission_routes.py | 9 | JWT |
asset_routes | src/routes/asset_routes.py | 8 | Mixed: VPN-IP upload callbacks + JWT reads |
calendar_routes | src/routes/calendar_routes.py | 7 | JWT |
vpn_routes | src/routes/vpn_routes.py | 5 | JWT |
video_room_routes | src/routes/video_room_routes.py | 4 | JWT; create authed by drone video-room token |
isaac_sim_routes | src/routes/isaac_sim_routes.py | 4 | JWT |
user_routes | src/routes/user_routes.py | 1 | JWT |
Three coexisting auth models
The gateway serves three different callers, each trusted by a different mechanism. This is intentional — the same rosbridge/topic contract makes SITL and physical drones interchangeable, but the callers are not.- JWT Bearer — the Dashboard. Validated by Flask-JWT-Extended (HS256). See Authentication & JWT Lifecycle.
- VPN source IP — physical drone and gamepad callbacks. The
check_vpn_ipdecorator (src/middleware/drone_vpn.py) trusts a request whoseremote_addr/X-Real-IP/X-Forwarded-Forstarts with10.71.(the drone WireGuard subnet), or anX-Drone-IPheader naming aSKYHUB_SITL_*container whenENABLE_SITLis set. It setsrequest.vpn_ipand returns400otherwise. These endpoints carry no JWT. See VPN IP Authentication & Jumphost Routing. - Token / signature — the 10-digit drone activation token header, the per-drone video-room bearer token, and the
Stripe-Signatureheader onPOST /billing/webhook.
Design: thin routes, service delegation
Route handlers do the boring parts — parse the request, authorize, and translate a service result into an HTTP response — then delegate all business logic to a service singleton. Every service is instantiated once at import time in the composition rootsrc/application/app.py and imported by the routes that need it. A representative handler:
src/routes/user_routes.py
Response envelope
Newer routes use the helpers insrc/utils/common_helper.py:
get_success_response(data, message, status_code)→{"success": true, "data": ..., "message": ...}(common_helper.py:4). Omitdatato drop the key; passdata=Noneto include an explicit null.get_error_response(code, message)→({"success": false, "error": {"code": code, "message": message}}, code)(common_helper.py:24).
Where things live (src/ map)
| Path | Role |
|---|---|
src/main.py | App factory / gunicorn entrypoint: registers the 12 blueprints, Swagger, CORS, JWTManager, Bcrypt, SocketIO, and wires socket routes |
src/application/app.py | Composition root — service singletons, the get_service(drone_type) factory, and get_vpn_service() lazy init |
src/application/settings.py | Environment configuration (see Gateway Environment Variables) |
src/routes/ | HTTP blueprints + socket_routes.py (Socket.IO telemetry handlers) |
src/service/ | Business-logic tier (see Service Layer & Factory) |
src/models/ | SQLAlchemy ORM models (User, Drone, Mission, Asset, MissionExecution, Subscription, Geofence, Calendar…) |
src/rosbridge/ | Drone WebSocket connection management: connection.py, request_format.py, socket.py |
src/middleware/ | drone_vpn.py — the check_vpn_ip decorator |
src/utils/ | common_helper.py, drone_types.py, mavros_topics.py, drone_utils.py, email_templates.py |
src/connector/ | db_connection.py — DB/SQLAlchemy wiring |
src/diagnostic/ | logs.py — logging + OpenTelemetry log configuration |
src/drone/ | messages.py — drone message helpers |
src/migrator.py | Alembic migration helper (see Migrations & DB Connection) |
Deciding where new backend logic belongs
Route handler — request plumbing only
Parse and validate input, run the auth decorator (
@jwt_required() or @check_vpn_ip), call one service, and shape the response with get_success_response / get_error_response. Keep it thin.Service — the business logic
Add or extend a class in
src/service/. Drone operations should go through DroneControlService (rosbridge dispatch) or a DroneService subclass obtained via get_service(drone_type). See Service Layer & Factory and DroneControlService & Rosbridge Dispatch.Wire the singleton
Instantiate any new service once in
src/application/app.py and import it from the route module — do not construct services inside request handlers.Explore the gateway
HTTP API Overview & Auth Models
Base path, the 12 blueprints, response envelope, and the three auth models in depth.
Drone Management & Control Actions
Drone CRUD and the ~20
/drone/action/* MAVROS commands.Socket.IO Telemetry Streaming
Handshake, room subscriptions, stream types, and the dashboard fan-out.
Executions, Assets & Reports
VPN-IP drone callbacks, upload flow, log analysis, archives, and reports.
Missions & Geofences API
Mission and geofence CRUD plus MAVLink waypoint format.
Billing, Calendar, VPN, Video & Isaac Sim
Stripe endpoints, scheduling, VPN config, video rooms, and Isaac Sim control.
Service Layer & get_service Factory
How services are wired as singletons and the physical/SITL factory.
Rosbridge Connection & Reconnect
The persistent drone WebSocket, reconnect, and subscription tracking.

