Skip to content

Architecture and Ports

Nik Pottbecker edited this page Oct 12, 2026 · 1 revision

Architecture and ports

Data flow

flowchart LR
    I[iPhone / Moblin] -->|Authenticated SRT or RTMP over Tailscale| M[MediaMTX]
    M -->|SRT or RTSP| O[OBS / VLC / FFmpeg]
    M -->|LL-HLS or WebRTC| B[Browser preview]
    M -->|Optional segments| R[Private recording directory]
    D[Dashboard] -->|Local API 127.0.0.1:9997| M
    D --> H[Connection history]
    D --> S[Aggregate storage telemetry]
Loading

Components

Tailscale

Provides encrypted device-to-device reachability. The software obtains the active IPv4 address from Tailscale and validates membership in 100.64.0.0/10 before starting.

MediaMTX

Receives the publisher, enforces role-based authentication, exposes receiver protocols, remuxes LL-HLS/WebRTC and optionally writes recording segments. It does not perform video transcoding.

Dashboard

A dependency-free Python HTTP service that:

  • Builds device-specific authenticated URLs
  • Generates the Moblin import profile
  • Reads MediaMTX status from localhost
  • Calculates browser-side bitrate
  • Persists state transitions
  • Reports aggregate disk/recording usage

Native service manager

  • macOS: two per-user LaunchAgents
  • Linux: two hardened systemd services

Listener map

Component Bind address Protocol Port Exposure
SRT Tailscale IPv4 UDP 8890 Tailnet
RTSP Tailscale IPv4 TCP 8554 Tailnet
RTMP Tailscale IPv4 TCP 1935 Tailnet
LL-HLS Tailscale IPv4 TCP 8888 Tailnet
WebRTC signaling/HTTP Tailscale IPv4 TCP 8889 Tailnet
WebRTC media Tailscale IPv4 UDP 8189 Tailnet
Dashboard Tailscale IPv4 TCP 8090 Tailnet
MediaMTX API 127.0.0.1 TCP 9997 Server localhost only
MediaMTX metrics 127.0.0.1 TCP 9998 Server localhost only

RTSP is restricted to TCP transport. Media over these ports should never be exposed through public router forwarding or tunnels.

Authentication model

Identity Permission Path
mobile Publish live
studio Read + playback live
Localhost pseudo-user API + metrics Local control endpoints

Random publisher and reader passwords are generated independently. The rendered MediaMTX configuration receives SHA-256 hashes rather than plaintext passwords.

Persistent data

Data Purpose Contains secrets?
credentials.env Publisher/reader passwords Yes
profile.env Moblin import settings No
recording.env Recording settings No
connection-history.json Event types, timestamps, durations No
recordings/ Optional media segments Media content
mediamtx.yml Rendered runtime configuration Password hashes

Key design decisions

  • Direct Tailscale binding: prevents accidental all-interface exposure.
  • Separate publisher/reader secrets: limits the effect of a leaked receiver URL.
  • Local control API: remote users cannot invoke MediaMTX administrative endpoints.
  • Native services: fewer moving parts than a container runtime for a small relay.
  • No transcoding: lower CPU use and latency; sender codec is preserved.
  • Bounded history and cached storage scans: operational visibility without a database or constant disk traversal.

Non-goals

  • Public ingest or playback
  • Video mixing, graphics or transcoding
  • SRTLA/multi-interface bonding
  • Multiple independent stream paths
  • Automatic cloud upload
  • User accounts for the dashboard

Clone this wiki locally