diff --git a/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-deploy.txt b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-deploy.txt new file mode 100644 index 00000000..b7b2a545 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-deploy.txt @@ -0,0 +1,16 @@ +19:23:07 jellyfin: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "media", "HDD_PATH": "/mnt/felhom-drives/media_a"} -> 202 {"ok": true, "data": {"warning": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. Normál használat mellett ez nem okoz problémát."}, "message": "Telepítés elindítva – az ál +19:33:11 jellyfin: never became deployed (state=not_deployed) + +2026/10/14 17:23:44 [WARN] [stacks] install jellyfin was INTERRUPTED by a controller restart — removing what it started (volumes kept) and reporting it (R-681) +2026/10/14 17:23:45 [WARN] [stacks] 1 install(s) interrupted by the restart were resolved and reported: [jellyfin] +2026/10/14 17:23:45 [INFO] Event pushed: app_deploy_failed (warning) [hu-only] — Alkalmazás telepítése nem sikerült: Jellyfin — interrupted by a controller restart before it finished — install it again + +19:33:32 jellyfin: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "media", "HDD_PATH": "/mnt/felhom-drives/media_a"} -> 202 {"ok": true, "data": {"warning": "Az alkalmazások csúcsterhelése meghaladhatja a rendelkezésre álló memóriát. Normál használat mellett ez nem okoz problémát."}, "message": "Telepítés elindítva – az ál +19:34:17 jellyfin: deployed, state=running, pinned={'jellyfin': 'jellyfin/jellyfin:10.11.11'} +running /var/lib/docker/volumes/jellyfin_jellyfin_cache/_data->/cache:true /var/lib/docker/volumes/jellyfin_jellyfin_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:false /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:false +2026/10/14 17:33:31 [INFO] [api] Deploy requested for stack: jellyfin +2026/10/14 17:33:32 [INFO] [stacks] Deploying stack jellyfin with 3 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH] +2026/10/14 17:33:32 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Jellyfin +2026/10/14 17:34:00 [INFO] [stacks] Stack jellyfin deployed successfully (took 28.8s) +2026/10/14 17:34:01 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Jellyfin + diff --git a/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-readback.txt b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-readback.txt new file mode 100644 index 00000000..224a09f7 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-readback.txt @@ -0,0 +1,8 @@ +auth 200 +Authorization+Token /Library/VirtualFolders 200 2 +Authorization+Token /Items 200 22 +X-Emby-Token /Library/VirtualFolders 200 2 +X-Emby-Token /Items 200 22 +X-MediaBrowser-Token /Library/VirtualFolders 200 2 +X-MediaBrowser-Token /Items 200 22 + diff --git a/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-setup.txt b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-setup.txt new file mode 100644 index 00000000..3fbfa0b1 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/E2-jellyfin-setup.txt @@ -0,0 +1,53 @@ + POST /Startup/Configuration -> 204 + GET /Startup/User -> 200 + POST /Startup/User -> 204 + POST /Startup/RemoteAccess -> 204 + POST /Startup/Complete -> 204 + POST /Users/AuthenticateByName -> 200 + POST /Library/VirtualFolders -> 204 + POST /Library/VirtualFolders -> 204 + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' + GET /Items -> 401 b'' +Series [] +Movie [] +Episodes: 0 + GET /Library/VirtualFolders -> 401 b'' + diff --git a/documentation/audits/media-all-drives-2026-10-14/S5-S6/PREDICTIONS.md b/documentation/audits/media-all-drives-2026-10-14/S5-S6/PREDICTIONS.md new file mode 100644 index 00000000..4f831672 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S5-S6/PREDICTIONS.md @@ -0,0 +1,21 @@ +# Predictions — written before S5/S6 (2026-10-14 ~19:45 CEST, controller 0.311.0, agent 0.156.1) + +S5 — Media B's virtual disk is detached while everything runs (`qm set 341 --delete scsi3`; the volume moves to `unused0`) +- The agent sees the drive go; the controller's drive gate marks /mnt/felhom-drives/media_b DISCONNECTED and stops + NOTHING: no app has HDD_PATH on media_b (all six media apps carry HDD_PATH=media_a). A `storage_disconnected` event + goes to the hub → a mail. FileBrowser is re-synced without media_b. +- The media containers keep their old bind of media_b: reads of B's files fail (I/O error) or the folder looks empty; + they do not crash. Sonarr/Radarr: root folder B „missing" in health; American Horror Story / Matchbox files missing. +- Plex: its automatic scan is off (R-1037), so NO scan runs and NO item is removed. Plex's „empty trash after every scan" + is on by default — it would remove B's items only if a scan ran. +- Jellyfin: real-time monitoring is on by default; if it scans, it may REMOVE B's items from its library. +- Nothing is written onto the guest's own disk under /mnt/felhom-drives/media_b (the gate exists to prevent that), unless + an app keeps writing into its stale bind — qBittorrent/Transmission are seeding from B and may log errors. +- The whole-guest backup is not affected (mp8 is excluded); tonight's app backups skip nothing on B (no app keeps data + there by HDD_PATH). + +S6 — the same volume is re-attached to the same slot (`qm set 341 --scsi3 nvme-scratch:341/vm-341-disk-4.raw,backup=0`) +- The agent re-mounts it and binds it under the parent again; the controller clears DISCONNECTED; it restarts only the + apps IT stopped — none. +- So the running media containers still do NOT see B until they are recreated (their bind was made at start). + Expected: the restart notice of decision 238 must also cover „a drive came back". diff --git a/documentation/audits/media-all-drives-2026-10-14/tools/jfsetup.py b/documentation/audits/media-all-drives-2026-10-14/tools/jfsetup.py new file mode 100644 index 00000000..7f247289 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/tools/jfsetup.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +"""jfsetup.py — S5 prep: Jellyfin's first-run wizard and two libraries, by its own API, INSIDE the guest. +The admin password is generated on DooPlex (kept ONLY in ~/.felhom-tester1/secrets.json, 0600) and substituted into +this file's copy on the box for the run; the copy is deleted afterwards. Nothing secret is printed.""" +import json, subprocess, time, urllib.parse, urllib.request +PW = "__JF_PW__" +ip = subprocess.run("docker inspect jellyfin -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'", shell=True, + capture_output=True, text=True).stdout.strip() +B = f"http://{ip}:8096" +AUTH = 'MediaBrowser Client="felhom-test", Device="wire", DeviceId="felhom-wire-1", Version="1"' + + +def call(m, path, body=None, tok=None): + h = {"Content-Type": "application/json", "Authorization": AUTH + (f', Token="{tok}"' if tok else "")} + r = urllib.request.Request(B + path, data=json.dumps(body).encode() if body is not None else None, headers=h, method=m) + try: + with urllib.request.urlopen(r, timeout=60) as x: + t = x.read().decode(); print(f" {m} {path.split('?')[0]} -> {x.status}"); return json.loads(t) if t else None + except urllib.error.HTTPError as e: + print(f" {m} {path.split('?')[0]} -> {e.code} {e.read()[:150]}"); return None + + +for _ in range(30): + try: + urllib.request.urlopen(B + "/health", timeout=5); break + except Exception: + time.sleep(3) +call("POST", "/Startup/Configuration", {"UICulture": "en-US", "MetadataCountryCode": "HU", "PreferredMetadataLanguage": "hu"}) +call("GET", "/Startup/User") +call("POST", "/Startup/User", {"Name": "admin", "Password": PW}) +call("POST", "/Startup/RemoteAccess", {"EnableRemoteAccess": True, "EnableAutomaticPortMapping": False}) +call("POST", "/Startup/Complete") +a = call("POST", "/Users/AuthenticateByName", {"Username": "admin", "Pw": PW}) +tok = a["AccessToken"] +for name, ct, sub in (("TV Shows", "tvshows", "tv"), ("Movies", "movies", "movies")): + q = urllib.parse.urlencode({"name": name, "collectionType": ct, "refreshLibrary": "true"}) + call("POST", f"/Library/VirtualFolders?{q}", {"LibraryOptions": {"PathInfos": [ + {"Path": f"/mnt/felhom-drives/{d}/userdata/media/{sub}"} for d in ("media_a", "media_b")]}}, tok) +for _ in range(40): + time.sleep(6) + s = call("GET", "/Items?Recursive=true&IncludeItemTypes=Series,Movie,Episode&Fields=Path", tok) or {} + items = s.get("Items", []) + eps = [i for i in items if i["Type"] == "Episode"] + if len(eps) >= 16: + break +for t in ("Series", "Movie"): + print(t, sorted(i["Name"] for i in items if i["Type"] == t)) +print("Episodes:", len(eps)) +for v in call("GET", "/Library/VirtualFolders", tok) or []: + print("library", v["Name"], v.get("Locations"))