diff --git a/AGENTS.md b/AGENTS.md index 1f4a587..0206de6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -851,6 +851,6 @@ history. | Benchmarking (only when ordered) | **Stop and mask the service first**, bench, unmask, restart, verify health. | | Deleting a file | **Move it to trash** — `.AGENTTRASH/` (in-repo) or `~/.AGENTTRASH/YYYYMMDD-topic`. Never `rm`. | | Relayout the movie library | **Census → plan → read the exceptions → `scripts/media-apply.py`** — renames only, journal before every move; never hand-rename, never copy. | -| Making media metadata visible to both media servers | **Bind `/var/lib/media-meta/view`** — the overlay `scripts/media-view.sh` installs (array below, NVMe above). Never write the array; never delete through the view. | +| Making media metadata visible to both media servers | **Bind `/var/lib/media-meta/view`** — the overlay `scripts/media-view.sh` installs (array below, NVMe above). Never write the array; never delete through the view. **Not yet deployed on Dankserver: the live quadlets still bind the array read-only — see [[Media Metadata View]].** | | Tests exist | **Run them** — verify tests pass after every change. Do not commit failing tests. | | Real secrets | **Never commit** — host `.env` files; `.env.example` holds placeholders only. | diff --git a/serverAppConfigs-docs/Dankserver/Jellyfin.md b/serverAppConfigs-docs/Dankserver/Jellyfin.md index 6c9e2a2..b2b6af3 100644 --- a/serverAppConfigs-docs/Dankserver/Jellyfin.md +++ b/serverAppConfigs-docs/Dankserver/Jellyfin.md @@ -18,7 +18,7 @@ clients. public vhost `jelly.franksdank.cloud` behind the [[Anubis]] guard chain → [[Authentik]] outpost (post-auth route proxies `localhost:8096`). -## Facts (verified from the live unit, 2026-09-03) +## Facts (the repo quadlet; the live unit is still the pre-view generation — see Data layout) | Item | Value | |------|-------| @@ -26,7 +26,7 @@ public vhost `jelly.franksdank.cloud` behind the [[Anubis]] guard chain → | Image | `ghcr.io/jellyfin/jellyfin:latest` | | Network | `dankserver_appnet`; publishes 8096/tcp, 8920/tcp, 7359/udp, 1900/udp | | Data roots | `/home/dank/Containers/jellyfin/config:/config`, `/home/dank/Containers/jellyfin/cache:/cache` | -| Media | `Volume=/var/lib/media-meta/view:/data` — the [[Media Metadata View]] overlay, **writable**; sidecars land on the NVMe, the [[Entropy Volume]] array underneath stays read-only and untouched | +| Media | `Volume=/var/lib/media-meta/view:/data` in git — the [[Media Metadata View]] overlay, **writable**, sidecars land on the NVMe and the [[Entropy Volume]] array underneath stays untouched. **The live unit is still pre-view: `/mnt/entropy:/data:ro`** — nothing is written into the library until the view is deployed | | GPU | none — no `AddDevice`; CPU decode only (jellyfin-ffmpeg path) | | Env quirks | `DOTNET_SYSTEM_IO_DISABLEFILELOCKING=true`, `LD_PRELOAD=libjemalloc.so.2`, `MALLOC_TRIM_THRESHOLD_=131072` kept verbatim from the compose-era container (zero-drift cutover) | | Health | `curl -f http://localhost:8096/health` every 30 s | @@ -36,23 +36,27 @@ public vhost `jelly.franksdank.cloud` behind the [[Anubis]] guard chain → ``` /home/dank/Containers/jellyfin/config -> /config (JELLYFIN_DATA_DIR) /home/dank/Containers/jellyfin/cache -> /cache (JELLYFIN_CACHE_DIR, XDG_CACHE_HOME) -/var/lib/media-meta/view -> /data (the SSD-backed overlay view) +/var/lib/media-meta/view -> /data (git: the overlay view) +/mnt/entropy -> /data (live today: the array, read-only) ``` -Library folders point at `/data` paths, which are the view's paths: stable -across container recreation, and identical to the raw array layout below it. +Library folders point at `/data` paths: stable across container recreation and +identical to the raw array layout below. Which side of that path the container +is on is the open question on this host — the installed unit binds the read-only +array, and `scripts/media-view.sh status` reports `NOT MOUNTED` (2026-10-31). -`Local metadata` — save metadata to media folders — is enabled, and it works -because `/data` is the [[Media Metadata View]] rather than the read-only array: -`movie.nfo`, `tvshow.nfo`, `episode.nfo` and artwork are written once, on the -NVMe upper layer, and [[Plex]] reads the same files. Jellyfin is the fleet's -only metadata writer. +`Local metadata` — save metadata to media folders — is enabled, and it is the +reason the view exists: `movie.nfo`, `tvshow.nfo`, `episode.nfo` and artwork +written once on the NVMe upper layer are files [[Plex]] can read too. While +`/data` is bound read-only every item update logs the error below and no sidecar +is written; Jellyfin becomes the fleet's only metadata writer with the deploy +that raises the view. **Failure signature:** `System.IO.IOException: Read-only file system : '/data/Movies//movie.nfo'` means the container is binding the raw array -instead of the view — the view is unmounted or the quadlet drifted. That used to -be the expected state (pre-view, every item update logged it); today it is a -fault. Run `scripts/media-view.sh status`. +instead of the view. On Dankserver that is today's expected state — the view has +never been installed (`scripts/media-view.sh status`). Once it is deployed, the +same line is a fault: the quadlet drifted or the mount died. ## Ports & access diff --git a/serverAppConfigs-docs/Dankserver/Media Metadata View.md b/serverAppConfigs-docs/Dankserver/Media Metadata View.md index 296d112..71aceb2 100644 --- a/serverAppConfigs-docs/Dankserver/Media Metadata View.md +++ b/serverAppConfigs-docs/Dankserver/Media Metadata View.md @@ -20,6 +20,19 @@ The mount point is not a volume and holds nothing while unmounted. It sits insid merged door are one directory — and so the mount table grows no new `/mnt` entry next to the array. +**Not deployed on Dankserver — verified 2026-10-31.** `scripts/media-view.sh +status` answers `view: /var/lib/media-meta/view NOT MOUNTED`, +`/var/lib/media-meta` does not exist, and the installed quadlets under +`~/.config/containers/systemd/` still carry the pre-view binds — `/mnt/entropy:/data:ro` +for [[Jellyfin]], `/mnt/entropy:/mnt/entropy:ro` for [[Plex]]. Everything below +is the shape git describes, not the shape the servers run. The cutover is an +ordinary deploy: `deploy.sh dankserver --service jellyfin|plex` runs +`ensure_media_view` (`install` then `verify`) before it restarts the container, +so it must not be fired through the middle of a library scan. Until that deploy +lands there is no upper layer, so no relayout can leave ghost metadata and +`reap` has nothing to do — `media-apply.py` prints that follow-up only when the +view really is a mountpoint. + ## Shape | Item | Value | diff --git a/serverAppConfigs-docs/Dankserver/Media Relayout.md b/serverAppConfigs-docs/Dankserver/Media Relayout.md index 9933e84..5c747e0 100644 --- a/serverAppConfigs-docs/Dankserver/Media Relayout.md +++ b/serverAppConfigs-docs/Dankserver/Media Relayout.md @@ -113,7 +113,7 @@ curl -s -K /tmp/jf.hdr -o /dev/null -w "jellyfin scan: %{http_code}\n" shred -u /tmp/jf.hdr ``` -`204` is acceptance; the scan then runs on the server's own schedule. +`204` is acceptance — anything else is no scan queued, `401` included (a handoff logged `401` with the same key and form that answered `200` seconds later), so re-issue rather than diagnose. The scan then runs on the server's own schedule. ### Proving the move landed @@ -457,10 +457,10 @@ own journal, behind the music writer, and then refreshes the Plex section. - **Sidecar metadata does not follow a rename, because it lives on another filesystem.** The media servers write `.nfo` and artwork through the [[Media Metadata View]] (NVMe upper layer); this tool moves bytes on the raw - array path. After any sweep or TV move, run `scripts/media-view.sh reap`, then - the same command with `--apply`: the sidecars left at the old coordinates become - ghost folders — artwork and an `.nfo` with no video — that both servers browse - and report as content. + array path, so where that view is mounted a sweep or a move must be followed by + `scripts/media-view.sh reap --apply` — stranded sidecars become ghost folders + that both servers browse and report as content. Whether this host mounts the + view at all is stated on that page; `media-apply.py` stays silent when it does not. - **Nothing deletes through the metadata view.** A delete there writes an overlayfs whiteout in the upper layer, which hides the array's copy without removing it; the file returns when the view is unmounted. Deletions in this pipeline happen diff --git a/serverAppConfigs-docs/Dankserver/Plex.md b/serverAppConfigs-docs/Dankserver/Plex.md index ec35283..0e7b831 100644 --- a/serverAppConfigs-docs/Dankserver/Plex.md +++ b/serverAppConfigs-docs/Dankserver/Plex.md @@ -27,7 +27,7 @@ public traffic can be fronted by Caddy like every other vhost. | Runtime | podman quadlet, `plex.service` (user manager) — migrated from the native `plexmediaserver.service` OS-package unit (2026-09) | | Image | `lscr.io/linuxserver/plex:1.43.5.11029-6bf8af3f0-ls329` (pinned; verified == the registry's `latest` manifest digest at pin time: `sha256:8f655bad…e9e2334`) | | Data root | `/var/lib/plexmediaserver` bind-mounted **in place** as `/config` (RW) — the migration moved zero bytes | -| Media | `/var/lib/media-meta/view` (the [[Media Metadata View]] overlay) bound at the same absolute path `/mnt/entropy` **read-only** — every library path in the PMS database stayed valid, zero re-scan | +| Media | git: `/var/lib/media-meta/view` (the [[Media Metadata View]] overlay) bound at the same absolute path `/mnt/entropy` **read-only**; live today: `/mnt/entropy` itself, read-only — the view has never been installed. Either way every library path in the PMS database stays valid, zero re-scan | | Identity | fleet rootless model — container root maps to the `dank` user (1000:1000); the deployer chowns the data root on first cutover | | Port | `32400/tcp` published on `0.0.0.0` — zero client reconfiguration | | Network | `dankserver_appnet`, alias `plex`; Tautulli reaches it at `http://plex:32400` | @@ -40,7 +40,8 @@ public traffic can be fronted by Caddy like every other vhost. The migration moved zero bytes: the container bind-mounts the native data root where it already lived, and the media archive re-mounts at `/mnt/entropy` inside the container — only the HOST side of that bind has moved -(array, then the overlay view), so every absolute library path in the +(the array today; the overlay view in git has never been deployed), so every +absolute library path in the PMS database keeps resolving. The one metadata change: rootless podman cannot map the real host uid 997, so the fleet model applies — container root runs as the `dank` user, and the deployer adopts the data root with