mirror of
git://f0xx.org/android_cast
synced 2026-07-29 07:58:10 +03:00
395 lines
16 KiB
Markdown
395 lines
16 KiB
Markdown
# Media service (SFU / MCU) — AI deployment prerequisites
|
||
|
||
**Document type:** Requirements / handoff (pre-SPEC)
|
||
**Date:** 2026-06-12
|
||
**Audience:** PO / sysop → AI agent for full deploy + tune on fresh Alpine HVM(s)
|
||
**Related:** [docs/specs/20100612_1_scaling.md](../docs/specs/20100612_1_scaling.md), [docs/DRs/20100612_1_scaling.md](../docs/DRs/20100612_1_scaling.md), [examples/sfu/README.md](../examples/sfu/README.md), [docs/20260608_BE_SERVICES_and_infra.md](../20260608_BE_SERVICES_and_infra.md) §SFU
|
||
**PDF:** `tmp/20260612-MEDIASERVICE-AI-requirements.pdf` — regenerate with `.venv-pdf/bin/python docs/_pdf_build/md_to_pdf.py` on the copy under `tmp/`
|
||
**Code names (product):** **SPIDERWEB** = SFU-alike, **SPIDER** = MCU-alike ([draft glossary](../docs/drafts/20100611_1_net_topology_glossary.txt))
|
||
|
||
---
|
||
|
||
---
|
||
|
||
---
|
||
|
||
## Table of contents
|
||
|
||
<!-- toc -->
|
||
- [1. Executive answer — HVM vs Docker vs monolith BE](#1-executive-answer-hvm-vs-docker-vs-monolith-be)
|
||
- [2. What you give the AI on day zero](#2-what-you-give-the-ai-on-day-zero)
|
||
- [3. Recommended instance layout (up to 3 HVMs)](#3-recommended-instance-layout-up-to-3-hvms)
|
||
- [4. Network, DNS, and firewall prerequisites](#4-network-dns-and-firewall-prerequisites)
|
||
- [5. Integration with existing BE (isolation model)](#5-integration-with-existing-be-isolation-model)
|
||
- [6. SFU vs MCU stack choice](#6-sfu-vs-mcu-stack-choice)
|
||
- [7. Packages and software (installed later by AI)](#7-packages-and-software-installed-later-by-ai)
|
||
- [8. Sizing and bandwidth (200/200 Mbit context)](#8-sizing-and-bandwidth-200200-mbit-context)
|
||
- [9. AI workflow after prerequisites are met](#9-ai-workflow-after-prerequisites-are-met)
|
||
- [10. Out of scope for first media PoP](#10-out-of-scope-for-first-media-pop)
|
||
- [11. PO checklist (copy before handoff)](#11-po-checklist-copy-before-handoff)
|
||
- [12. Changelog](#12-changelog)
|
||
<!-- /toc -->
|
||
|
||
---
|
||
|
||
## 1. Executive answer — HVM vs Docker vs monolith BE
|
||
|
||
### Recommendation (DR-aligned)
|
||
|
||
| Approach | Verdict | Why |
|
||
|----------|---------|-----|
|
||
| **SFU/MCU on current androidcast BE monolith** | **No** | PHP/MariaDB/RSSH/Gitea share CPU/RAM/NIC; `pm.max_children=5` today; blast radius; [INFRA.md](../docs/INFRA.md) keeps `:8089` as **other** Janus stack |
|
||
| **Fresh dedicated Alpine HVM(s)** | **Yes (primary)** | Isolated **media plane**; snapshots; UDP/WebRTC without touching hub/crashes |
|
||
| **Docker on dedicated HVM** | **Yes (preferred delivery)** | Reproducible Janus/mediasoup/coturn/ffmpeg; easy rollback; same host still “empty” at handoff |
|
||
| **Docker on existing BE** | **No** | Does not isolate bandwidth or failure domain |
|
||
|
||
**Practical model:** PO provides **1–3 empty Alpine HVMs** with SSH + DNS. AI installs **Docker (+ compose)** on them and runs **SPIDERWEB (SFU)** or **SPIDER (MCU)** stacks in containers. **Control plane** (signaling auth, room lifecycle, RBAC) talks to existing crashes BE via **HTTPS/OpenAPI-style APIs** — no co-location of MariaDB or PHP-FPM with media.
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph Today["Today — monolith BE (unchanged)"]
|
||
FE["Gentoo FE apps.f0xx.org"]
|
||
BE["Alpine BE :80<br/>PHP MariaDB Gitea RSSH"]
|
||
end
|
||
|
||
subgraph New["New — media PoP (1–3 HVMs)"]
|
||
MED["media-1 Alpine HVM"]
|
||
DOCK["Docker: SFU + TURN + signaling sidecar"]
|
||
end
|
||
|
||
MOB["Android app"] --> FE
|
||
FE --> BE
|
||
MOB -.->|"WebRTC UDP/TCP<br/>post-alpha"| MED
|
||
DOCK --> MED
|
||
BE -.->|"REST: token room RBAC<br/>OpenAPI"| DOCK
|
||
|
||
class New fill:#e8f5e9,stroke:#2e7d32
|
||
```
|
||
|
||
**Not too heavy:** one HVM + Docker Compose is enough for **lab/stress**; three HVMs match **prod-shaped** split (SFU / TURN / transcode). Start with **1**; split when metrics or PO asks.
|
||
|
||
---
|
||
|
||
## 2. What you give the AI on day zero
|
||
|
||
Minimum handoff — **no media packages required yet** on the empty machine.
|
||
|
||
### 2.1 Access
|
||
|
||
| Item | Required | Example |
|
||
|------|----------|---------|
|
||
| **SSH target** | Yes | `ssh media@media-lab.example.org` or intra `10.7.x.x` |
|
||
| **Sudo** | Yes | passwordless `sudo` for `apk`, `rc-service`, docker |
|
||
| **SSH key** | Yes | laptop → HVM; optional: HVM → git read for deploy scripts |
|
||
| **Jump host** | If no public SSH | `ProxyJump` via `f0xx-monstro` / `alpine-be` (document in `~/.ssh/config`) |
|
||
|
||
### 2.2 Machine identity
|
||
|
||
| Item | Required | Notes |
|
||
|------|----------|-------|
|
||
| **Hostname** | Yes | e.g. `media-1.intra.raptor.org` |
|
||
| **OS** | Yes | Alpine 3.x x86_64 (match prod BE) |
|
||
| **vCPU / RAM** | Yes | See [§8](#8-sizing-and-bandwidth-200200-mbit-context) |
|
||
| **Root disk** | ≥ 40 GiB SSD | containers + logs |
|
||
| **Intra IP** | Yes | `10.7.0.0/8` address stable across reboots |
|
||
| **Public reach** | For external test | DNS name OR FE proxy only (see [§4](#4-network-dns-and-firewall-prerequisites)) |
|
||
|
||
### 2.3 PO-owned (snapshots, DNS, firewall)
|
||
|
||
| Item | Owner | AI needs |
|
||
|------|-------|----------|
|
||
| **VM snapshots** | PO | “Snapshot before AI session” + rollback name |
|
||
| **External DNS** | PO | A/AAAA or CNAME for WebRTC/signaling test |
|
||
| **Firewall / DNAT** | PO | UDP/TCP port list from [§4](#4-network-dns-and-firewall-prerequisites) opened on router/FE |
|
||
| **TLS certs** | PO or AI via DNS-01 | Let’s Encrypt on FE or on media host |
|
||
|
||
### 2.4 Integration secrets (existing BE — read-only API)
|
||
|
||
| Item | Required | Purpose |
|
||
|------|----------|---------|
|
||
| **Crashes base URL** | Yes | `https://apps.f0xx.org/app/androidcast_project/crashes` |
|
||
| **Service account or API token** | Yes | Mint rooms / validate session (new `sfu` API or extend OpenAPI) |
|
||
| **RBAC model** | Reference | Same pattern as remote access / short links |
|
||
| **Gitea / deploy** | Optional | Pull `android_cast` branch `feature/sfu-relay` or deploy tarball |
|
||
|
||
AI does **not** need MariaDB root on monolith BE if all integration is **HTTP APIs**.
|
||
|
||
### 2.5 Decisions PO should state (or defaults)
|
||
|
||
| Decision | Default if silent |
|
||
|----------|-------------------|
|
||
| **Topology code name** | **SPIDERWEB** (SFU) first; MCU later |
|
||
| **Engine** | **mediasoup** (greenfield) — Janus on `:8089` is **other** stack, avoid unless PO picks Janus on **new** host |
|
||
| **Instance count** | **1** for lab; **3** for prod-shaped |
|
||
| **Public path** | Subdomain `media.apps.f0xx.org` → FE → media HVM (keeps TLS at FE) |
|
||
| **TURN** | **coturn** on same HVM (lab) or instance 2 (prod) |
|
||
|
||
---
|
||
|
||
## 3. Recommended instance layout (up to 3 HVMs)
|
||
|
||
### 3.1 Lab — single HVM (minimum handoff)
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph HVM1["media-1 (single Alpine HVM)"]
|
||
NG["nginx :443 reverse proxy"]
|
||
SIG["signaling sidecar<br/>Node or Go"]
|
||
SFU["mediasoup / Janus SFU"]
|
||
TURN["coturn STUN/TURN"]
|
||
FF["ffmpeg worker optional"]
|
||
end
|
||
|
||
Client["App / browser"] --> NG
|
||
NG --> SIG
|
||
Client --> TURN
|
||
Client --> SFU
|
||
SIG --> SFU
|
||
BE["Existing BE PHP"] -.->|HTTPS| SIG
|
||
```
|
||
|
||
All services in **one docker-compose** stack; fine for stress and first WAN cast tests.
|
||
|
||
### 3.2 Prod-shaped — three HVMs
|
||
|
||
| VM | Role | Containers / daemons |
|
||
|----|------|----------------------|
|
||
| **media-1** | **SPIDERWEB SFU** | mediasoup (or Janus), nginx edge, signaling sidecar |
|
||
| **media-2** | **TURN + signaling** | coturn, optional dedicated signaling if scaled |
|
||
| **media-3** | **Transcode / MCU path** | ffmpeg workers, optional MCU (SPIDER) if not SFU-only |
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
FE["FE TLS"] --> M1["media-1 SFU"]
|
||
FE --> M2["media-2 TURN"]
|
||
APP["Android"] -->|UDP WebRTC| M1
|
||
APP -->|UDP TURN| M2
|
||
BE["Monolith BE APIs"] -->|REST| M1
|
||
M1 --> M3["media-3 ffmpeg optional"]
|
||
```
|
||
|
||
PO can hand **one** machine first; AI documents split boundaries so instances 2–3 are **additive**, not rewrite.
|
||
|
||
---
|
||
|
||
## 4. Network, DNS, and firewall prerequisites
|
||
|
||
### 4.1 Traffic paths
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
Internet["Internet clients"]
|
||
Router["Router 200/200 Mbit"]
|
||
FE["Gentoo FE"]
|
||
M["Media HVM(s)"]
|
||
BE["Androidcast BE :80"]
|
||
|
||
Internet --> Router --> FE
|
||
FE -->|"HTTPS signaling<br/>wss / REST"| M
|
||
FE --> BE
|
||
Internet -->|"UDP WebRTC<br/>after DNAT or 1:1"| M
|
||
```
|
||
|
||
**Do not** attach media to androidcast **`:8089`** vhost ([INFRA.md](../docs/INFRA.md) §2).
|
||
|
||
### 4.2 Ports to plan (PO opens on FE/router → media HVM)
|
||
|
||
| Port / proto | Service | Notes |
|
||
|--------------|---------|-------|
|
||
| **443/tcp** | HTTPS / WSS signaling | Via FE reverse proxy recommended |
|
||
| **3478/udp,tcp** | STUN/TURN (coturn) | Required for many NATs |
|
||
| **49152–65535/udp** | WebRTC media (ICE) | Range size depends on engine; document in SFU SPEC |
|
||
| **8080/tcp** | SFU HTTP API (internal) | **Not** public; FE or intra only |
|
||
| **22/tcp** | SSH | Intra or VPN only in prod |
|
||
|
||
Exact ICE port range: set in mediasoup/Janus config during deploy; PO must allow **UDP range** on path to media HVM.
|
||
|
||
### 4.3 DNS names (examples)
|
||
|
||
| Name | Points to | Use |
|
||
|------|-----------|-----|
|
||
| `media.apps.f0xx.org` | FE → proxy → media-1 | Public signaling + health |
|
||
| `turn.apps.f0xx.org` | FE or media-2 | TURN URI in app |
|
||
| `media-lab.intra.raptor.org` | 10.7.x.x | AI + sysop intra testing |
|
||
|
||
---
|
||
|
||
## 5. Integration with existing BE (isolation model)
|
||
|
||
Media HVM **must not** share MariaDB or PHP-FPM with crashes console. Integration is **API-only**:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant App as Android app
|
||
participant FE as FE nginx
|
||
participant BE as Crashes BE PHP
|
||
participant SIG as Media signaling
|
||
participant SFU as SFU engine
|
||
|
||
App->>FE: HTTPS session / cast token
|
||
FE->>BE: existing auth APIs
|
||
App->>SIG: WSS create room (bearer)
|
||
SIG->>BE: POST validate token / RBAC
|
||
BE-->>SIG: room allowed + ttl
|
||
SIG->>SFU: create room
|
||
App->>SFU: WebRTC publish/subscribe UDP
|
||
```
|
||
|
||
| Integration | Method | Existing artifact |
|
||
|-------------|--------|-------------------|
|
||
| Health gate | GET | `api/sfu_health.php` (stub today) |
|
||
| Feature flag | config | `config.php` → `sfu.enabled`, URLs |
|
||
| Room mint / revoke | **New REST** | OpenAPI under crashes or `media-api` submodule |
|
||
| Auth | Bearer + session RBAC | Same as remote access / short links pattern |
|
||
| Metrics | POST ingest | Optional → `graph_upload.php` or new series |
|
||
| No direct DB | — | Media DB (if any) is **local** to HVM (Redis/SQLite) |
|
||
|
||
**OpenAPI:** extend [examples/crash_reporter/backend](../examples/crash_reporter/backend) or new `backend/media-signaling` repo later — AI needs **written** API contract before prod wire-up.
|
||
|
||
---
|
||
|
||
## 6. SFU vs MCU stack choice
|
||
|
||
| | **SPIDERWEB (SFU)** | **SPIDER (MCU)** |
|
||
|---|---------------------|------------------|
|
||
| **Use** | Multiparty cast; subscribers receive per-stream | Mix down; single output; higher CPU per room |
|
||
| **Engine candidates** | **mediasoup** (preferred greenfield), Janus VideoRoom | Janus MCU plugin, ffmpeg bridge, custom |
|
||
| **CPU** | Moderate per stream; scales with subscribers | High per room (encode) |
|
||
| **Latency** | Lower | Higher |
|
||
| **First milestone** | **Yes** | Phase 2 / media-3 |
|
||
|
||
**Janus on existing `:8089`:** treat as **foreign** — do not piggyback androidcast nginx. New Janus instance on **media HVM** only if PO rejects mediasoup.
|
||
|
||
**Docker vs bare metal on HVM:** use **Docker** for SFU/TURN/signaling; host only needs Alpine + docker + nginx + firewall tools.
|
||
|
||
---
|
||
|
||
## 7. Packages and software (installed later by AI)
|
||
|
||
PO delivers **almost empty** Alpine; AI installs when handoff complete.
|
||
|
||
### 7.1 Host (Alpine packages)
|
||
|
||
```text
|
||
# Baseline (all media HVMs)
|
||
openssh sudo bash curl wget git ca-certificates
|
||
nginx openssl iptables ip6tables
|
||
docker docker-cli-compose
|
||
# Optional host tools
|
||
tcpdump bind-tools jq python3
|
||
|
||
# If bare-metal fallback (no Docker) — not recommended first
|
||
# janus-gateway mediasoup — prefer official containers
|
||
```
|
||
|
||
### 7.2 Container images (typical compose)
|
||
|
||
| Service | Image / build | Role |
|
||
|---------|---------------|------|
|
||
| **mediasoup** or **janus** | Official or pinned Dockerfile in repo | SFU |
|
||
| **coturn** | `coturn/coturn` | STUN/TURN |
|
||
| **signaling** | Custom Node/Go from repo | Rooms, BE API client |
|
||
| **nginx** | `nginx:alpine` or host nginx | TLS termination at edge or intra |
|
||
| **ffmpeg** | `jrottenberg/ffmpeg` or alpine | Transcode sidecar (media-3) |
|
||
| **redis** | `redis:alpine` | Room state optional |
|
||
|
||
### 7.3 Repo deliverables (AI produces after deploy)
|
||
|
||
| Artifact | Location (proposed) |
|
||
|----------|---------------------|
|
||
| `docker-compose.yml` | `examples/sfu/deploy/` or `backend/media-pop/` |
|
||
| nginx FE fragment | `examples/sfu/deploy/nginx.fe-media.snippet` |
|
||
| BE config keys | `config.example.php` → `sfu.*` (already stubbed) |
|
||
| Smoke script | `examples/sfu/scripts/smoke_sfu.sh` |
|
||
| OpenAPI | `examples/sfu/openapi.yaml` |
|
||
|
||
---
|
||
|
||
## 8. Sizing and bandwidth (200/200 Mbit context)
|
||
|
||
WAN **200/200 Mbit** sits behind FE; media UDP may be **DNAT** to media HVM.
|
||
|
||
| Profile | vCPU | RAM | Notes |
|
||
|---------|------|-----|-------|
|
||
| **Lab / stress (1 HVM)** | **4–8** | **8–16 GiB** | SFU + TURN + signaling; ~10–30 parallel WebRTC streams |
|
||
| **Prod SFU node** | **8+** | **16 GiB** | Dedicated NIC budget; see [SPEC §12.3](../docs/specs/20100612_1_scaling.md#123-network-bandwidth-and-egress) |
|
||
| **TURN only** | **2** | **4 GiB** | Mostly relay bandwidth |
|
||
| **ffmpeg node** | **8+** | **16–32 GiB** | CPU-bound transcodes |
|
||
|
||
**Bandwidth rule of thumb (SFU):**
|
||
|
||
| Streams | Approx WAN need |
|
||
|---------|-----------------|
|
||
| 10 × 1 Mbps video | **~10–15 Mbps** + overhead |
|
||
| 50 × 1 Mbps | **~50–80 Mbps** — approaches 200 Mbit shared with rest of estate |
|
||
| 100+ viewers | **CDN/SFU PoP or second site** — not single 200 Mbit |
|
||
|
||
Stress test: measure on **intra** first, then **public DNS** through FE.
|
||
|
||
---
|
||
|
||
## 9. AI workflow after prerequisites are met
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["PO: empty HVM + SSH + DNS + firewall list"] --> B["AI: verify ssh sudo intra ping"]
|
||
B --> C["AI: apk + docker + compose"]
|
||
C --> D["AI: deploy SFU stack lab compose"]
|
||
D --> E["AI: nginx FE fragment + BE sfu.enabled URLs"]
|
||
E --> F["Smoke: sfu_health + WSS + ICE"]
|
||
F --> G{"PO: load test OK?"}
|
||
G -->|yes| H["Document + optional 2nd/3rd HVM split"]
|
||
G -->|no| I["Tune UDP range CPU docker limits"]
|
||
I --> F
|
||
```
|
||
|
||
| Step | AI action | PO action |
|
||
|------|-----------|-----------|
|
||
| 0 | — | Snapshot empty VM |
|
||
| 1 | Probe CPU/RAM/NIC, `apk update` | Confirm DNS/firewall |
|
||
| 2 | Install docker, compose, pin versions | — |
|
||
| 3 | Bring up mediasoup + coturn + signaling | — |
|
||
| 4 | FE nginx `location /sfu/` or subdomain proxy | Reload FE nginx |
|
||
| 5 | Enable `sfu.enabled` + URLs on monolith BE **only** | Sync config.php |
|
||
| 6 | Run smoke + optional load/WebRTC client | Review metrics |
|
||
| 7 | Snapshot “golden” media PoP | — |
|
||
|
||
---
|
||
|
||
## 10. Out of scope for first media PoP
|
||
|
||
- Replacing LAN UDP cast (alpha path unchanged)
|
||
- Moving crashes/tickets/Gitea to media HVM
|
||
- Full GEO policy ([SPEC §14](../docs/specs/20100612_1_scaling.md#14-global-availability-and-geo-policy)) — later with F3/F10
|
||
- Production payments / recording storage
|
||
- Merging with Janus stack on BE `:8089`
|
||
|
||
---
|
||
|
||
## 11. PO checklist (copy before handoff)
|
||
|
||
```text
|
||
[ ] Alpine 3.x HVM provisioned (1–3); specs noted
|
||
[ ] SSH + sudo for AI; key installed
|
||
[ ] Intra IP 10.7.x.x stable
|
||
[ ] External DNS name for test (or FE-only proxy agreed)
|
||
[ ] Firewall: UDP 3478 + WebRTC range → media HVM
|
||
[ ] Snapshot taken (empty baseline)
|
||
[ ] Decision: SPIDERWEB (SFU) first — mediasoup default
|
||
[ ] Crashes BE base URL + API token for room validation
|
||
[ ] Confirm: do NOT use androidcast :8089 / monolith BE for SFU
|
||
[ ] Optional: 2nd/3rd HVM reserved for TURN + ffmpeg
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Changelog
|
||
|
||
| Date | Change |
|
||
|------|--------|
|
||
| 2026-06-12 | Initial prerequisites — HVM vs Docker, 3-instance layout, BE API isolation, PO checklist |
|
||
|
||
---
|
||
|
||
**Documentation index:** [docs/README.md](../docs/README.md)
|