# zoitestream Landing page + lightweight API for `stream.dumpnet.chat`, backed by [MediaMTX](https://github.com/bluenviron/mediamtx). This is intentionally tiny: a single Flask process with a SQLite database (on a persistent volume, not container-local disk) tracking the currently active stream and stream history. MediaMTX calls this service's `/hooks/*` endpoints on stream start/stop (via its `runOnReady`/`runOnNotReady` path hooks), and the landing page polls `/api/active` and `/api/history` to render status. An IRC bot can call `/api/creds` to fetch the current publish URL to hand out to whoever wants to stream. State persists across pod restarts/redeploys (SQLite file lives on the shared `appdata` hostPath PV, mounted at `STATE_DB_PATH`). ## Design notes - One shared publish stream/password for everyone (a deliberate feature, not a limitation) — credentials are retrievable via `/api/creds` (meant for the IRC bot to call), never shown on the public landing page. The IRC channel is a high-trust environment, so this endpoint is only lightly protected (shared token), not treated as a hard secret boundary. - `/hooks/ready` and `/hooks/not-ready` are protected by a separate shared token (`HOOK_TOKEN`) so MediaMTX is the only thing that can flip stream status — rotatable independently of the creds token. - Publish password itself lives in MediaMTX's own internal auth config (`authInternalUsers`), not validated by zoitestream — zoitestream just knows the current value (via `PUBLISH_PASSWORD` env var, sourced from the same secret) so it can hand it back out through `/api/creds`. - No scheduling API yet — planned as a future addition, likely driven by an IRC bot, and will live in the same SQLite database. ## Endpoints - `GET /` — landing page - `GET /api/active` — JSON list of currently active streams - `GET /api/history` — JSON list of recent streams (capped, most-recent-first) - `GET /api/creds` — publish URL/user/password (token-protected via `CREDS_TOKEN`) - `GET /health` — health check - `POST /hooks/ready` — called by MediaMTX `runOnReady` (form/query: `path`, `source_type`) - `POST /hooks/not-ready` — called by MediaMTX `runOnNotReady` (form/query: `path`) ## Env vars - `HOOK_TOKEN` — shared secret required (as `?token=` or `X-Hook-Token` header) on `/hooks/*` - `CREDS_TOKEN` — shared secret required (as `?token=` or `X-Creds-Token` header) on `/api/creds` - `PUBLISH_USER` — publish username to report via `/api/creds` (default `streamer`) - `PUBLISH_PASSWORD` — publish password to report via `/api/creds` — must match MediaMTX's own config - `DOMAIN` — display domain shown on the landing page and in `/api/creds` (default `stream.dumpnet.chat`) - `RTMP_PORT` — RTMP port used in the URL built by `/api/creds` (default `1935`) - `STATE_DB_PATH` — path to the SQLite file (default `/data/zoitestream.db`) - `HISTORY_LIMIT` — number of history entries to retain (default `50`) ## Local dev ``` uv sync STATE_DB_PATH=./dev.db uv run flask --app wsgi:app run --debug ```