System Architecture
Start With the Model
Section titled “Start With the Model”SyncTV is a single-binary service. One process contains HTTP APIs, public gRPC, WebSocket realtime rooms, management gRPC, media providers, proxying, livestreaming, and cluster coordination.
Start with the single-node production model: clients reach SyncTV through a TLS reverse proxy or Ingress, SyncTV connects to PostgreSQL, and Redis is recommended for shared short-lived state.
PostgreSQL is durable business state. Redis is shared short-lived state and coordination. data_dir is the local runtime file directory. When you need horizontal scaling, add multiple SyncTV replicas, shared PostgreSQL/Redis, a unified edge entrypoint, and an explicit HLS storage or publisher-node proxy model.
Deployment Levels
Section titled “Deployment Levels”| Level | Use case | Required components | Extra attention |
|---|---|---|---|
| Single-node production | Small team or one server | SyncTV, PostgreSQL, production secrets | Redis is recommended, backups are required |
| Multi-replica production | Horizontal scaling, rolling updates, highly available entrypoints | Multiple SyncTV instances, PostgreSQL, Redis, cluster secret | Split HTTP/gRPC Ingresses and choose an HLS model |
Major Components
Section titled “Major Components”- API and realtime: HTTP REST, public gRPC, WebSocket, and room realtime events share the same business services and permission model.
- Authentication: Password, OPAQUE, passkey/WebAuthn, email codes, OAuth2, user-level 2FA, and JWT tokens make up the login layer.
- Media: Providers resolve external media, the proxy performs controlled forwarding, and slice cache stores Range slices only.
- Horizontal scaling: Multi-node deployments use Redis, discovery, leader election, and transactional outbox delivery boundaries.
Runtime Surfaces
Section titled “Runtime Surfaces”| Surface | Default port or path | Purpose | Production guidance |
|---|---|---|---|
| HTTP REST | server.port=8080 | Client API and OpenAPI UI | Expose through a reverse proxy or Ingress |
| Health | health.port=8081 | Liveness and readiness probes | Keep private to the cluster or local host |
| Public gRPC | server.port=8080 | gRPC API for clients or SDKs | Use a separate Kubernetes Service and Ingress |
| WebSocket | server.port=8080 | Room realtime events, playback sync, chat | Configure connection limits and shutdown drain |
| Management gRPC | Unix socket or management.port=50052 | CLI, administration, operational control plane | Prefer Unix socket; TCP requires a token |
| Metrics | metrics.port=9090 | Prometheus metrics | Keep private and authenticated |
| RTMP | livestream.rtmp_port=1935 | Livestream publishing | Expose only when livestreaming is used |
| STUN UDP | webrtc.stun_port=3478 | WebRTC NAT assistance | Expose only when built-in STUN is used |
HTTP REST and public gRPC share the main process port, but Kubernetes deployments should still use separate Services and Ingresses. This lets the gRPC Ingress set nginx.ingress.kubernetes.io/backend-protocol: "GRPC" independently.
State and Storage
Section titled “State and Storage”PostgreSQL is the durable system of record. Users, rooms, permissions, provider instances, user preferences, audit data, and business records live there.
Production requirements: startup runs embedded SQLx migrations automatically; maintain backup and restore procedures; keep connection pool sizing within database capacity; treat database migrations as rollback-sensitive.
Redis stores shared short-lived state and distributed coordination data. Single-node deployments can run without Redis, but production and multi-replica deployments should configure it.
Redis affects rate limits, brute-force protection, token blacklist, OAuth2 state, WebAuthn challenges, email verification state, L2 cache, cluster node registry, and event catch-up.
Redis is usually not a long-term backup target, but restarting or clearing it affects in-flight authentication, rate windows, and cluster short-term state.
data_dir is the root directory for runtime-owned local files.
Common contents include the management Unix socket, file logs, HLS files, and file-backed proxy slice cache.
Static input files such as *_file secrets and TLS certificates are not rebased under data_dir.
Request Flows
Section titled “Request Flows”API Requests
Section titled “API Requests”- A client calls SyncTV through HTTP or gRPC.
- The API layer performs authentication, rate limiting, permission checks, and request parsing.
- Services read and write PostgreSQL, using Redis and L1 caches when configured.
- After transactional business changes commit, realtime changes are pushed to local WebSocket connections. In multi-replica mode, database outbox records and shared event channels deliver the event to other nodes. Some membership or runtime events use best-effort fanout and do not share the same database transaction boundary.
Media Provider and Proxy Requests
Section titled “Media Provider and Proxy Requests”- A provider resolves a media URL and decides whether the client should fetch directly or through the proxy.
- The provider explicitly selects upstream headers such as
User-Agent,Referer,Range, or authentication headers. - The proxy layer uses provider-supplied headers only. It does not forward raw client headers by itself.
- Slice cache handles Range-capable upstreams only. If an upstream does not support Range, the proxy bypasses caching and does not store full-body responses.
Cluster Model
Section titled “Cluster Model”Cluster mode is for multi-replica deployments. When cluster.enabled=true, Redis and cluster.secret are required.
Key settings:
| Setting | Purpose |
|---|---|
cluster.discovery_mode | Node discovery: redis, static, or k8s_dns |
cluster.leader_election_mode | Background task leader election: redis or k8s_lease |
server.advertise_host | Address other nodes use to reach this node |
cluster.secret | Authentication for inter-node gRPC calls |
cluster.catchup_window_secs | Event replay window after short disconnects |
For multi-replica deployments, also confirm:
- All replicas share the same PostgreSQL database.
- All replicas share the same Redis and
redis.key_prefix. - WebSocket tickets must be cross-node capable, not node-local memory only.
- Local HLS backends use publisher-node proxying.
shared_fileserves TS segments from the current node’s shared mount, and S3 provides object-storage-backed segment storage. - Kubernetes termination grace period is longer than
server.shutdown_drain_timeout_seconds.
For the full runtime design, discovery modes, leader election, and clustered livestreaming boundaries, see Cluster Configuration.
Livestream Model
Section titled “Livestream Model”The livestream path is separate from the on-demand proxy path. RTMP is the publishing entrypoint; HTTP-FLV serves low-latency playback; HLS remuxing produces playlists and segments into the selected memory, file, or s3 backend. In multi-replica deployments, the publisher owner is exposed through a shared registry; today non-publisher nodes read playlists and segments through the HLS gRPC proxy to the publisher node. Shared filesystem storage or S3 is a storage and recovery boundary, not a signal that non-publisher nodes bypass the publisher and read segments directly.
For the RTMP/StreamHub/HTTP-FLV/HLS pipeline, backend selection, and clustered livestreaming failure boundaries, see Livestream Configuration.
Production Topologies
Section titled “Production Topologies”Kubernetes multi-replica topology: