diff --git a/STATUS.md b/STATUS.md index 151657b..2d9bfb6 100644 --- a/STATUS.md +++ b/STATUS.md @@ -1,6 +1,6 @@ # Vanguard stack — what runs, what people do, what's unfinished -Living document. Updated whenever something is deployed or changes behaviour. Last update: 2026-10-30 (evening). +Living document. Updated whenever something is deployed or changes behaviour. Last update: 2026-10-31. Host: `ovh1.dumpstervigilantes.rip` (OVH SYS-1, Debian 13). Bundle: `~/vanguard-stream` on the box. Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea (`ssh://git@git.dumpstervigilantes.rip:2222/techy/Media-Server-Setup.git`). @@ -17,7 +17,7 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea | Daily 04:10 ET | Recordings roll between weekday → week → month → year seasons as the calendar moves; no files change | Jellyfin plugin | | Daily 05:00 ET | Dumped nights older than 3 days deleted for good | trash purge (systemd timer) | | On boot | Everything restarts itself after the media mount is up; interrupted recordings stay playable | systemd + Docker | -| Continuous | Active streams are watchable live (WebRTC, HLS fallback) by Discord-server members; optional "is live" Discord post when a stream starts | MediaMTX + live portal (**built, not deployed**) | +| Continuous | Active streams are watchable live (WebRTC, HLS fallback) by Discord-server members; optional "is live" Discord post when a stream starts | MediaMTX + live portal (page deployed; MediaMTX port fix pending apply) | | **Not yet running** | Pull of files older than 15 days to the home HDD | `home/rclone-archive.sh`, needs the home box | | **Not yet running** | Nightly backup of the Authentik database (Postgres) | planned | @@ -30,8 +30,8 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea | Editor | Browse by streamer/title, scrub in browser, download originals, upload finals | copyparty `https://ovh1…/` | **local account** (to move to Discord) | | Editor | Mount the library as a drive | WebDAV, same URL | same | | Anyone in the Discord server | Watch recordings and clips by streamer and season | Jellyfin `https://watch.…/` | Discord (Authentik) | -| Anyone in the Discord server | Watch live: all active streams in dockable windows, audio follows focus, per-window mute/volume | Live portal `https://live.…/` | Discord (Authentik) — **pending deploy** | -| Streamer | **Go Live** from the browser: pick the WarDogs window (+audio), streams as `/HelmetCam-`; no OBS needed | Live portal → Go Live | Discord; key fetched for the logged-in user — **pending deploy** | +| Anyone in the Discord server | Watch live: all active streams in dockable windows, audio follows focus, per-window mute/volume | Live portal `https://live.…/` | Discord (Authentik, domain-level) — live, first real stream untested | +| Streamer | **Go Live** from the browser: pick the WarDogs window (+audio), streams as `/HelmetCam-`; no OBS needed | Live portal → Go Live | Discord; key fetched for the logged-in user — awaiting MediaMTX port fix | | Devs | Host code, review, CI | Gitea `https://git.…/`, ssh 2222 | **local account** (Discord later) | | Bot dev | Push code, restart container, read logs; web UI at `https://bot.…/` | Gitea + compose; `bot/AGENT.md` | web UI behind Discord (Authentik), once its provider exists | | Admin | Add/remove streamers & editors, hostnames, bot token | `./configure.py` | ssh | @@ -58,13 +58,14 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea | Gitea | git hosting on MariaDB, Actions enabled; holds this repo | live, local admin | | act_runner | CI jobs in Docker | **not registered** | | discord-bot | Python runtime, idles until `bot/app/main.py` exists | live, no code yet | -| `bot.` host | bot web UI behind Authentik forward-auth; Caddy requires group `bot-users` (from a Discord role) | Caddy block ready; **CNAME + Authentik domain-level provider + roles→groups policy pending** | -| Live portal (`live/`, `live.` host) | React + dockview multi-stream viewer + browser Go Live (WHIP); MediaMTX HLS/WebRTC/API re-enabled on loopback; dumpd `/me/stream-key` | **built; CNAME, Authentik proxy provider, `./deployLive.sh`, configure apply pending** | +| `bot.` host | bot web UI behind Authentik forward-auth; Caddy requires group `bot-users` (from a Discord role) | Caddy block live; **`bot` CNAME still points at the wrong IP; roles→groups policy pending** | +| Live portal (`live/`, `live.` host) | React + dockview multi-stream viewer + browser Go Live (WHIP); MediaMTX HLS/WebRTC/API on loopback; dumpd `/me/stream-key` | page live behind Authentik (domain-level); **MediaMTX recreate with new ports pending** | ## Unfinished, in rough priority -1. **Deploy the `.live` recording change** (mediamtx.yml + sweep) and **plugin 0.1.2**; refresh the one stream Jellyfin indexed while it was still recording. -1b. **Live portal**: CNAME `live`, `./configure.py --full` (live hostname + optional webhook), Authentik proxy provider for `https://live.HOST`, `./deployLive.sh`. +1. **Recreate MediaMTX** with the corrected compose (loopback HLS/WebRTC/API ports) → portal list + Go Live work. +1b. **`bot` CNAME** → `ovh1`; Authentik `discord-roles-to-groups` policy + `bot-users` group. +1c. Confirm plugin 0.1.2 installed and runner registered (were in the same deploy block). 2. **Register the runner** (`./registerGiteaRunner.sh`). 3. **`bot` CNAME + Authentik proxy provider** — first use of forward-auth; proves the pattern for copyparty. 4. **copyparty behind Authentik** (group permissions, WebDAV via app passwords, then drop local accounts). The one change that can lock editors out; keep a local admin until a Discord editor has used the new path. @@ -78,6 +79,9 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea 12. **SYS-3** when transcoding or sweep speed hurts: Quick Sync, flip the plugin's policy toggle off. ## Verified behaviours worth remembering +- **Never scp generated configs** (`mediamtx.yml`, `copyparty.conf`, `Caddyfile`, `dump-users.txt`, `.env`): a template copy wiped all streamers once. They are gitignored now; `*.example.*` are reference only. +- Caddy runs in a container: anything it serves from disk must be a mount (`/srv/live` was missing → 404). +- MediaMTX 1.21 refuses to start without `webrtcAdditionalHosts` or `webrtcIPsFromInterfaces`; configure.py writes the public host in. - Jellyfin honours provider-supplied season numbers on a Shows library (day seasons appeared). Next check: Monday 04:10 ET should fold them into a week season. - A stream indexed by Jellyfin while still recording has no duration and skips to the next episode on seek — fixed by recording into `.live/` so nothing sees a file until it is finished and flat. - Sweep leaves compliant Syncthing files untouched and keeps exactly one copy of converted ones (tested). diff --git a/addCommunityMediaServer.sh b/addCommunityMediaServer.sh index 47b8801..c072d1f 100755 --- a/addCommunityMediaServer.sh +++ b/addCommunityMediaServer.sh @@ -150,18 +150,26 @@ PHASE 3 — Jellyfin (direct play only on this CPU) Custom CSS: paste jellyfin/branding.css -> Save; reload https://$WATCH/ -LIVE PORTAL (https://$LIVE/) — same Authentik forward-auth gate as the bot host: - Authentik -> Applications -> Create with provider -> Proxy Provider (forward auth, single application), - External host https://$LIVE -> add to the embedded outpost -> group policy. - Deploy the page: on the dev box `cd live && npm run build`, then ./deployLive.sh (scp dist -> /srv/live). - WebRTC viewers need UDP/TCP 8189 open (firewall rule included). +AUTHENTIK FORWARD-AUTH (gates https://$LIVE/ and https://$BOT/; later the main host) — once: + 1. Applications -> Outposts -> edit "authentik Embedded Outpost" -> config: authentik_host: https://$AUTH + 2. Applications -> Create with provider -> Application "Vanguard sites"; Proxy Provider, authorization + flow implicit-consent, mode "Forward auth (domain level)", authentication URL https://$AUTH, + cookie domain ${BASE} -> then edit the Embedded Outpost -> Applications: add it -> Update + 3. Per-host access is enforced by Caddy from X-Authentik-Groups: .env BOT_REQUIRE_GROUP=bot-users + (default), LIVE_REQUIRE_GROUP= (empty = any logged-in member). ./configure.py apply regenerates. + 4. Discord roles -> Authentik groups: Directory -> Groups: create bot-users (keep streamers/editors/admins); + Discord source -> Scopes: "guilds guilds.members.read"; Customization -> Policies -> Expression Policy + "discord-roles-to-groups" from authentik/discord-roles-to-groups.py (server id + role-id->group table); + bind to default-source-enrollment AND default-source-authentication (engine mode all). + 5. Test: private window -> https://$LIVE/ -> Discord -> portal; https://$BOT/ -> 403 unless you hold the + bot role; https://$LIVE/me/stream-key shows your username. -BOT WEB UI (https://$BOT/) — Authentik forward-auth gate, create once: - Authentik -> Applications -> Create with provider -> Proxy Provider - mode "Forward auth (single application)", External host https://$BOT - -> Applications -> Outposts -> edit "authentik Embedded Outpost" -> add the new application -> Update - -> policy on the application: groups admins / editors / streamers as you see fit - Until this exists the site returns an error (never open). The bot receives X-Authentik-Username/-Groups/-Email. +LIVE PORTAL (https://$LIVE/) + Deploy the page from the dev box: ./deployLive.sh (builds live/ and copies dist/ to /srv/live). + WebRTC viewers and Go Live need UDP/TCP 8189 open (firewall rule included). Optional "is live" Discord + post: LIVE_WEBHOOK_URL in .env (asked by configure.py --full). + +BOT WEB UI (https://$BOT/) — gated by the forward-auth above + group bot-users; the bot dev's brief is bot/AGENT.md. GITEA (git.$GIT) 1. https://$GIT/ -> the first visit shows the install page: database is pre-filled from env; set the diff --git a/install.sh b/install.sh index 87463db..d5c8351 100755 --- a/install.sh +++ b/install.sh @@ -41,6 +41,7 @@ install -d -o 1000 -g 1000 /srv/media/{streams,clips,finals,trash,.markers,.sync install -d -o 1000 -g 1000 /srv/appdata/jellyfin/{config,cache} /srv/appdata/authentik/{media,templates,certs} install -d /srv/appdata/authentik/{db,redis} /srv/appdata/mariadb install -d -o 1000 -g 1000 /srv/appdata/discord-bot /srv/appdata/gitea /srv/appdata/act_runner +install -d -o "$OWNER" /srv/live # live portal static build (deployLive.sh) install -d -o 1000 -g 1000 /srv/media/streams/{techy,diceofire} /srv/media/clips/{techy,diceofire} for d in /srv/media/clips/*/; do [[ -e "$d/.stignore" ]] || install -m 0644 -o 1000 -g 1000 syncthing/stignore "$d/.stignore"