Split hosts: media (gateway at /), files, clips, sync; live gains /dock, /dock/clock, /dump, /me, /time
configure.py: three new host roles (files/sync/clips) in --full, --hosts, .env, Caddy generator; main host serves about/ at the root (/about* redirects); /time only where it is fetched same-origin. about/index.html links across hosts; addCommunityMediaServer.sh asks for the new names; Caddyfile.example regenerated; README/FRESH-INSTALL/HOSTNAME-CHANGE (procedure C)/STATUS updated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
45
README.md
45
README.md
@@ -2,6 +2,21 @@
|
||||
|
||||
> **FRESH-INSTALL.md** = the ordered walkthrough from an empty box to a working stack and pipeline, manual steps included.
|
||||
> **HOSTNAME-CHANGE.md** = every place a hostname lives and how to rename a subdomain or move to a new base domain safely.
|
||||
|
||||
**Hostnames** (all CNAMEs to the main host; one Discord login covers all of them):
|
||||
|
||||
| Host | What | Who |
|
||||
|---|---|---|
|
||||
| `media.BASE` (main) | gateway page at `/` (role-aware links + how-tos), `/whoami`, `/logout` | everyone |
|
||||
| `files.BASE` | copyparty: browse, scrub, download, upload finals | editors / media-admins |
|
||||
| `clips.BASE` | self-service clip intake (pair your Syncthing) | any member |
|
||||
| `sync.BASE` | Syncthing admin UI | media-admins |
|
||||
| `live.BASE` | live portal, Go Live, OBS dock `/dock/`, clock source `/dock/clock/`, `/dump`, `/me`, `/time` | everyone (`LIVE_REQUIRE_GROUP`) |
|
||||
| `watch.BASE` | Jellyfin | Discord via OIDC |
|
||||
| `git.BASE`, `bot.BASE`, `auth.BASE` | Gitea, bot web UI (`bot-users`), Authentik | — |
|
||||
|
||||
`/time` (the server clock the timecode strip syncs to) lives only on `live` because browsers fetch it
|
||||
same-origin: both pages that use it, the clock source and Go Live, are on that host.
|
||||
> **STATUS.md** = what runs, who does what, what's unfinished (kept current). **CommunityDiscordMedia.md** = design + phases.
|
||||
|
||||
Ingest from OBS, browse/scrub/download for editors, automatic normalization to one editing
|
||||
@@ -82,7 +97,7 @@ Everything else is dropped and a trickle is logged (`journalctl -k | grep vg-dro
|
||||
Jellyfin (8096), copyparty, the Syncthing UI and dumpd bind to 127.0.0.1 only and are reached via Caddy
|
||||
or an ssh tunnel, so they are deliberately not opened. MariaDB runs on the host network (not a Docker
|
||||
published port) precisely so the firewall can govern it.
|
||||
Syncthing's admin UI is proxied at `https://HOST/sync/` behind the Authentik gate + group `media-admins`; its
|
||||
Syncthing's admin UI is proxied at `https://sync.BASE/` behind the Authentik gate + group `media-admins`; its
|
||||
own GUI login is disabled (no user/password, `insecureAdminAccess=true`) so Discord is the only login:
|
||||
|
||||
docker exec syncthing syncthing cli --home /var/syncthing/config config gui insecure-admin-access set true
|
||||
@@ -103,7 +118,7 @@ There are no stream passwords or keys to hand out: the dock mints a **12-hour pu
|
||||
logged-in Discord user each time Go Live is pressed and sets it as the OBS stream key itself.
|
||||
|
||||
OBS → Tools → WebSocket Server Settings → enable, set a password.
|
||||
OBS → Docks → Custom Browser Docks → `https://media.example.com/dock/` → log in with Discord once.
|
||||
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
|
||||
@@ -120,7 +135,7 @@ Browser streaming without OBS: the live portal's **Go Live** (Windows Chrome 141
|
||||
|
||||
## 5a. Onboarding a gamer's clips (Syncthing) — self-service
|
||||
|
||||
Gamers do it themselves at **https://HOST/clips/** (Discord login): the page shows our Syncthing Device ID and
|
||||
Gamers do it themselves at **https://clips.BASE/** (Discord login): the page shows our Syncthing Device ID and
|
||||
asks for theirs; on submit, dumpd creates `clips/<username>` with the video-only `.stignore`, adds their device
|
||||
to our Syncthing (REST API, key read from `/srv/media/.syncthing/config.xml`) and creates the receive-only
|
||||
folder `clips-<username>` shared with them (min free 300 GB). They accept the device + folder on their PC,
|
||||
@@ -136,16 +151,16 @@ Manual fallback (admin, same result for the server folder):
|
||||
./addClipUser.sh <username> # creates /srv/media/clips/<username> with the video-only .stignore
|
||||
|
||||
Their side (Windows: Syncthing installer or SyncTrayzor; Mac/Linux: Syncthing): open the UI, **Actions → Show ID**,
|
||||
send you the Device ID. In their UI: **Add Remote Device** with the server's ID (ours: `/sync/` → Actions → Show ID),
|
||||
send you the Device ID. In their UI: **Add Remote Device** with the server's ID (ours: `sync.BASE` → Actions → Show ID),
|
||||
then **Add Folder** on their clip directory (ShadowPlay/Medal/Steam saves), Sharing tab → tick the server,
|
||||
Folder Type **Send Only**, Save.
|
||||
|
||||
Our side at https://HOST/sync/: **Add Remote Device** → paste their ID, name it after them, Save. When their folder
|
||||
Our side at https://sync.BASE/: **Add Remote Device** → paste their ID, name it after them, Save. When their folder
|
||||
offer pops up: **Add** → Folder Path `/var/syncthing/clips/<username>`, Folder Type **Receive Only**, Advanced →
|
||||
Minimum Free Disk Space **300 GB**, Save. Clips start arriving; the sweep normalizes them; Jellyfin shows the
|
||||
folder as that person's show. Their own folder structure is mirrored; only video files are pulled.
|
||||
|
||||
## 5a². Gateway page — https://HOST/about/
|
||||
## 5a². Gateway page — https://media.BASE/ (the main host root; `/about/` redirects here)
|
||||
|
||||
Role-aware landing page (Discord login): everyone sees Watch / Live / Send clips / Sign out with short how-tos;
|
||||
`streamers` get the dock + OBS setup steps; `editors` the browser + multicam/timecode notes; `media-admins`
|
||||
@@ -162,7 +177,7 @@ already `.mp4`), the raw's name is appended to `.stlocal` (an `#include`d ignore
|
||||
drops it without flagging or re-pulling), and the raw is deleted locally — it still exists on the
|
||||
gamer's PC. `configure.py` creates the folder + ignore file for each streamer.
|
||||
|
||||
In the UI (https://HOST/sync/):
|
||||
In the UI (https://sync.BASE/):
|
||||
- each clips folder → path `/var/syncthing/clips/<user>`, **Folder Type: Receive Only**,
|
||||
Edit → Advanced → **Minimum Free Disk Space: 300 GB** (so clips can never fill the mirror and stall
|
||||
the recordings).
|
||||
@@ -171,8 +186,8 @@ In the UI (https://HOST/sync/):
|
||||
|
||||
## 5c. Authentik (Discord login) and Jellyfin
|
||||
|
||||
DNS: `auth` and `watch` as CNAMEs to the main host. Then run `./addCommunityMediaServer.sh` — it asks
|
||||
for both hostnames, checks DNS, generates `.env` (Authentik secret + DB password; never commit it),
|
||||
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
|
||||
hostnames filled in. (`./configure.py --full` asks for the same hostnames if you prefer.) Then:
|
||||
|
||||
@@ -285,7 +300,7 @@ from `/me/stream-token` for the logged-in Discord user (member of `streamers`).
|
||||
Bandwidth: no transcoding, every viewer gets the source bitrate (~15 Mbps) → ~30 concurrent viewers on
|
||||
the 500 Mbps line. For a crowd, restream to Twitch from OBS.
|
||||
|
||||
## 5g. Authentik forward-auth for the Caddy-gated hosts (main host, `live.`, `bot.`)
|
||||
## 5g. Authentik forward-auth for the Caddy-gated hosts (`media.`, `files.`, `clips.`, `sync.`, `live.`, `bot.`)
|
||||
|
||||
Design: **Authentik authenticates once for the whole domain; Caddy authorizes per host** from the
|
||||
`X-Authentik-Groups` header the outpost returns; **Discord roles fill the Authentik groups** at login.
|
||||
@@ -296,8 +311,8 @@ Design: **Authentik authenticates once for the whole domain; Caddy authorizes pe
|
||||
cookie domain = the parent domain (e.g. `dumpstervigilantes.rip`). Edit the Embedded Outpost →
|
||||
Applications → add it → Update. One login now covers every gated host.
|
||||
3. Per-host group requirement = `.env`: `BOT_REQUIRE_GROUP=bot-users` (default), `LIVE_REQUIRE_GROUP=`
|
||||
(empty = any logged-in member), `SYNC_REQUIRE_GROUP=media-admins` (the `/sync` Syncthing UI on the main host).
|
||||
On the main host copyparty itself authorizes by group (`editors`, `media-admins`); the dump endpoint lets
|
||||
(empty = any logged-in member), `SYNC_REQUIRE_GROUP=media-admins` (the Syncthing UI on `sync.`).
|
||||
On `files.` copyparty itself authorizes by group (`editors`, `media-admins`); the dump endpoint lets
|
||||
`admins`/`media-admins` dump anyone's night. Leave "Intercept header authentication" **off**: login is
|
||||
Discord only, no app passwords, no WebDAV. `./configure.py` apply regenerates the Caddy blocks; a user outside the
|
||||
group gets a 403 from Caddy before the app sees the request.
|
||||
@@ -325,7 +340,7 @@ Jellyfin and Gitea use OIDC providers instead, not this.
|
||||
|
||||
## 6. Editors
|
||||
|
||||
Log in with Discord at https://media.example.com/ (copyparty behind Caddy + Authentik). Access is by Authentik
|
||||
Log in with Discord at https://files.BASE/ (copyparty behind Caddy + Authentik). Access is by Authentik
|
||||
group: **editors** read streams and clips and write `/finals`; **media-admins** have full rights everywhere
|
||||
including `/trash`. Scrub any `.mp4` in the browser, download what you want.
|
||||
|
||||
@@ -371,7 +386,7 @@ Where the time comes from, best first:
|
||||
(`bin/vgstrip.py`) at the start, middle and end of each recording → the timecode is frame-accurate at the
|
||||
start and the sidecar records drift over the file. Game image untouched. OBS: dock → *Timecode band* →
|
||||
"Add band + clock source" (canvas grows by W/160 rows, a locked browser source `Vanguard Clock` →
|
||||
`/dock/clock/` sits in the band; only while not streaming). Go Live: the "Timecode band" checkbox (default on).
|
||||
`https://live.BASE/dock/clock/` sits in the band; only while not streaming). Go Live: the "Timecode band" checkbox (default on).
|
||||
2. **Server arrival time** (no band) — microseconds in the recording filename. Within ~0.1–0.3 s per streamer
|
||||
(encoder + network delay); coarse but always available.
|
||||
|
||||
@@ -535,4 +550,4 @@ evening; after 17:00 it is tonight. The 05:00 trash purge is 05:00 Eastern.
|
||||
|------|------|------|
|
||||
| normalize-sweep.timer | every 5 min | remux/re-encode finished files to the editing standard |
|
||||
| trash-purge.timer | daily 05:00 Eastern | deletes trash older than 3 days |
|
||||
| dumpd.service | always | the dump endpoint on 127.0.0.1:8080, behind Caddy at /dump |
|
||||
| dumpd.service | always | the dump endpoint on 127.0.0.1:8080, behind Caddy on `live.` at /dump, /me, /time and on `clips.` at /me/clips |
|
||||
|
||||
Reference in New Issue
Block a user