HydraSRT Architecture
July 17, 2026 ยท View on GitHub
Overview
HydraSRT has three runtime layers:
- Elixir/OTP - route supervision, route state, configuration, REST API, WebSocket updates.
- Rust + GStreamer - native media pipelines, one OS process per running route.
- React + Vite - web UI.
Control Plane
The Elixir layer owns route lifecycle and state:
- Each active route has a
RoutesSupervisor. - Each
RoutesSupervisorstarts oneRouteHandler. RouteHandleris a:gen_statemprocess that starts, monitors, restarts, and stops the native pipeline process.- Route configuration and runtime state are stored in SQLite.
- Status changes, metrics, and logs are published through Phoenix/PubSub and exposed by the API/UI.
Media Plane
Each route's media work runs outside the BEAM:
- The native binary is built from
native/intopriv/native/hydra_srt_pipeline. - Elixir starts it as an Erlang Port / OS process.
- GStreamer handles stream processing.
- A native crash affects the route process that owns that pipeline, not the whole BEAM VM.
Process Architecture
Supervision Tree
flowchart TD
App[HydraSrt.Application]
App --> Telemetry[Telemetry]
App --> PromEx[PromEx]
App --> Repo[Ecto Repo SQLite]
App --> Victoria[Victoria HTTP Clients]
App --> PubSub[Phoenix.PubSub]
App --> Endpoint[Phoenix Endpoint]
App --> DynSup[PartitionSupervisor]
App --> StatsCollector[Stats.Collector]
App --> EventLogger[Stats.EventLogger]
App --> PipelineLogger[Stats.PipelineLogger]
DynSup --> RouteSup1[RoutesSupervisor route_1]
DynSup --> RouteSup2[RoutesSupervisor route_2]
DynSup --> RouteSup3[RoutesSupervisor route_N]
RouteSup1 --> Handler1[RouteHandler route_1]
RouteSup2 --> Handler2[RouteHandler route_2]
RouteSup3 --> Handler3[RouteHandler route_N]
Handler1 -.->|manages OS process| Pipeline1[Rust Pipeline OS Process]
Handler2 -.->|manages OS process| Pipeline2[Rust Pipeline OS Process]
Handler3 -.->|manages OS process| Pipeline3[Rust Pipeline OS Process]
Supervision Strategy
HydraSrt.Application: root supervisor,:one_for_one.PartitionSupervisor: starts dynamic route supervisors.HydraSrt.RoutesSupervisor: one supervisor per active route.HydraSrt.RouteHandler: route state machine; owns one native pipeline process.
Route Lifecycle
- User enables a route via UI/API.
HydraSrt.start_route/1is calledPartitionSupervisorstarts aRoutesSupervisor.RoutesSupervisorstarts aRouteHandler.RouteHandlerstarts the Rust pipeline as an OS process.RouteHandlerreads pipeline stdout/stderr and monitors process exit.- On pipeline failure,
RouteHandlerrestarts or switches source depending on route state. - User disables the route;
RouteHandlerterminates the pipeline.
Inter-Process Communication
Elixir <-> Rust Pipeline
Communication uses Erlang Ports:
- Start/config: Elixir starts the pipeline with command-line arguments.
- Logs/status: the pipeline writes structured lines to stdout/stderr;
RouteHandlerparses them. - Exit:
RouteHandlerreceives port exit events.
The native pipeline remains a standalone binary. It does not run inside the BEAM.
Native Process Boundary
Media processing runs outside the BEAM. A native crash should terminate the route pipeline process, not the Elixir VM.
Data Layer
SQLite (Ecto)
Purpose: Configuration state and operational data.
Used for:
- Route definitions (sources, destinations, SRT parameters, RTP-over-UDP source options)
- RTP sources expect MPEG-TS over RTP (MP2T payload via
rtpmp2tdepay) - User authentication and sessions
- Route enable/disable state
- Active source tracking (for failover)
Storage notes:
- Single file.
- No external database service.
- ACID transactions for configuration changes.
Location: DATABASE_PATH env var, default hydra_srt.db
VictoriaMetrics and VictoriaLogs
Purpose: Historical observability storage outside the BEAM runtime.
Used for:
- System metrics history
- Network interface statistics
- Route performance metrics
- Route events and status history
- Pipeline logs history
Storage notes:
- VictoriaMetrics stores numeric time-series data and route events.
- VictoriaLogs stores structured pipeline log lines.
- Both services are external HTTP processes, so storage failures degrade analytics without crashing HydraSRT.
Location: VICTORIA_METRICS_URL and VICTORIA_LOGS_URL, defaulting to loopback services in local deployments.
Metrics Collection and Export
HydraSRT exposes current metrics through Prometheus and writes historical metrics to VictoriaMetrics. Pipeline logs are written to VictoriaLogs.
Collection Path
flowchart LR
OsMon[OsMon Plugin] -->|emits| Telemetry[Telemetry Events]
RouteHandler[RouteHandler] -->|parses logs| PubSub[Phoenix PubSub]
PubSub --> PipelineLogger[Stats.PipelineLogger]
Telemetry --> SystemCollector[Stats.SystemTelemetryCollector]
SystemCollector -->|writes samples| VM[VictoriaMetrics]
EventLogger[Stats.EventLogger] -->|writes events| VM
PipelineLogger -->|buffers logs| VL[VictoriaLogs]
PipelineLogger -->|emits| TelemetryMetrics[Telemetry Metrics]
Telemetry --> PromEx[PromEx]
TelemetryMetrics --> PromEx
PromEx --> Prometheus[/metrics endpoint]
Metric Types
System Metrics (collected by HydraSrt.PromEx.Plugins.OsMon):
- CPU utilization and load average
- RAM and swap usage
- Network interface statistics (per interface)
Pipeline Metrics (from GStreamer debug logs):
- Log lines processed, dropped, unparsed
- Per-route, per-level counters
Network Metrics (per interface):
- Collected by
HydraSrt.Monitoring.NetIf - Linux: reads
/sys/class/net/*/statistics/* - macOS/FreeBSD: parses
netstat -i -b -noutput - Includes rx/tx bytes, packets, errors, drops
- Both absolute counters (
_total) and computed rates (_per_sec)
Prometheus Export
Metrics are exposed at /metrics in Prometheus text format via HydraSrt.PromEx.
Optional bearer token authentication: set METRICS_SECRET env var.
Poll interval controlled by PROM_POLL_RATE (default 5000ms).
Historical Analytics
Selected telemetry is stored in VictoriaMetrics for historical queries.
API endpoint: GET /api/nodes/:id/analytics
Supports:
- Time range filtering
- Automatic downsampling (controlled by
max_pointsparameter) - Bucket sizes: 10s, 30s, 1m, 5m, 15m, 30m, 1h
The UI uses this endpoint for CPU, RAM, network, and load average charts.
Failover Architecture
HydraSRT supports source failover with a primary + N backup sources per route.
Failover Implementation
Failover is handled by RouteHandler, not by hot-swapping inputs inside GStreamer:
RouteHandlermonitors pipeline output for bitrate and connection status- When active source fails, handler detects it (zero bitrate, connection errors)
- Handler terminates the current pipeline process
- Handler selects the next available source (based on failover mode)
- Handler spawns a new pipeline process with the new source
- Database is updated with new
active_source_id
Pipeline Restart
Hot-swapping sources inside one running GStreamer pipeline adds failure modes:
- GStreamer state transitions are tricky
- Risk of memory leaks or partial state
- More state to recover after a failed switch
Restarting starts a new pipeline with a known config. A short output interruption is expected.
Failover Modes
active: Automatically fail over to backup, then probe primary source in background. Return to primary when stable.passive: Fail over to backup only when current source fails. No automatic return to primary.disabled: No automatic failover. Source switch must be manual.
React UI Layer
The web application lives in web_app/:
- Vite
- React 18
- Ant Design
- TanStack Router
Communication with Backend
- REST API: All CRUD operations (routes, sources, destinations)
- WebSocket (Phoenix Channels): Real-time updates for route status, metrics, logs
- Authentication: Credential-based login with token sessions persisted in SQLite
Development Mode
In development (make dev), Phoenix starts the Vite dev server automatically:
- Vite runs on port 5173
- Phoenix proxies requests to
/assets/*to Vite for HMR (Hot Module Replacement) - API requests go directly to Phoenix on port 4000
Production Build
For production (MIX_ENV=prod mix release):
npm run buildcompiles React app to static assets- Assets are copied to
priv/static/ - Phoenix serves them directly (no separate Vite process)
Code Organization
Use existing boundaries when adding code:
lib/hydra_srt/- control-plane code: route lifecycle, DB access, stats, monitoring, and native process management.lib/hydra_srt_web/- Phoenix boundary: controllers, channels, router, endpoint, auth plugs.native/- Rust/GStreamer pipeline binary. Add media-processing changes here, not in Elixir.web_app/- React UI. Add dashboard, forms, charts, and client-side API calls here.priv/repo/- SQLite migrations and seed data.test/- Elixir unit/integration tests and E2E coverage.web_app/src/**/*.test.*andweb_app/tests/- UI unit and browser tests.
References
- development.md - Setup and deployment guide
- Elixir Documentation
- OTP Design Principles
- GStreamer Documentation