diff --git a/FRESH-INSTALL.md b/FRESH-INSTALL.md new file mode 100644 index 0000000..474abbe --- /dev/null +++ b/FRESH-INSTALL.md @@ -0,0 +1,123 @@ +# Fresh install — from an empty OVH box to a working stack with the deploy pipeline + +Everything in order. Scripted steps are commands; manual steps are click lists. `HOST` = your main hostname +(ours: `ovh1.dumpstervigilantes.rip`), `BASE` = the parent domain. Detailed background for each part is in +README.md (section numbers given). + +## 0. Before touching the server +- DNS: `A` record for `HOST` → the server IP; `CNAME`s `auth`, `watch`, `git`, `bot`, `live` → `HOST`. + Optional: the bare domain `A` → server IP (it redirects to the watch site or `/about/`). +- Discord developer portal → New Application (one app serves Authentik login, the Jellyfin plugin's bot and the + Discord bot): + - OAuth2 → Redirects: `https://auth.BASE/source/oauth/callback/discord/` (the last segment is the Authentik + source slug — keep them identical). Copy Client ID + Client Secret. + - Bot → Reset Token (copy it); enable **Server Members** intent; OAuth2 URL generator: scope `bot`, permissions + View Channels + Send Messages + Embed Links + Attach Files → invite it to the server. + - Discord → User Settings → Advanced → Developer Mode on; right-click the server → Copy Server ID; Server + Settings → Roles → Copy Role ID for each role you will map to a group. +- If this is a **rebuild**, have the backups ready: `.env`, `/srv/appdata`, `/srv/media`, `/etc/vanguard/clip-quotas.json`. + Restoring `.env` + `/srv/appdata` makes sections 5–7 unnecessary (that state lives there). + +## 1. OS (README §1) +OVH manager → Reinstall → Debian 13, custom partitioning, distribution kernel. RAID 1: +`/boot` 1 GB ext4 · `/` 250 GB ext4 · swap 8 GB per disk · `/srv` rest xfs. Add your SSH key. You get the `debian` user with sudo. + +## 2. Code onto the box +Fresh box has no deploy key yet, so copy the repo in: + + scp -r vanguard-stream debian@HOST:~/ # from a machine that has the repo (or git clone over https with a token) + +## 3. Host install (README §2, §4) + + ssh debian@HOST + cd ~/vanguard-stream && sudo bash install.sh + +Packages, Docker, timezone (Eastern), uid 1000, `/srv` folders, host scripts and timers, sysctl, live-portal +build, **firewall** (keep this session open; test a new ssh in a second terminal). Log out and back in once (docker group). + +## 4. Configure + start (README §3) + + cd ~/vanguard-stream && ./configure.py --full + +Asks: main hostname; `auth`/`watch`/`git`/`bot`/`live` hostnames (defaults); bare domain (optional) and where it +redirects; Discord bot token + server ID (optional now, needed for the plugin feed); "is live" webhook (optional). +Generates `.env` with every secret, the configs, starts all containers, waits, then runs the post-start fixups +(Syncthing GUI auth off, Gitea admin user, Jellyfin plugin). `./configure.py --show` prints every credential +you will need below (akadmin password, Gitea admin, MariaDB, bot DB). + +Check: `docker compose ps` → all `Up`; `https://auth.BASE/`, `https://watch.BASE/`, `https://git.BASE/`, +`https://HOST/` answer with certificates (Caddy fetches them itself, give it a minute). + +## 5. Authentik (README §5c, §5g) — manual, ~20 minutes +Log in at `https://auth.BASE/` as `akadmin` with `AUTHENTIK_BOOTSTRAP_PASSWORD` from `.env` (change it: user menu → settings). +1. Directory → Federation & Social login → Create → **Discord OAuth Source**: name `Discord`, slug `discord`, + consumer key/secret = the Discord app's Client ID/Secret, scopes `guilds guilds.members.read`, user matching + "link on unique identifier", flows default-source-authentication / default-source-enrollment. +2. Flows & Stages → Stages → `default-authentication-identification` → Sources: add Discord, tick "Show sources' + labels". (No Discord button appears until this is done.) +3. Flows & Stages → Stages → `default-source-enrollment-write` → User type **Internal**, user path `users`. +4. Directory → Groups: `streamers`, `editors`, `media-admins`, `admins`, `bot-users`, `git-users`, `git-admins`. + Nesting: `admins` parent `authentik Admins`; `authentik Admins` parent `git-admins` (admins inherit everything). +5. Customization → Policies → Create Expression Policy ×2 from the repo: + `discord-guild-member` (`authentik/discord-guild-policy.py`, set your server ID) and + `discord-roles-to-groups` (`authentik/discord-roles-to-groups.py`, set server ID + role-id→group table). + Tick "Execution logging" on both. +6. Flows & Stages → Flows → `default-source-enrollment` and `default-source-authentication`: bind both policies + (guild first), **Policy engine mode: all**. +7. Applications → Outposts → edit "authentik Embedded Outpost" → config `authentik_host: https://auth.BASE`. +8. Applications → Create with provider → `Vanguard sites`: **Proxy Provider**, implicit consent, + **Forward auth (domain level)**, authentication URL `https://auth.BASE`, cookie domain `BASE`, + invalidation flow `default-provider-invalidation-flow`. Edit the Embedded Outpost → add the application. +9. Applications → Create with provider → `Jellyfin`: OAuth2/OpenID, implicit consent, Confidential, redirect + `https://watch.BASE/sso/OID/redirect/authentik`, default signing cert, scopes `openid email profile`. Keep ID + secret. +10. Applications → Create with provider → `Gitea` (slug `gitea`): OAuth2/OpenID, same settings, redirect + `https://git.BASE/user/oauth2/authentik/callback`. Bind groups `git-users` + `git-admins` on the application. +11. Test in a private window: `https://auth.BASE/` → Discord → your user exists and is Internal. Add yourself to + `admins` (and `streamers`, `media-admins` as needed) or give yourself the Discord roles; sign out and back in. + `https://HOST/whoami` shows your groups. + +## 6. Jellyfin (README §5c) — manual, ~10 minutes +`https://watch.BASE/` → wizard: admin user; libraries `/media/streams` and `/media/clips` as **Shows** with +metadata downloaders = only "Vanguard Metadata" (Series/Season/Episode), image fetchers Vanguard Metadata +(Series) + Screen Grabber (Episode), real-time monitoring on; `/media/finals` as Home Videos, fetchers off. +Dashboard → Playback → Transcoding: hardware acceleration none. Dashboard → Plugins: Vanguard Metadata is +installed → its page: Discord bot token + server ID, feed channel ID, public URL `https://watch.BASE`. +Plugins → Repositories → add `https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json` +→ Catalog → SSO Authentication → install → `docker compose restart jellyfin`. Plugins → SSO-Auth → add OID provider +`authentik`: endpoint `https://auth.BASE/application/o/jellyfin/`, client ID/secret, roles claim `groups`, +admin roles `admins`, enabled folders = the 3 libraries, Scheme Override `https`, Port Override EMPTY, Save. +Dashboard → General → Branding: paste `jellyfin/branding-disclaimer.html` (set the hostname) and `jellyfin/branding.css`. +Test: `https://watch.BASE/sso/OID/start/authentik`. Scheduled Tasks → run "Vanguard: snapshot Discord events", +"roll seasons", "enforce playback policy" once. + +## 7. Gitea (README §5e) — manual, ~10 minutes +Log in at `https://git.BASE/` as `GITEA_ADMIN_USER` / `GITEA_ADMIN_PASSWORD` from `.env`. +1. Site Administration → Identity & Access → Authentication Sources → Add: OAuth2, name `authentik`, OpenID + Connect, client ID/secret from step 5.10, discovery URL + `https://auth.BASE/application/o/gitea/.well-known/openid-configuration`, Skip local 2FA, group claim + `groups`, administrator group `git-admins`. +2. Create the repo `techy/Media-Server-Setup` (empty) and push this repo to it from your dev box: + `git remote add origin ssh://git@git.BASE:2222/techy/Media-Server-Setup.git && git push -u origin main` + (your SSH key under Settings → SSH keys). +3. Runner: `./registerGiteaRunner.sh` on the server → Site Administration → Actions → Runners shows it idle. + +## 8. Pipeline (README "Deployment") +1. On the server: `./switchToClone.sh key` → add the printed key as a read-only **Deploy Key** on the repo + → `./switchToClone.sh clone` (keeps `.env`, generated configs, plugin zips) → `./configure.py --apply`. +2. Gitea → repo → Settings → Webhooks → Add → Gitea: URL `http://:8081/deploy` + (`docker network inspect vanguard-stream_default -f '{{(index .IPAM.Config 0).Gateway}}'`), secret = + `DEPLOY_WEBHOOK_SECRET` from `.env`, push events, branch `main`. Test delivery → 202 "auto-deploy disabled". +3. Manual deploys from now on: push, then `sudo vanguard-deploy pull` on the server. When trusted: + `DEPLOY_AUTO=1` in `.env` → `./configure.py --apply`; CI: `git mv .gitea/draft-workflows/ci.yml .gitea/workflows/ci.yml`. + +## 9. Streamers, gamers, editors (README §5, §5a, §6) +- Streamers: OBS → WebSocket server on; Docks → Custom Browser Dock `https://HOST/dock/` → Discord login → + Settings: OBS WebSocket password; Timecode band → "Add band + clock source" (not streaming). Then title → Go Live. +- Gamers: `https://HOST/clips/` → paste their Syncthing Device ID → accept device + folder on their PC (Send Only). + Syncthing UI (`https://HOST/sync/`, media-admins): Actions → Settings → Connections → incoming rate limit 12500 KiB/s. +- Everyone: `https://HOST/about/`. + +## 10. Afterwards +- Pin `AUTHENTIK_TAG` in `.env` to the running version. Rotate any secret that was ever pasted into chat. +- Back up `.env`, `/srv/appdata`, `/srv/media` (home archive puller: README §7). +- `STATUS.md` = what runs and what is unfinished; `check.sh` before any commit. diff --git a/README.md b/README.md index 9ee9a8b..c19618a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # vanguard-stream — SYS-1 (Xeon E-2136, 32 GB ECC, 2×4 TB SATA soft RAID) +> **FRESH-INSTALL.md** = the ordered walkthrough from an empty box to a working stack and pipeline, manual steps included. > **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