Media drive events, night 2026-10-14: 15 §10 (event x app, measured), K4-K8 evidence, rows R-1044..R-1049, narrowed R-1022/1023/1024/1026/1028/1037/1038, night report
gates / gates (push) Successful in 6m34s
gates / gates (push) Successful in 6m34s
- 15-media-library.md §10: what the box does on each drive event, per app, with the measured call; the guards that ARE needed and the ones that are NOT (K1: nothing lost while a drive is gone). - Evidence K4 (dead-drive check: mountinfo `shutdown`, stat unreliable), K5 (restart), K6 (unclaimed Plex), K7 (old Sonarr/Radarr repair on the bench), K8 (hub received the drive-return events and mailed none), K9 (files identical). - nightly-rotation.md runs scripts/https_only_check.py first every night; scripts/CHANGELOG.md entry. - STATUS, CONTEXT (decisions 244-246), REPORT-night-2026-10-14.md. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
> learn. **No document covered this before 2026-10-14** — itself a finding: the four templates were written one by one
|
||||
> and never tested together (R-1014). Rulings: `09` §3 decisions 237–241. Measured on Tester 1:
|
||||
> `audits/media-all-drives-2026-10-14/`. Marks as in `01`: **[DESIGN]** a decision or proposal; **[FACT]** read in a
|
||||
> named file or measured. **Status: DRAFT** — measured end to end on Tester 1; the layout of §2 is RULED (decision 242) and live for one drive.
|
||||
> named file or measured. **Status: DRAFT** — measured end to end on Tester 1; the layout of §2 is RULED (decision 242) and live for one drive; the drive events of §10 measured (decision 245, option A).
|
||||
|
||||
## 1. The problem
|
||||
|
||||
@@ -82,8 +82,8 @@ container does not see a drive added later (guest propagation is `shared,slave`,
|
||||
not change; it logged success in 0.1 s while the containers kept their start time. Stop + start works. The notice of
|
||||
decisions 238–239 needs a real restart (`up -d --force-recreate` once the per-drive binds are in the compose).
|
||||
|
||||
**[DESIGN]** The restart notice (decision 238) fires on: a drive added, a drive removed, and — pending S6 — a drive
|
||||
that came back. Each drive card carries „apps restarted since this drive was added: yes/no" with the button
|
||||
**[DESIGN]** The restart notice (decision 238) fires on: a drive added, a drive removed, and a drive that came back
|
||||
(S6 and K1-c/K5 measured it: after a return nothing sees the drive until restarted; §10 has the per-app actions). Each drive card carries „apps restarted since this drive was added: yes/no" with the button
|
||||
(decision 239).
|
||||
|
||||
## 5. A drive is pulled / comes back [FACT, measured on Tester 1 2026-10-14, `audits/media-all-drives-2026-10-14/S5-S6/`]
|
||||
@@ -179,3 +179,51 @@ Call sites that assume ONE `HDD_PATH`/`USERDATA_PATH` per app (controller main a
|
||||
An installed media app keeps its `HDD_PATH` (its config's unit lives there) and gains binds for the other drives at its
|
||||
next recreate. Because the paths are the same everywhere (§2 reason 2), nothing Sonarr or Radarr stored changes. A
|
||||
household's existing files under `media/torrents` stay where they are.
|
||||
|
||||
## 10. What the box does on each drive event [FACT, measured 2026-10-14 night on Tester 1 + the bench; `09` §3 decision 245 — option A]
|
||||
|
||||
Evidence: `audits/media-drive-events-2026-10-14/` (K1–K7). „Idempotent" = a second run changes nothing (K2: run 2 left every entry, folder and library as it
|
||||
was). „Household" = a step only the household can take.
|
||||
|
||||
| event → app | action | measured call | idempotent | household | evidence |
|
||||
|---|---|---|---|---|---|
|
||||
| **added** — every media app | the compose gains the drive's bind → `up -d` recreates it (the compose CHANGED, so today's restart works) | controller compose `up -d` | yes | no | K5-1: 1–10 s per app |
|
||||
| added — FileBrowser | already: `SyncFileBrowserMounts` recreates on a new path | — | yes | no | K2-2 |
|
||||
| added — the drive's folders | `torrents/{tv,movies}`, `transmission/{tv,movies}` made (the skeleton lacks them for a drill template; Sonarr raises a „remote path mapping" health error without them) | mkdir, owner 1000, 2775 | yes | no | K2-6 |
|
||||
| added — qBittorrent | categories `tv-<d>`, `movies-<d>`: save path AND incomplete path on that drive; automatic torrent management ON | `createCategory`/`editCategory`, `setPreferences auto_tmm_enabled` | yes (re-writes equal values) | no | K2-6/7 |
|
||||
| added — Transmission | no per-drive setting; one incomplete folder → OFF, written into `settings.json` at start (an RPC `session-set` was LOST after the 17:25 power cut) | entrypoint `jq` | yes | no | K1 note, wire run 3 |
|
||||
| added — Sonarr, Radarr | root folder, tag `<d>`, „qBittorrent <d>" (category) + „Transmission <d>" (directory, category CLEARED), min free space | `/api/v3/rootfolder`, `/tag`, `/downloadclient` (Host: <app>) | yes (check before create) | no | K2-6/7 |
|
||||
| added — Prowlarr | nothing per drive (it feeds Sonarr/Radarr) | — | — | indexers | H |
|
||||
| added — Plex | the drive's folders added to the EXISTING libraries, a scan, „scan when a change is detected" ON | `PUT /library/sections/<id>?…&location=…` (all locations repeated), `/refresh`, `PUT /:/prefs FSEventLibraryUpdatesEnabled=1` | yes | no — but the token is the server's own `PlexOnlineToken` (a household-account token; 401 without one, K2-5) → R-1046 | K2-6 |
|
||||
| added — Jellyfin | the drive's folders added, a scan; an API key for the box | `POST /Library/VirtualFolders/Paths`, `/Library/Refresh`, `POST /Auth/Keys?app=felhom` | yes | no IF the box made the first admin itself; else ONE step (an API key) | K2-6 |
|
||||
| **safe removal** — qBittorrent, Transmission | pause ONLY the torrents saved on that drive | qBit `torrents/stop` by `save_path`; Transmission `torrent-stop` by `downloadDir` | yes | no | K3-2 |
|
||||
| safe removal — every media app | **recreate WITHOUT the drive's bind BEFORE the unmount**: today's eject detaches the guest's bind and keeps the raw mount, while the running apps still HOLD the filesystem (they saw its files after the „safe" removal) | render binds for present drives; `up -d` | yes | no | K3-3 |
|
||||
| safe removal — the drive | eject today also DEREGISTERS it; after a re-attach the agent mounts it again, but the household must „Meglévő meghajtó csatolása" (`register-mounted`) | — | — | **yes, today** | K3-5 |
|
||||
| safe removal — Plex, Jellyfin | nothing (they keep every item, §5) | — | — | no | K1 |
|
||||
| **pulled** — every app | NOTHING is lost: Plex (autoEmptyTrash on or off), Jellyfin, Sonarr/Radarr refresh + rescan (unmonitor on or off), torrents — the guest keeps a DEAD mount and every read is an I/O error, which every app treats as „unavailable" | — | — | no | K1 |
|
||||
| pulled — the box | knows which apps hold a dead view WITHOUT disk I/O: the guest's `/proc/self/mountinfo` shows `shutdown` on the drive; each container's `/proc/<pid>/mountinfo` shows the old device + `shutdown` | read `/proc` only | yes | no | K4 |
|
||||
| pulled — restart an app | still starts while the DEAD mount is there (K1: Jellyfin); FAILS (500, „error while creating mount source path … permission denied") once the path is gone (after an eject) — the app is down until the drive returns | — | — | no | K3-4 |
|
||||
| **came back** — every media app + FileBrowser | stop+start, or `up -d --force-recreate` WITH the controller's environment (a bare `compose up` lost every `${…}` value: routes `Host(\`.\`)`, an empty password — K5-2); today's restart (`up -d`, compose unchanged) does nothing (R-1026) | `StopStack`+`StartStack`, or `composeExecCustomEnv(…, "up","-d","--force-recreate")` | yes | no | K5-2/3: 2–10 s per app, FileBrowser 5 s |
|
||||
| came back — qBittorrent, Transmission | nothing: every torrent seeds again, no „missing files", no re-check | — | — | no | K1-c, K3-6 |
|
||||
| came back — a registered drive | the gate re-binds it by itself; an EJECTED one needs the household's attach | — | — | after an eject | K1-c, K3-5 |
|
||||
| came back — a playing stream | is CUT by the restart (curl exit 56), Jellyfin answers again 22 s later → offer „later", or check active sessions first | Jellyfin `/Sessions`, Plex `/status/sessions` | — | no | K5-4 |
|
||||
|
||||
**Guards the measurements show ARE needed:** (1) never start a media app with a bind to an absent path — render one bind per
|
||||
PRESENT drive (the FileBrowser pattern); (2) a safe removal recreates the media apps without the drive, then unmounts the
|
||||
raw device (R-1044); (3) every restart through the controller's environment; (4) a returned drive restarts every container that binds
|
||||
it, FileBrowser included; (5) Transmission's settings written at start, not by RPC. **Guards NOT needed — do not build
|
||||
them:** a scan or „empty trash" suppression for Plex/Jellyfin, an unmonitor guard for Sonarr/Radarr, pausing torrents on
|
||||
a pull — K1 measured no loss in any of them while the dead mount stands. **The one condition behind that:** the guest keeps
|
||||
a DEAD mount (I/O errors). R-1038's fix must NOT turn a pulled drive into an EMPTY folder that a scan could read as „all
|
||||
files deleted" — keep the dead mount, or stop the media apps first.
|
||||
|
||||
**Old installs (K7):** Sonarr/Radarr from before app-catalog `bc54c01` keep their library at `/media/tv` / `/media/movies`;
|
||||
after the update the root folder is missing (health „Missing root folder"), records stay. Repair through the apps' own API,
|
||||
no file moved: add the same-path root folder; `PUT /series/editor` (`/movie/editor`) `rootFolderPath`, `moveFiles:false`;
|
||||
rescan; delete the old root folder; Radarr's collections keep the old root (a separate setting) and the health page shows the
|
||||
old warning until its next check. File and inode unchanged.
|
||||
|
||||
**Plex without the race (K6):** an unclaimed Plex starts with no code and answers every request from a private address
|
||||
WITHOUT a token — it must never be routed publicly while unclaimed; `POST /myplex/claim?token=<code>` on the RUNNING server
|
||||
claims it (the route answered and called plex.tv; a real code needs the household's account — the morning step). So the box
|
||||
asks for the code only once the server is up: no 4-minute race with the pull.
|
||||
|
||||
Reference in New Issue
Block a user