bunch of shit

This commit is contained in:
2026-11-06 19:05:47 -06:00
parent ca01516d42
commit 630b253278
29 changed files with 2793 additions and 303 deletions

View File

@@ -128,22 +128,34 @@ logged-in Discord user each time Go Live is pressed and sets it as the OBS strea
OBS → Settings → Output → Encoder: **B-frames = 0** (NVENC "Max B-frames: 0"; x264 add `bframes=0`). MediaMTX
refuses WebRTC playback of H.264 with B-frames ("WebRTC doesn't support H264 streams with B-frames"), so the live
page falls back to HLS. The dock checks the actual recording a few seconds after Go Live and shows a red banner
if B-frames are present (and in Simple output mode with x264 it sets `bf=0` for you); the Live tab on `diag.`
shows the same per publisher. Nothing can set it for you in Advanced output mode, so the live
portal would fall back to HLS with seconds of delay. Keyframe interval 2 s. Recording quality is unaffected.
OBS → Tools → WebSocket Server Settings → enable, set a password.
OBS → Docks → Custom Browser Docks → `https://live.BASE/dock/` → log in with Discord once.
In the dock's Settings: ingest host (prefilled), OBS WebSocket password/port, optional title prefix/suffix
(`{date}` → YYYY-MM-DD, e.g. prefix `HelmetCam-` or suffix `-{date}`).
Per stream: type a title, click **Go Live**. The dock shows the final stream name (`<you>/<Title>`) as you type
and enforces the naming rules the metadata relies on: letters, digits, `_ . -` only, no double underscore
and enforces the naming rules the metadata relies on: letters, digits, `_ -` only (no dots: `<title>.webrtc` is reserved for the audio mirror), no double underscore
(it separates title and timestamp in filenames), no leading dot, max 64 chars.
After a bad night: **Dump tonight** (dumps the night of the account you are logged in as; `admins`/`media-admins`
may dump anyone's).
After a bad night: **Dump tonight** in the dock dumps the night of the account you are logged in as, and only
that, admin or not. Dumping someone else's night is triage: **Dump a night**, a tab of the streaming page on
`diag.BASE/streaming#dump` (media-admins): the night's recordings and clips per streamer, a date picker, Dump per
streamer or Dump everyone, two confirmations. Same endpoint, same 3-day undo; dumpd honours a `user` only on that
route. The night is 17:00–02:00 Eastern.
Manual OBS/other software: dock → **Manual OBS setup** shows the RTMP server, the stream key and the SRT URL
with the token hidden (Show / Copy / Fresh token). Tokens expire after 12 h; a running stream is not cut when
its token expires, but a reconnect needs a fresh one.
Browser streaming without OBS: the live portal's **Go Live** (Windows Chrome 141+ for window audio + GPU encode).
On a lossy uplink Go Live switches itself to TCP: when the server reports more than 0.3% packet loss over 20 s it
re-publishes the same capture with only MediaMTX's TCP candidate, which costs latency and nothing else. The stats
line shows loss, retransmit requests (nack) and `via TCP` once it has happened; the recording picks up one extra
file at the switch, which the sweep stitches. The switch is remembered in that browser, so the next stream starts
on TCP; the "Lossy uplink" checkbox in Go Live's setup shows and controls it. Starlink users: tick it before the
first stream, the dish drops a burst on every satellite handoff (~15 s cycle) and that is what was splitting files.
## 5a. Onboarding a gamer's clips (Syncthing) — self-service
@@ -198,6 +210,15 @@ In the UI (https://sync.BASE/):
## 5c. Authentik (Discord login) and Jellyfin
**Login page branding and the Discord forward are not clicked in the UI.** `./configure.py --apply` (or
`./configure.py --ak-branding` on its own) sets them from the ORM every time: brand title, logo, favicon and
background from `authentik/brand/*.svg`, the login-card CSS, the media gateway as the brand's default application,
flow titles, and a redirect stage at the top of `default-authentication-flow` that sends members straight to
Discord. Nobody sees an Authentik page unless they open the admin card at
`https://auth.BASE/if/flow/default-authentication-flow/?admin=1` (akadmin + password; the link is on the diag
landing dashboard). Change the look by editing the SVGs or the `CSS` list in `configure.py`, then re-run.
A UI edit to those objects is overwritten by the next apply, on purpose.
DNS: `auth`, `watch` (and `files`, `clips`, `sync`, `live`, `git`, `bot`) as CNAMEs to the main host. Then run `./addCommunityMediaServer.sh` — it asks
for every hostname, checks DNS, generates `.env` (Authentik secret + DB password; never commit it),
adds the Caddy site blocks, starts the containers, waits for them and prints the steps below with your
@@ -447,8 +468,8 @@ Two paths on that host are ours rather than Kibana's:
| path | what |
|---|---|
| `/stuck` | **stuck recordings**: everything that finished but never published, why each one is stuck, and Retry / Bin buttons. The list is readable by anyone the gate allows; the actions are enforced in dumpd for the admin groups, not hidden in the page. Binning moves the file to `trash/<today>/`, the same 3-day undo as a dumped night |
| `/stuck/api` | dumpd serves it. Both the page and `vgstuck.sh` on the host run the same script, so they cannot disagree about why something is stuck |
| `/streaming` (Stuck tab) | **stuck recordings**: everything that finished but never published, why each one is stuck, and Retry / Bin buttons. The list is readable by anyone the gate allows; the actions are enforced in dumpd for the admin groups, not hidden in the page. Binning moves the file to `trash/<today>/`, the same 3-day undo as a dumped night |
| `/streaming/api/stuck` | dumpd serves it. Both the page and `vgstuck.sh` on the host run the same script, so they cannot disagree about why something is stuck |
Kibana deliberately does **not** live under `/kibana`. That would need `server.basePath` plus
`rewriteBasePath`, a Caddy handle that does not strip the prefix, and the prefix appended in both Beats'
@@ -456,8 +477,11 @@ Kibana deliberately does **not** live under `/kibana`. That would need `server.b
one crash-loops Metricbeat, which exits when Kibana is unreachable. `defaultRoute` buys the landing page
without any of it.
After any hostname change, `sudo vghosts-check.py` validates every place a name lives, in the order the change
must be made (HOSTNAME-CHANGE.md §E).
`observability/kibana-setup.py` creates the first-run objects and takes a step name, so you can redo one:
`connector`, `rule`, `dataview`, `landing`, or all of them when omitted. `observability/kibana-alarms.py`
`connector`, `rule`, `dataview`, `landing`, or all of them when omitted. Disk use by streamer, clipper, month, logs
and system is its own page, `https://diag.BASE/disk`. `observability/kibana-alarms.py`
creates the six alarm rules.
| container | job |
@@ -685,6 +709,11 @@ A `.env`-driven Authentik blueprint would remove the first line (planned).
## Layout on the server
MediaMTX runs with `network_mode: host`, as its documentation recommends for WebRTC: the bridge put Docker's
userland UDP proxy between browsers and port 8189 and it lost packets, which corrupted recordings and split
them into fragments. Its ingest ports (1935, 8890/udp, 8189 tcp+udp) are accepted by the host firewall; its
API, HLS, WHIP/WHEP signalling and metrics bind 127.0.0.1 and are reached only through Caddy and Metricbeat.
/srv/media/streams/<user>/<Title>/.live/<ts>.mp4 recording in progress (hidden from Jellyfin/copyparty)
/srv/media/streams/<user>/<Title>__<ts>.json capture time (UTC ms), timecode, source (strip|arrival), drift
/srv/media/streams/<user>/<Title>__<ts>.mp4 finished stream, one flat MP4 per recording, directly in
@@ -744,4 +773,4 @@ evening; after 17:00 it is tonight. The 05:00 trash purge is 05:00 Eastern.
| trash-purge.timer | daily 05:00 Eastern | deletes trash older than 3 days |
| vanguard-alerts.timer | every 1 min | delivers Kibana's alert documents to Discord, with a 15-min per-rule repeat guard |
| vanguard-deadman.timer | every 5 min | watches the alerting stack from outside it, and the disks |
| dumpd.service | always | the dump endpoint on 127.0.0.1:8080, behind Caddy on `live.` at /dump, /me, /time, on `clips.` at /me/clips and on `diag.` at /stuck/api |
| dumpd.service | always | the dump endpoint on 127.0.0.1:8080, behind Caddy on `live.` at /dump, /me, /time, on `clips.` at /me/clips and on `diag.` at /streaming/api/stuck |