diff --git a/documentation/architecture/09-update-architecture.md b/documentation/architecture/09-update-architecture.md index 1c88bf0a..d16b5c9c 100644 --- a/documentation/architecture/09-update-architecture.md +++ b/documentation/architecture/09-update-architecture.md @@ -915,6 +915,18 @@ its length, and both fixes cost something the household would notice — operato 127. **The agent's three by-design abilities (`03` §3.1) stay for now**; revisited before the first paying customer. *Operator ruling 2026-10-05.* (R-861) +### 2026-10-14 (evening, ~21:20) — the media folders and the N100 (recorded before the work; the media-folder-fix brief) + +242. **D9 = A, with a limit.** The media apps keep today's `media` folder — nothing on a drive moves. Sonarr and Radarr + see the drive's whole `media` folder (they must: download and library on ONE mount, or every import is a full + copy); **the download clients (qBittorrent, Transmission) see only their own download folder**, so they can never + write into the family's photos, music or books. Every app sees a file at the same path as on the box. + Not chosen: B — a separate video folder (a one-time move on every box with files). *Operator ruling 2026-10-14.* + (`15-media-library.md` §2; R-1014) +243. **The N100 lived-in setup waits until the all-drives build has been tested on the Tester 1 rig.** Until then the + N100 stays Tier 0 and is not fenced (it may host a second test rig). This replaces decision 240's timing. + *Operator ruling 2026-10-14.* + ### 2026-10-14 (afternoon) — media apps on every drive (recorded before the work; the media-all-drives brief) 237. **Media apps see every drive.** Each media app (`15-media-library.md` §3) binds every enrolled drive's media diff --git a/documentation/architecture/15-media-library.md b/documentation/architecture/15-media-library.md index 6bf72717..83dc45d5 100644 --- a/documentation/architecture/15-media-library.md +++ b/documentation/architecture/15-media-library.md @@ -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 operator's layout decision (§2, STATUS D9) is open. +> 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. ## 1. The problem @@ -41,16 +41,23 @@ The four reasons: **[FACT, and a correction of the brief]** `media/` is not photo-free: the skeleton puts `photos`, `books`, `audiobooks`, `music`, `comics`, `podcasts` there too (`appbackup/userdata.go` + the catalog-derived set), and Immich, calibre-web, audiobookshelf and navidrome use them. With reason 1, a download app needs ONE mount that holds both its -download folder and the libraries — so binding `media/tv` and `media/torrents` separately brings EXDEV back. **Open -question for the operator (two options):** +download folder and the libraries — so binding `media/tv` and `media/torrents` separately brings EXDEV back. -- **A — bind `media/` whole, rw for the download apps.** Simple; measured working. Cost: qBittorrent, Transmission, - Sonarr and Radarr can write into the family's photos and music folders. -- **B — move the video library into its own folder** (e.g. `userdata/video/{tv,movies,torrents,transmission}`) and bind - only that. Download apps never see photos. Cost: a one-time move on boxes that already have files there, and every - template and skeleton entry changes. -- **CC recommends B** for the build: the download apps are the internet-facing ones, and „the box cannot touch your - photos" is a promise worth one move. A is fine for the Tester 1 rig. +**RULED 2026-10-14 (`09` §3 decision 242, D9 = A with a limit) — this table replaces every earlier shape:** + +| App | Bind (rw unless noted), the SAME path inside the container as on the box | Sees | +|---|---|---| +| Sonarr, Radarr | `${USERDATA_PATH}/media` | the whole `media` folder — it must: download and library on ONE mount (S1) | +| qBittorrent | `${USERDATA_PATH}/media/torrents` (+ the same folder as `/downloads`, for torrents added before 2026-10-14) | only its download folder | +| Transmission | `${USERDATA_PATH}/media/transmission` (+ `/downloads` likewise; `/watch` = `media/transmission/watch`) | only its download folder | +| Plex, Jellyfin, Emby | unchanged: `${USERDATA_PATH}/media:/media:ro` | they move with the all-drives build | + +The hardlink is made by Sonarr/Radarr in their own container, where both folders are one mount; the client reports +`…/media/torrents/` and Sonarr finds exactly that path. The download clients cannot reach `media/photos` (B-3, +measured: the path does not exist in their containers). Sonarr/Radarr's bind is declared `excluded` in their backup +block — unlisted it would be MANDATORY, every tier, off-site included (B-1, measured with the production classifier). +Built in the live catalog 2026-10-14 (`audits/media-folder-fix-2026-10-14/`); the all-drives build adds one such bind +per drive at the same paths, so nothing installed now is redone. ## 3. Which apps are „media apps" [DESIGN, proposal] diff --git a/documentation/audits/media-folder-fix-2026-10-14/A-who-runs-them.txt b/documentation/audits/media-folder-fix-2026-10-14/A-who-runs-them.txt new file mode 100644 index 00000000..46e6b69b --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/A-who-runs-them.txt @@ -0,0 +1,23 @@ +# Part A — read 2026-10-14T21:13:19+02:00 from the hub (read only): /apps and each /hosts/ page +apps page: Sonarr +apps page: Transmission +apps page: qBittorrent +apps page: Radarr +host Tester-2-be8404: (apps listed on the page: 0) +host demo-felhom-8363b5: (apps listed on the page: 0) +host demo-hp-bb76ea: (apps listed on the page: 0) +host tester-1-d70be4: (apps listed on the page: 0) +hub /apps: Sonarr on 2 box(es) +hub /apps: Radarr on 2 box(es) +hub /apps: qBittorrent on 2 box(es) +hub /apps: Transmission on 2 box(es) +--- per box (docker ps, read only): +demo-hp 9201: +demo-hp 9202: +demo-felhom (N100) 9201: +tester-1 9201 (the rig, drill catalog): transmission qbittorrent radarr sonarr +bench 9401 (demo-hp): +/apps/sonarr: [' / error / [Fatal] ConsoleApp: Failed to bind to address http://[::]:8989: address already in use. This can happen if another instance of Sonarr is already running another application is using the same port (def', 'fingerprint: [fatal] consoleapp: failed to bind to address http://[::]:8989: address already in use. this can hap · severity: error · first seen: 2026-10-14 16:24:53 · last seen: 2026-10-14 18:38:45 affected customers: tester-1 Full message: Copy [Fatal] ConsoleApp: Failed to bind to address http://[::]:8989: address already in use. This can happen if another instance of Sonarr is already running another application is using the same port (def Context around first occurrence — from tester-1 : Copy [Info] Microsoft.Hosting.Lifetime: Now listening on: http://[::]:8989 [Info] ConfigureHostFilteringOptions: Accepting requests for hosts: sonarr, sonarr.enkicsifelhom.hu, localhost, 127.0.0.1, [::1], 732c0982f1bb [Info] ForwardedHeadersConfigurator: Trusting forwarded headers from 192.168.0.0/24 [Fatal] ConsoleApp: Failed to bind to address http://[::]:8989: address already in use. This can happen if another instance of Sonarr is already running another application is using the same port (default: 8989) or the user has insufficient permissions Press enter to exit... [Info] Microsoft.Hosting.Lifetime: Application started. Press Ctrl+C to shut down. [Info] Microsoft.Hosting.Lifetime: Hosting environment: Production [Info] Microsoft.Hosting.Lifetime: Content root path: /app/sonarr/bin [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled', ' / warn / [Warn] FetchAndParseRssService: No available indexers. check your configuration.', 'fingerprint: [warn] fetchandparserssservice: no available indexers. check your configuration. · severity: warn · first seen: 2026-10-13 11:04:53 · last seen: 2026-10-14 16:24:53 affected customers: demo-felhom , tester-1 Full message: Copy [Warn] FetchAndParseRssService: No available indexers. check your configuration. No context captured (pre-v0.111 report or warn-severity issue).', ' / error / [Warn] SonarrErrorPipeline: Invalid request Validation failed:', 'fingerprint: [warn] sonarrerrorpipeline: invalid request validation failed: · severity: error · first seen: 2026-10-14 14:19:38 · last seen: 2026-10-14 16:24:53 affected customers: tester-1 Full message: Copy [Warn] SonarrErrorPipeline: Invalid request Validation failed: Context around first occurrence — from tester-1 : Copy [Info] Microsoft.Hosting.Lifetime: Application started. Press Ctrl+C to shut down. [Info] Microsoft.Hosting.Lifetime: Hosting environment: Production [Info] Microsoft.Hosting.Lifetime: Content root path: /app/sonarr/bin [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled [ls.io-init] done. [Warn] SonarrErrorPipeline: Invalid request Validation failed: -- TvCategory: Cannot use Category and Directory [Warn] SonarrErrorPipeline: Invalid request Validation failed: -- TvCategory: Cannot use Category and Directory', ' / error / --- End of inner exception stack trace ---', 'fingerprint: --- end of inner exception stack trace --- · severity: error · first seen: 2026-10-14 15:39:57 · last seen: 2026-10-14 15:42:44 affected customers: tester-1 Full message: Copy --- End of inner exception stack trace --- Context around first occurrence — from tester-1 : Copy at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket. g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.attemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 327 at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 313 at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.AddHttp11ConnectionAsync(HttpRequestMessage request) at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)', ' / error / at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)', 'fingerprint: at system.net.sockets.socket.awaitablesocketasynceventargs.throwexception(socketerror error, cancell · severity: error · first seen: 2026-10-14 15:39:57 · last seen: 2026-10-14 15:42:44 affected customers: tester-1 Full message: Copy at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) Context around first occurrence — from tester-1 : Copy [ls.io-init] done. [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080) ---> System.Net.Sockets.SocketException (111): Connection refused at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket. g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.attemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 327 at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 313 at System.Net.Http.HttpConnectionPool.ConnectToTcpHostAsync(String host, Int32 port, HttpRequestMessage initialRequest, Boolean async, CancellationToken cancellationToken)', ' / error / ---> System.Net.Sockets.SocketException (111): Connection refused', 'fingerprint: ---> system.net.sockets.socketexception (111): connection refused · severity: error · first seen: 2026-10-14 15:39:57 · last seen: 2026-10-14 15:42:44 affected customers: tester-1 Full message: Copy ---> System.Net.Sockets.SocketException (111): Connection refused Context around first occurrence — from tester-1 : Copy [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled [ls.io-init] done. [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080) ---> System.Net.Sockets.SocketException (111): Connection refused at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket. g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.attemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 327 at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.onConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 313', ' / error / [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080)', 'fingerprint: [v4.0.20.3014] system.net.http.httprequestexception: connection refused (qbittorrent:8080) · severity: error · first seen: 2026-10-14 15:39:57 · last seen: 2026-10-14 15:42:44 affected customers: tester-1 Full message: Copy [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080) Context around first occurrence — from tester-1 : Copy [Info] Microsoft.Hosting.Lifetime: Content root path: /app/sonarr/bin [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled [ls.io-init] done. [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080) ---> System.Net.Sockets.SocketException (111): Connection refused at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket. g__WaitForConnectWithCancellation|277_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at NzbDrone.Common.Http.Dispatchers.ManagedHttpDispatcher.attemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) in ./Sonarr.Common/Http/Dispatchers/ManagedHttpDispatcher.cs:line 327', ' / error / [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck', 'fingerprint: [error] downloadclientremovescompleteddownloadscheck: unknown error occurred in downloadclienthistor · severity: error · first seen: 2026-10-14 15:39:57 · last seen: 2026-10-14 15:42:44 affected customers: tester-1 Full message: Copy [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck Context around first occurrence — from tester-1 : Copy [Info] Microsoft.Hosting.Lifetime: Application started. Press Ctrl+C to shut down. [Info] Microsoft.Hosting.Lifetime: Hosting environment: Production [Info] Microsoft.Hosting.Lifetime: Content root path: /app/sonarr/bin [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled [ls.io-init] done. [Error] DownloadClientRemovesCompletedDownloadsCheck: Unknown error occurred in DownloadClientHistoryRetentionCheck HealthCheck [v4.0.20.3014] System.Net.Http.HttpRequestException: Connection refused (qbittorrent:8080) ---> System.Net.Sockets.SocketException (111): Connection refused at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)'] +/apps/radarr: [" / warn / [Warn] DiskTransferService: Hardlinking from '/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264", "fingerprint: [warn] disktransferservice: hardlinking from '/mnt/felhom-drives/media_a/userdata/media/transmission · severity: warn · first seen: 2026-10-14 17:23:11 · last seen: 2026-10-14 17:33:35 affected customers: tester-1 Full message: Copy [Warn] DiskTransferService: Hardlinking from '/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264 No context captured (pre-v0.111 report or warn-severity issue).", " / warn / [Warn] DiskScanService: Movie's root folder (/mnt/felhom-drives/media_b/userdata/media/movies) is empty. Rescan will not update movies as a failsafe.", "fingerprint: [warn] diskscanservice: movie's root folder (/mnt/felhom-drives/media_b/userdata/media/movies) is em · severity: warn · first seen: 2026-10-14 14:19:38 · last seen: 2026-10-14 17:23:50 affected customers: tester-1 Full message: Copy [Warn] DiskScanService: Movie's root folder (/mnt/felhom-drives/media_b/userdata/media/movies) is empty. Rescan will not update movies as a failsafe. No context captured (pre-v0.111 report or warn-severity issue).", " / warn / [Warn] DiskScanService: Movie's root folder (/mnt/felhom-drives/media_a/userdata/media/movies) is empty. Rescan will not update movies as a failsafe.", "fingerprint: [warn] diskscanservice: movie's root folder (/mnt/felhom-drives/media_a/userdata/media/movies) is em · severity: warn · first seen: 2026-10-14 15:04:38 · last seen: 2026-10-14 17:23:50 affected customers: tester-1 Full message: Copy [Warn] DiskScanService: Movie's root folder (/mnt/felhom-drives/media_a/userdata/media/movies) is empty. Rescan will not update movies as a failsafe. No context captured (pre-v0.111 report or warn-severity issue).", ' / error / [Warn] RadarrErrorPipeline: Invalid request Validation failed:', 'fingerprint: [warn] radarrerrorpipeline: invalid request validation failed: · severity: error · first seen: 2026-10-14 14:19:38 · last seen: 2026-10-14 16:27:40 affected customers: tester-1 Full message: Copy [Warn] RadarrErrorPipeline: Invalid request Validation failed: Context around first occurrence — from tester-1 : Copy [Info] Microsoft.Hosting.Lifetime: Application started. Press Ctrl+C to shut down. [Info] Microsoft.Hosting.Lifetime: Hosting environment: Production [Info] Microsoft.Hosting.Lifetime: Content root path: /app/radarr/bin [Info] ManagedHttpDispatcher: IPv4 is available: True, IPv6 will be disabled [ls.io-init] done. [Warn] RadarrErrorPipeline: Invalid request Validation failed: -- MovieCategory: Cannot use Category and Directory [Warn] RadarrErrorPipeline: Invalid request Validation failed: -- MovieCategory: Cannot use Category and Directory [Info] AddMovieService: Adding Movie [Night of the Living Dead (1968)][tt0063350, 10331] Path: [/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)] [Info] RefreshMovieService: Updating info for Night of the Living Dead', ' / warn / [Warn] FetchAndParseRssService: No available indexers. check your configuration.', 'fingerprint: [warn] fetchandparserssservice: no available indexers. check your configuration. · severity: warn · first seen: 2026-10-14 14:49:38 · last seen: 2026-10-14 16:12:40 affected customers: tester-1 Full message: Copy [Warn] FetchAndParseRssService: No available indexers. check your configuration. No context captured (pre-v0.111 report or warn-severity issue).', " / warn / [Warn] DiskTransferService: Hardlinking from '/mnt/felhom-drives/media_a/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4' to '/mnt/felhom-drives/m", "fingerprint: [warn] disktransferservice: hardlinking from '/mnt/felhom-drives/media_a/userdata/media/torrents/mov · severity: warn · first seen: 2026-10-14 15:28:24 · last seen: 2026-10-14 15:28:24 affected customers: tester-1 Full message: Copy [Warn] DiskTransferService: Hardlinking from '/mnt/felhom-drives/media_a/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4' to '/mnt/felhom-drives/m No context captured (pre-v0.111 report or warn-severity issue)."] +/apps/qbittorrent: [] +/apps/transmission: [] diff --git a/documentation/audits/media-folder-fix-2026-10-14/B-volume-persistence.txt b/documentation/audits/media-folder-fix-2026-10-14/B-volume-persistence.txt new file mode 100644 index 00000000..28dc69eb --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/B-volume-persistence.txt @@ -0,0 +1,14 @@ +self-testing the prober (both directions)… + prober flags the R-156 signature and clears a correct template — trustworthy +jellyfin CLEAN +plex UNDETERMINED plex: declared volume /transcode is EMPTY after the exercise — the app was not shown to write where the template preserves it +qbittorrent CLEAN qbittorrent: writable-layer writes at /srv/felhom-gate/hdd/userdata/media (path suggests state, no database signature — judgement needed): ['torrents' +radarr CLEAN +sonarr CLEAN +transmission CLEAN transmission: bind /watch is EMPTY after the exercise — not judged (a household folder may be empty on a fresh install); if this app keeps STATE there + +UNDETERMINED — NOT a pass. The question was not answered for these: + plex: plex: declared volume /transcode is EMPTY after the exercise — the app was not shown to write where the template preserves it + +5 clean · 0 broken · 1 undetermined (of 6 in scope) +INCOMPLETE — no broken template among those decided, but 1 were never decided. This is not a clean bill of health. diff --git a/documentation/audits/media-folder-fix-2026-10-14/B1-classes-export-remove.txt b/documentation/audits/media-folder-fix-2026-10-14/B1-classes-export-remove.txt new file mode 100644 index 00000000..61ca94c6 --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/B1-classes-export-remove.txt @@ -0,0 +1,98 @@ +# B-1 — the production code (felhom-controller main 9b95e8e) over the templates; namespace root = one enrolled drive +## TODAY (live catalog main 8f5ad8a) +== /sonarr backup-block=true hasClassification=true + bind userdata/media/tv ro=false class="excluded" origin=explicit + bind userdata/downloads ro=false class="excluded" origin=explicit + tier secondary captures: [] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[] excluded(default out)=[downloads media/tv] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /sonarr/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /sonarr/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/tv /mnt/felhom-drives/hdd_1/userdata/downloads] +== /radarr backup-block=true hasClassification=true + bind userdata/media/movies ro=false class="excluded" origin=explicit + bind userdata/downloads ro=false class="excluded" origin=explicit + tier secondary captures: [] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[] excluded(default out)=[downloads media/movies] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /radarr/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /radarr/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/movies /mnt/felhom-drives/hdd_1/userdata/downloads] +== /qbittorrent backup-block=true hasClassification=true + bind userdata/media/torrents ro=false class="optional" origin=explicit + tier secondary captures: [media/torrents(optional)] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[media/torrents] excluded(default out)=[] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /qbittorrent/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /qbittorrent/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/torrents] +== /transmission backup-block=true hasClassification=true + bind userdata/media/transmission ro=false class="optional" origin=explicit + bind userdata/media/transmission/watch ro=false class="mandatory" origin=default_writable + tier secondary captures: [media/transmission(optional)] + tier offsite captures: [media/transmission/watch(mandatory)] + export: mandatory=[media/transmission/watch] optional(default in)=[media/transmission] excluded(default out)=[] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /transmission/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /transmission/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/transmission /mnt/felhom-drives/hdd_1/userdata/media/transmission/watch] + +## CANDIDATE (the §2 shape) +== /sonarr backup-block=true hasClassification=true + bind userdata/media ro=false class="excluded" origin=explicit + tier secondary captures: [] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[] excluded(default out)=[media] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /sonarr/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /sonarr/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media] +== /radarr backup-block=true hasClassification=true + bind userdata/media ro=false class="excluded" origin=explicit + tier secondary captures: [] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[] excluded(default out)=[media] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /radarr/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /radarr/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media] +== /qbittorrent backup-block=true hasClassification=true + bind userdata/media/torrents ro=false class="optional" origin=explicit + tier secondary captures: [media/torrents(optional)] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[media/torrents] excluded(default out)=[] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /qbittorrent/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /qbittorrent/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/torrents] +== /transmission backup-block=true hasClassification=true + bind userdata/media/transmission ro=false class="optional" origin=explicit + bind userdata/media/transmission/watch ro=false class="optional" origin=explicit + tier secondary captures: [media/transmission(optional)] + tier offsite captures: [] + export: mandatory=[] optional(default in)=[media/transmission media/transmission/watch] excluded(default out)=[] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /transmission/docker-compose.yml + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] +2026/10/14 21:15:15 [INFO] [stacks] ParseComposeHDDMounts: found 0 HDD mounts for /transmission/docker-compose.yml + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media/transmission /mnt/felhom-drives/hdd_1/userdata/media/transmission/watch] + +## CONTROL (decoy): the candidate Sonarr bind with its backup entry REMOVED (a backup block that does not list it) +== /sonarr-unlisted backup-block=true hasClassification=true + bind userdata/media ro=false class="mandatory" origin=default_writable + tier secondary captures: [media(mandatory)] + tier offsite captures: [media(mandatory)] + export: mandatory=[media] optional(default in)=[] excluded(default out)=[] + export mounts (ExportDataMounts): [/mnt/felhom-drives/hdd_1/userdata] + removal with „delete data” deletes (ParseComposeHDDMounts): [] + userdata folders the removal names as kept: [/mnt/felhom-drives/hdd_1/userdata/media] diff --git a/documentation/audits/media-folder-fix-2026-10-14/B2-B3-qbittorrent.txt b/documentation/audits/media-folder-fix-2026-10-14/B2-B3-qbittorrent.txt new file mode 100644 index 00000000..6cfafe01 --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/B2-B3-qbittorrent.txt @@ -0,0 +1,27 @@ +== 1. install from TODAY's template + container: healthy /srv/b2/userdata/media/torrents->/downloads /var/lib/docker/volumes/b2qbt_qbittorrent_config/_data->/config + default save path: /downloads +{"added_torrent_ids":["a5801fa9a8beb3f047303e352844db8898570758"],"failure_count":0,"pending_count":0,"success_count":1} + torrent b2-old state= stalledUP progress= 1 save_path= /downloads +== 2. update to the NEW template (same volume) + container: healthy /srv/b2/userdata/media/torrents->/srv/b2/userdata/media/torrents /srv/b2/userdata/media/torrents->/downloads /var/lib/docker/volumes/b2qbt_qbittorrent_config/_data->/config + entrypoint said: [felhom] default save path moved from /downloads to /srv/b2/userdata/media/torrents + default save path now: /srv/b2/userdata/media/torrents | temp: /srv/b2/userdata/media/torrents/.temp + the OLD torrent after the update: + torrent b2-old state= stalledUP progress= 1 save_path= /downloads +{"added_torrent_ids":["496bc2c1261736321d710aeca0e4c7c88a8bf946"],"failure_count":0,"pending_count":0,"success_count":1} + a NEW torrent (no save path given): + torrent b2-new state= stalledUP progress= 1 save_path= /srv/b2/userdata/media/torrents +== 3. B-3 from inside the container + ls of the media folder path: + torrents + ls of the photos folder: + ls: /srv/b2/userdata/media/photos: No such file or directory + write into the photos path: + rc=0 + on the BOX, the photos folder after that: family.jpg + and the container's own copy of that path is on: overlay +== 4. a FRESH install of the new template + container: healthy /srv/b2/userdata/media/torrents->/srv/b2/userdata/media/torrents /srv/b2/userdata/media/torrents->/downloads /var/lib/docker/volumes/b2qbt_qbittorrent_config/_data->/config + default save path: /srv/b2/userdata/media/torrents | temp: /srv/b2/userdata/media/torrents/.temp +== cleaned up: 0 container(s), 0 volume(s) diff --git a/documentation/audits/media-folder-fix-2026-10-14/B2-B3-transmission.txt b/documentation/audits/media-folder-fix-2026-10-14/B2-B3-transmission.txt new file mode 100644 index 00000000..422f3409 --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/B2-B3-transmission.txt @@ -0,0 +1,24 @@ +== 1. install from TODAY's template + container: healthy /var/lib/docker/volumes/b2tr_transmission_config/_data->/config /srv/b2t/userdata/media/transmission->/downloads /srv/b2t/userdata/media/transmission/watch->/watch + session: {'download-dir': '/downloads/complete', 'incomplete-dir': '/downloads/incomplete'} +{"arguments":{"torrent-added":{"hashString":"01268661fcc63db754b6f6fcafb0b49c182b8a42","id":1,"name":"b2-old"}},"result":"success"} + torrent b2-old status= seeding done= 1.0 dir= /downloads/complete error= '' +== 2. update to the NEW template (same volume) + container: healthy /srv/b2t/userdata/media/transmission->/srv/b2t/userdata/media/transmission /srv/b2t/userdata/media/transmission->/downloads /srv/b2t/userdata/media/transmission/watch->/watch /var/lib/docker/volumes/b2tr_transmission_config/_data->/config + session: {'download-dir': '/srv/b2t/userdata/media/transmission/complete', 'incomplete-dir': '/srv/b2t/userdata/media/transmission/incomplete'} + the OLD torrent after the update: + torrent b2-old status= seeding done= 1.0 dir= /downloads/complete error= '' +{"arguments":{"torrent-added":{"hashString":"b4dbeccdc89f6297bceaac75a89d07ad13e7f553","id":2,"name":"b2-new"}},"result":"success"} + a NEW torrent (no folder given): + torrent b2-new status= seeding done= 1.0 dir= /srv/b2t/userdata/media/transmission/complete error= '' +== 3. B-3 from inside the container + ls of the media folder path: + transmission + ls of the photos folder: + ls: cannot access '/srv/b2t/userdata/media/photos': No such file or directory + write into the photos path: rc=0 on overlay + on the BOX, the photos folder after that: family.jpg +== 4. a FRESH install of the new template + container: healthy /srv/b2t/userdata/media/transmission->/srv/b2t/userdata/media/transmission /var/lib/docker/volumes/b2tr_transmission_config/_data->/config /srv/b2t/userdata/media/transmission->/downloads /srv/b2t/userdata/media/transmission/watch->/watch + session: {'download-dir': '/srv/b2t/userdata/media/transmission/complete', 'incomplete-dir': '/srv/b2t/userdata/media/transmission/incomplete'} +== cleaned up: 0 container(s), 0 volume(s) diff --git a/documentation/audits/media-folder-fix-2026-10-14/B2-sonarr-radarr.txt b/documentation/audits/media-folder-fix-2026-10-14/B2-sonarr-radarr.txt new file mode 100644 index 00000000..4bfa764b --- /dev/null +++ b/documentation/audits/media-folder-fix-2026-10-14/B2-sonarr-radarr.txt @@ -0,0 +1,19 @@ +== sonarr (fresh, new template): healthy /srv/b2sonarr/userdata/media->/srv/b2sonarr/userdata/media /var/lib/docker/volumes/b2sonarr_sonarr_config/_data->/config + log: [felhom] first start - AuthenticationRequired=Enabled seeded + config.xml: Enabled None ApiKey present: 1 + what the first-admin form reads (GET /api/v3/config/host): authenticationRequired= enabled authenticationMethod= none + /initialize.json from another container, no sign-in: 200, apiKey in it: 1 + hardlink inside the one mount: + /srv/b2sonarr/userdata/media/torrents/f.mkv links=2 inode=2228262 + /srv/b2sonarr/userdata/media/tv/f.mkv links=2 inode=2228262 + existing install restarted: seeded again? 0 line(s) (1 = only the first start); AuthenticationRequired now: DisabledForLocalAddresses +== radarr (fresh, new template): healthy /srv/b2radarr/userdata/media->/srv/b2radarr/userdata/media /var/lib/docker/volumes/b2radarr_radarr_config/_data->/config + log: [felhom] first start - AuthenticationRequired=Enabled seeded + config.xml: Enabled None ApiKey present: 1 + what the first-admin form reads (GET /api/v3/config/host): authenticationRequired= enabled authenticationMethod= none + /initialize.json from another container, no sign-in: 200, apiKey in it: 1 + hardlink inside the one mount: + /srv/b2radarr/userdata/media/torrents/f.mkv links=2 inode=2228262 + /srv/b2radarr/userdata/media/movies/f.mkv links=2 inode=2228262 + existing install restarted: seeded again? 0 line(s) (1 = only the first start); AuthenticationRequired now: DisabledForLocalAddresses +== cleaned up: 0 container(s)