System architecture
Joystick is built on a modern microservices architecture, with each service handling specific responsibilities. All services communicate through a unified network layer orchestrated by Docker Compose and Traefik.Architecture diagram
Core services
Traefik
Role: Reverse proxy and load balancer Ports: 80 (HTTP), 8080 (Dashboard) Responsibilities:- Routes incoming requests to appropriate services based on path prefixes
- Handles CORS headers for cross-origin requests
- Provides service discovery through Docker labels
- Exposes dashboard for monitoring routes and backends
Access the Traefik dashboard at
http://localhost:8080 to view active routes and service health.PocketBase
Role: Database, authentication, and real-time subscriptions Port: 8090 Responsibilities:- User authentication with JWT tokens
- SQLite database for all application data
- Real-time subscriptions via WebSocket
- Admin dashboard for data management
- Automated migrations and hooks
- Device access control and permissions
users: User accounts and authenticationdevices: Device configurations and connection detailsactions: Available device actions and parameterspermissions: Feature-based access controllogs: Action execution historysensors: GPS, IMU, battery, and signal data
PocketBase includes a health check endpoint at
/api/health. All dependent services wait for PocketBase to be healthy before starting.MediaMTX
Role: Video streaming server Network: Host mode (uses ports 8554, 8888, 9997) Responsibilities:- RTSP stream ingestion from devices
- WebRTC streaming to browsers
- HLS streaming support
- Stream management API
- Automatic stream health monitoring
- RTSP (port 8554): Input from devices
- WebRTC: Browser-based viewing
- HLS (port 8888): HTTP Live Streaming
- API (port 9997): Stream management
App
Role: Web interface Internal port: 80 (exposed via Traefik at/)
Technology stack:
- React 19 with TypeScript
- Vite for build and dev server
- TanStack Query for data fetching
- Zustand for state management
- Tailwind CSS for styling
- React Router for navigation
- Device management interface
- Real-time video streaming
- Action execution and logging
- Sensor data visualization
- User authentication
- Permission management
Joystick
Role: Core device control API Port: 8000 (exposed via Traefik at/joystick)
Framework: Elysia (Bun-based web framework)
Responsibilities:
- Device action execution
- Authentication and authorization
- Sensor data collection
- Device health monitoring
- Notification management
- API documentation via Swagger
POST /api/run/:device/:action- Execute device actionGET /api/ping/:device- Check device connectivityGET /api/cpsi- Cellular signal dataGET /api/battery- Battery statusGET /api/gps- GPS coordinatesGET /api/imu- IMU sensor dataPOST /api/notifications/send- Send push notifications
- JWT Bearer token (production)
- Query parameter token
- X-API-Key header (development)
The Swagger documentation is available at
http://localhost/joystick/swagger with interactive API testing.Switcher
Role: Automatic slot switching and health monitoring Port: 8080 (exposed via Traefik at/switcher)
Responsibilities:
- Health check execution every 30 seconds
- Automatic failover between primary and secondary slots
- Slot health status tracking
- Manual slot switching API
- Failure count monitoring
- Query devices with
autoSlotSwitch=true - Execute
slot-checkaction on active slot - Track consecutive failures
- Switch to alternate slot after 2 failures
- Update device
activeSlotfield - Trigger stream URL updates via PocketBase hooks
POST /api/slot/:deviceId/:slot- Manual slot switchGET /api/health/:deviceId?- Get health statusPOST /api/health/check- Trigger health check
The Switcher service ensures 24/7 uptime by automatically switching to healthy backup connections when the primary fails.
Panel
Role: Device status dashboard Port: 4000 (exposed via Traefik at/panel)
Responsibilities:
- Real-time device status monitoring
- Aggregated sensor data views
- System health dashboards
- Alert management interface
Baker
Role: Stream management and video processing Port: 3000 (exposed via Traefik at/baker)
Responsibilities:
- Stream URL generation and management
- Video stream health monitoring
- Stream metadata processing
- Integration with MediaMTX API
Studio
Role: Advanced video compositing and layouts Port: 8001 (exposed via Traefik at/studio)
Responsibilities:
- Multi-stream video compositing
- Custom layout generation
- Video effects and overlays
- Scene management
Whisper
Role: Audio processing and communication Port: 8081 (exposed via Traefik at/whisper)
Responsibilities:
- Audio stream processing
- Voice communication handling
- Audio analytics
Dozzle
Role: Web-based log viewer Port: 8084 Responsibilities:- Real-time container log streaming
- Multi-container log aggregation
- Log search and filtering
- Container management interface
Network architecture
App network
All services (except MediaMTX) communicate through a bridge network:- Custom bridge network for service isolation
- DNS resolution by service name
- CORS enabled on all HTTP services
- Extra hosts for
host.docker.internalaccess
Host network access
Services access the host network using:Data flow
Device action execution
Video streaming flow
Automatic slot switching flow
Volumes and persistence
PocketBase data volume:- SQLite database files
- User uploads and attachments
- Migration history
Logging and monitoring
Log configuration
All services use JSON file logging with rotation:Monitoring tools
-
Traefik Dashboard (
http://localhost:8080)- Service routing status
- Backend health
- Request metrics
-
Dozzle (
http://localhost:8084)- Real-time log streaming
- Multi-container views
- Log search and filtering
-
PocketBase Admin (
http://localhost:8090/_/)- Database records
- API logs
- Collection management
-
Swagger UI (
http://localhost/joystick/swagger)- API endpoint testing
- Request/response inspection
- Authentication testing
Health checks
PocketBase health check:Security considerations
Security features:- JWT-based authentication
- Feature-based permission system
- Device-level access control
- API key authentication for inter-service communication
- CORS configuration for cross-origin requests
- Isolated Docker network
Scaling considerations
For production deployments:- Database: Consider migrating from SQLite to PostgreSQL for concurrent writes
- MediaMTX: Deploy separate instances for different regions
- Load balancing: Use Traefik’s built-in load balancing for multiple service replicas
- Monitoring: Add Prometheus and Grafana for metrics collection
- Logging: Integrate with centralized logging (ELK stack, Loki)
All services are stateless (except PocketBase) and can be horizontally scaled by running multiple replicas.
Next steps
Authentication
Learn about JWT tokens and API keys
Device control
Master device actions and monitoring
Slot switching
Configure automatic failover
API reference
Explore the complete API documentation