Alarm mentions, targeted --set, and a logout that actually logs out

Mentions
- bin/vgnotify.sh is shared by the alert timer and the deadman. A webhook will not ping a role
  from <@&id> in the content alone: the request has to whitelist it, so every post now sends an
  explicit allowed_mentions. parse is always [] as well, so an @everyone appearing inside a log
  line or filename in an alert text can never ping the server.
- ALERT_MENTION is written as role:<id>/user:<id> rather than raw Discord syntax, and
  ALERT_MENTION_LEVELS keeps pings to errors by default so warnings arrive quietly. The deadman
  pings too, because its alarm means nothing else can get through, but its recovery is info level.
- Both scripts now read /etc/vanguard/alerts.env themselves instead of relying on systemd to
  inject it. Running one by hand previously behaved differently from the timer, which is exactly
  how "the mention is configured but never appears" happened.

configure.py --set
- Change one setting and apply only what it needs. --full re-prompts for every hostname and
  secret, which is far too blunt for pointing the alarms at a new channel, and mistyping a
  hostname there has broken TLS before.
- Every Discord credential and webhook is now one commented block at the top of .env, so there is
  one place to look, and --show prints the same five together with the secrets truncated.
- Each key applies only its own side effect: the webhook rewrites its root-only file, the deploy
  webhook rewrites deploy.env, the bot token recreates the bot container, log retention re-injects
  the ILM delete phase. LIVE_WEBHOOK_URL deliberately stops short, because MediaMTX only reads it
  at start and a recreate cuts a live recording.

Logout
- The proxy provider pointed at default-provider-invalidation-flow ("Logged out of application"),
  which ends only that application's session and leaves the SSO session alive, so /logout dropped
  the proxy cookie and forward-auth signed the user straight back in. It looked like a refresh.
  default-invalidation-flow ("Logout") is what Authentik's own button uses. --apply now enforces

  it had explicitly recommended the wrong flow.

Sweep
- The band-removal branch overwrote $reasocodec> marker, which is the
  only signal telling the encode to convert audio. A Go Live stream with both the band and Opus
  audio therefore got -c:a copy and shippeachable once Windows Go
  Live audio started working.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-11-03 21:07:17 -06:00
parent b38d40ab0c
commit a9de09fa18
11 changed files with 371 additions and 46 deletions

View File

@@ -69,6 +69,7 @@ works from ~/vanguard-stream without sudo.
./configure.py --full # first time: asks everything, then applies
./configure.py # later: add/remove/reset streamers and editors, then apply
./configure.py --show # print current users and the OBS settings to hand out
./configure.py --set K=V # change one setting (Discord tokens/webhooks, retention) without a full pass
Stateless: it reads the current values out of `Caddyfile`, `mediamtx.yml` and `.env`, regenerates
`Caddyfile`, `mediamtx.yml`, `copyparty.conf` and `streamers.json` (→ `/etc/vanguard`), creates
@@ -368,7 +369,8 @@ React + dockview page (`live/`) behind Authentik forward-auth: every active stre
audio follows the focused window, per-window mute + volume. WebRTC (WHEP) with HLS fallback, served by
MediaMTX (HLS 8888, WebRTC 8889, API 9997 on loopback; WebRTC media on **8189** udp/tcp, firewall opened).
MediaMTX runs the `-ffmpeg` image for the on-ready hook `bin/mtx-on-ready.sh` (bind-mounted into the container):
the optional "is live" Discord post (`LIVE_WEBHOOK_URL` in `.env`, asked by `configure.py --full`) and the
the optional "is live" Discord post (`LIVE_WEBHOOK_URL`, one of the Discord settings grouped at the top of
`.env`; set it with `./configure.py --set LIVE_WEBHOOK_URL=…`, which tells you MediaMTX needs a recreate) and the
**WebRTC audio mirror**. Browsers cannot receive AAC over WebRTC (MediaMTX forwards Opus/G711/G722/LPCM only) and
OBS sends AAC, so for every stream whose audio is not already Opus the hook runs one ffmpeg that copies the video
and transcodes the audio to Opus into the sibling path `<user>/<Title>.webrtc` (via MediaMTX's RTSP on the
@@ -416,9 +418,13 @@ Design: **Authentik authenticates once for the whole domain; Caddy authorizes pe
Logout: copyparty's logout link, the live portal's "Sign out" and the dock's "sign out" go through
`/outpost.goauthentik.io/sign_out`; `https://<any gated host>/logout` does the same from anywhere
(e.g. from the Syncthing UI, which has no logout of its own). Set the provider's **Invalidation flow** to
`default-provider-invalidation-flow` so that also ends the Authentik login: every logout then forces a full
Discord re-login on the next visit (and picks up group changes). Discord itself may approve silently if
you are still logged into Discord in that browser.
`default-invalidation-flow` — the one named **"Logout"**, the same flow Authentik's own logout button uses.
Do NOT use `default-provider-invalidation-flow`: despite the name it is called "Logged out of application"
and ends the session for that one application only, leaving the SSO session alive. With it, `/logout` drops
the proxy cookie, forward-auth asks Authentik again, Authentik still knows you, and you are signed straight
back in — which looks exactly like the page merely refreshing. With `default-invalidation-flow` every
logout ends the whole session and forces a full Discord re-login on the next visit (and so picks up group
changes). Discord itself may still approve silently if you remain logged into Discord in that browser.
5. Debug: `https://HOST/whoami` prints the username/groups headers exactly as the apps receive them, and
`https://HOST/me` adds the `discord_id` claim decoded from `X-Authentik-Jwt` — the quickest check that the
`Vanguard profile` scope mapping is live. The raw token is deliberately never echoed: the browser does not