diff --git a/REPORT-media-all-drives-2026-10-14.md b/REPORT-media-all-drives-2026-10-14.md new file mode 100644 index 00000000..7653fa25 --- /dev/null +++ b/REPORT-media-all-drives-2026-10-14.md @@ -0,0 +1,33 @@ +**S1–S3 pushed; the live-fix task may start.** + +> This report is `REPORT-media-all-drives-2026-10-14.md`, not the root `REPORT.md`: the repo rule (`CLAUDE.md`, „two +> sessions in this repo clobber each other") sends a parallel session to a topic file. The task asked for the line at +> the top of `REPORT.md`; it is here instead. + +**Resume point:** Parts A–C done and pushed. Next: Part D (Plex path check, the test sheet, then STOP for the operator). + +# Media apps on every drive — Tester 1 rig (2026-10-14) + +Evidence: `documentation/audits/media-all-drives-2026-10-14/README.md` (one row per part). + +## In plain words + +1. **The chain works with the new folder layout.** A film went from the download app to the library on the same + drive as one file with two names. No extra copy. Through both download apps. +2. **Today's layout cannot work.** The download apps save where Sonarr and Radarr never look. And even with the same + folders, two separate mounts force a full copy. Measured. +3. **Each film can go to its own drive** by a tag. A wrong tag still works, but makes a full copy across drives. +4. **No test film reached any backup.** Proven three ways before the first download. +5. **A power cut hit Tester 1 at 17:25.** It came from another session's test on the HP box. Tester 1 came back by + itself. + +## Claims in the task that turned out wrong + +- „`media/` only keeps photos and documents out of these apps' reach" — `media/` holds `photos`, `books`, + `audiobooks` and more in the skeleton. Documents are outside it; photos are not. +- R-615: removing the cache is no longer needed (a changed `repo_url` re-clones, seen live). +- The `/media/shows` copy is in Plex too, not only Sonarr and Jellyfin (R-1015). + +## Register + +Rows before 133 → after 139 · opened 6 (R-1014..R-1019) · closed 0. diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-dashboard-api.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-dashboard-api.txt new file mode 100644 index 00000000..71d65a05 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-dashboard-api.txt @@ -0,0 +1,9 @@ +== /api/system/info 200 +{"ok": true, "data": {"sync_status": {"last_sync": "2026-10-14T13:44:42.082522426Z", "last_status": "ok", "syncing": false}, "system": {"total_mem_mb": 4096, "used_mem_mb": 680, "avail_mem_mb": 3416, "mem_percent": 16.6015625, "disk_total_gb": 68.72599792480469, "disk_used_gb": 4.120944976806641, "disk_avail_gb": 61.089439392089844, "disk_percent": 6.319461258646124, "disk_known": true, "hdd_total_gb": 48.914520263671875, "hdd_used_gb": 0.23301315307617188, "hdd_avail_gb": 46.1658821105957, "hdd_percent": 0.5021954763190453, "hdd_configured": true, "hdd_known": true, "cpu_percent": 17.57910597689603, "load_avg_1": 0.21, "load_avg_5": 0.3, "load_avg_15": 0.32, "temperature_celsius": 0}}} +== /api/system/memory 200 +{"data": {"vmid": 9201, "allocated_mb": 4096, "usage_mb": 944, "host_total_mb": 7872, "min_mb": 2048, "max_mb": 5824, "floor_mb": 2048, "running": true}, "ok": true} +== /api/disks 200 +{"data": {"vmid": 9201, "disks": [{"name": "863c67fd-3e21-4d9f-9a74-3c5fa406f2a2", "type": "usb", "state": "attached", "backing_device": "/dev/sdb", "mount_path": "/mnt/adatlemez", "class": "", "role": "user-data", "data_bearing": false, "data_reason": "", "total_bytes": 52521566208, "used_bytes": 250195968, "used_fraction": 0.0047636806375718965, "durable_id": "uuid:863c67fd-3e21-4d9f-9a74-3c5fa406f2a2", "guest_attached": false, "guest_path": "/mnt/felhom-drives/adatlemez", "bound_under_parent": true, "smart": {"health": "UNKNOWN", "model_name": "QEMU QEMU HARDDISK", "temperature_c": 0, "power_on_hours": null, "reallocated_sectors": null, "pending_sectors": null, "offline_uncorrectable": null, "critical_warning": null, "media_errors": null, "percentage_used": null}}, {"name": "local", "type": "local", "state": "attached", "backing_device": "", "mount_path": "", "class": "", "role": "system", "data_bearing": false, "data_reason": "", "total_bytes": 62903865344, "used_bytes": 13649629184, "used_fraction": 0.216991898818217, "durable_id": "path:/var/lib/vz", "guest_attached": false, "backup_target": true, "bound_under_parent": false, "smart": {"health": "UNKNOWN", "model_name": "QEMU QEMU HARDDISK", "temperature_c": 0, "power_on_hours": null, "reallocated_sectors": null, "pending_sectors": null, "offline_uncorrectable": null, "critical_warning": null, "media_errors": null, "percentage_used": null}}, {"name": "local-lvm", "type": "lvmthin", "state": "attached", "backing_device": "", "mount_path": "", "class": "", "role": "system", "data_bearing": false, "data_reason": "", "total_bytes": 121521569792, "used_bytes": 7813836937, "used_fraction": 0.0642999999948519, "durable_id": "pve/data", "guest_attached": false, "bound_under_parent": false, "smart": {"health": "UNKNOWN", "temperature_c": null, "power_on_hours": null, "reallocated_sectors": null, "pending_sectors": null, "offline_uncorrectable": null, "critical_warning": null, "media_errors": null, "percentage_used": null}}, {"name": "felhom-pbs", "type": "pbs", "state": "attached", "backing_device": "", "mount_path": "", "class": "", "role": "backup", "data_bearing": false, "data_reason": "", "total_bytes": 0, "used_bytes": 0, "used_fraction": 0, "durable_id": "10.77.0.1:felhom-offsite#c6:07:28:3f:5b:7b:5a:41:90:28:d7:ca:4f:37:14:70:56:39:2e:2f:0b:71:e8:06:ca:60:4a:d5:56:5f:3c:fd", "guest_attached": false, "bound_under_parent": false, "smart": {"health": "UNKNOWN", "temperature_c": null, "power_on_hours": nul +== /api/storage/backup-target 200 +{"data": {"degraded": true, "known": true, "label": "", "message": "A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer — így hibás fájlok ellen véd, lemezhiba ellen nem. Csatlakoztass egy második meghajtót a teljes védelemhez.", "offer_label": "Adatlemez", "offer_message": "Ezt a meghajtót kijelölheted a rendszermentés helyéül — így egy lemezhiba után is vissza tudod állítani a rendszert.", "offer_path": "/mnt/felhom-drives/adatlemez", "target": "local"}, "ok": true} +== deployed: [('bookstack', 'running'), ('paperless-ngx', 'running'), ('privatebin', 'running')] diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-demo-hp.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-demo-hp.txt new file mode 100644 index 00000000..dca78292 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-demo-hp.txt @@ -0,0 +1,30 @@ +2026-10-14T15:57:57+02:00 +bios: ovmf +boot: order=scsi0 +cores: 4 +cpu: host +efidisk0: nvme-scratch:341/vm-341-disk-0.raw,efitype=4m,pre-enrolled-keys=0,size=528K +machine: q35 +memory: 8192 +meta: creation-qemu=11.0.0,ctime=1791141969 +name: night1004-tester1 +net0: virtio=BC:24:11:AC:E3:F9,bridge=vmbr0 +onboot: 1 +ostype: l26 +scsi0: nvme-scratch:341/vm-341-disk-1.raw,size=200G +scsi1: nvme-scratch:341/vm-341-disk-2.raw,size=50G +scsihw: virtio-scsi-single +smbios1: uuid=451c8d94-abf2-46e4-a278-b579a9f315b5 +vga: std +vmgenid: acdf89db-a810-445c-9d88-7bccd688d3c1 +Filesystem 1B-blocks Used Available Use% Mounted on +/dev/nvme0n1 1006980812800 135772049408 819981512704 15% /mnt/hdd_1 +total 76833588 + 4 drwxr----- 2 root root 4096 Oct 4 21:26 . + 4 drwxr-xr-x 5 root root 4096 Oct 14 05:42 .. + 528 -rw-r----- 1 root root 540672 Oct 14 14:07 vm-341-disk-0.raw +75117456 -rw-r----- 1 root root 214748364800 Oct 14 15:57 vm-341-disk-1.raw + 1715596 -rw-r----- 1 root root 53687091200 Oct 14 15:57 vm-341-disk-2.raw + total used free shared buff/cache available +Mem: 29994 11576 10048 164 8973 18417 +Swap: 8191 0 8191 diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-tester1-host.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-tester1-host.txt new file mode 100644 index 00000000..460ba121 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A1-tester1-host.txt @@ -0,0 +1,49 @@ +2026-10-14T15:57:59+02:00 +felhom-agent 0.156.1 +arch: amd64 +cores: 3 +features: nesting=1,keyctl=1 +hookscript: local:snippets/felhom-guest-hook.sh +hostname: tester-1 +memory: 4096 +mp0: local-lvm:vm-9201-disk-1,mp=/var/lib/felhom,backup=1,size=70G +mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives +mp9: /var/lib/felhom-agent/guests/9201/bootstrap,mp=/etc/felhom-bootstrap,ro=1 +net0: name=eth0,bridge=vmbr0,hwaddr=BC:24:11:FF:2F:FC,ip=dhcp,type=veth +net1: name=eth1,bridge=vmbr9,hwaddr=BC:24:11:6C:BB:E5,ip=169.254.253.2/30,type=veth +onboot: 1 +ostype: debian +rootfs: local-lvm:vm-9201-disk-0,size=32G +swap: 512 +unprivileged: 1 +NAME SIZE TYPE MOUNTPOINT SERIAL +sda 200G disk drive-scsi0 +|-sda1 1007K part +|-sda2 1G part /boot/efi +`-sda3 199G part + |-pve-swap 7.7G lvm [SWAP] + |-pve-root 59.8G lvm / + |-pve-data_tmeta 1.2G lvm + | `-pve-data-tpool 113.2G lvm + | |-pve-data 113.2G lvm + | |-pve-vm--9201--disk--0 32G lvm + | `-pve-vm--9201--disk--1 70G lvm + `-pve-data_tdata 113.2G lvm + `-pve-data-tpool 113.2G lvm + |-pve-data 113.2G lvm + |-pve-vm--9201--disk--0 32G lvm + `-pve-vm--9201--disk--1 70G lvm +sdb 50G disk /mnt/adatlemez drive-scsi1 + total used free shared buff/cache available +Mem: 7872 2668 2247 105 3333 5203 +Swap: 7871 0 7871 +felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.310.0 Up About an hour (healthy) +paperless-webserver ghcr.io/paperless-ngx/paperless-ngx:2.20.15 Up 2 hours (healthy) +paperless-postgres postgres:18-alpine Up 2 hours (healthy) +paperless-redis redis:7-alpine Up 2 hours (healthy) +privatebin privatebin/pdo:2.0.6 Up 2 hours (healthy) +bookstack lscr.io/linuxserver/bookstack:26.09.1 Up 2 hours (healthy) +bookstack-db mariadb:12.3 Up 2 hours (healthy) +filebrowser gtstef/filebrowser:1.5.6-stable Up 2 hours (healthy) +cloudflared cloudflare/cloudflared:2026.9.3 Up 2 hours (healthy) +traefik traefik:v3.7.13 Up 2 hours diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A2-disks-added.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A2-disks-added.txt new file mode 100644 index 00000000..209ba087 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A2-disks-added.txt @@ -0,0 +1,33 @@ ++ date -Is +2026-10-14T15:58:36+02:00 ++ df -B1G /mnt/hdd_1 ++ tail -1 +/dev/nvme0n1 938 127 764 15% /mnt/hdd_1 ++ qm set 341 --scsi2 nvme-scratch:100,backup=0 +update VM 341: -scsi2 nvme-scratch:100,backup=0 +Formatting '/mnt/hdd_1/images/341/vm-341-disk-3.raw', fmt=raw size=107374182400 preallocation=off +scsi2: successfully created disk 'nvme-scratch:341/vm-341-disk-3.raw,backup=0,size=100G' ++ qm set 341 --scsi3 nvme-scratch:100,backup=0 +update VM 341: -scsi3 nvme-scratch:100,backup=0 +Formatting '/mnt/hdd_1/images/341/vm-341-disk-4.raw', fmt=raw size=107374182400 preallocation=off +scsi3: successfully created disk 'nvme-scratch:341/vm-341-disk-4.raw,backup=0,size=100G' ++ qm config 341 ++ grep -E '^scsi|^memory|hotplug' +memory: 8192 +scsi0: nvme-scratch:341/vm-341-disk-1.raw,size=200G +scsi1: nvme-scratch:341/vm-341-disk-2.raw,size=50G +scsi2: nvme-scratch:341/vm-341-disk-3.raw,backup=0,size=100G +scsi3: nvme-scratch:341/vm-341-disk-4.raw,backup=0,size=100G +scsihw: virtio-scsi-single ++ ls -las /mnt/hdd_1/images/341/ +total 76833596 + 4 drwxr----- 2 root root 4096 Oct 14 15:58 . + 4 drwxr-xr-x 5 root root 4096 Oct 14 05:42 .. + 528 -rw-r----- 1 root root 540672 Oct 14 14:07 vm-341-disk-0.raw +75117456 -rw-r----- 1 root root 214748364800 Oct 14 15:58 vm-341-disk-1.raw + 1715596 -rw-r----- 1 root root 53687091200 Oct 14 15:58 vm-341-disk-2.raw + 4 -rw-r----- 1 root root 107374182400 Oct 14 15:58 vm-341-disk-3.raw + 4 -rw-r----- 1 root root 107374182400 Oct 14 15:58 vm-341-disk-4.raw ++ df -B1G /mnt/hdd_1 ++ tail -1 +/dev/nvme0n1 938 127 764 15% /mnt/hdd_1 diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_a.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_a.txt new file mode 100644 index 00000000..e67a9b84 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_a.txt @@ -0,0 +1,5 @@ +15:59:08 candidates -> 200 ['/dev/sdc', '/dev/sdd'] +15:59:08 POST /api/storage/init {"device": "/dev/sdc", "fstype": "ext4", "mount_name": "media_a", "label": "Media A", "set_default": false, "confirmed": false, "durable_id": ""} -> 200 {"data": {"phase": "formatting", "started": true}, "ok": true} +15:59:10 status -> 200 {"device": "/dev/sdc", "durable_id": "", "error": "", "opsign": "", "phase": "mounting", "reason": "", "started_at": "2026-10-14T13:59:08.76516334Z", "updated_at": "2026-10-14T13:59:09.122147197Z", "where": "/mnt/media_a"} +15:59:12 status -> 200 {"device": "/dev/sdc", "durable_id": "", "error": "", "opsign": "", "phase": "done", "reason": "", "started_at": "2026-10-14T13:59:08.76516334Z", "updated_at": "2026-10-14T13:59:10.953891979Z", "where": "/mnt/felhom-drives/media_a"} +RESULT done diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_b.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_b.txt new file mode 100644 index 00000000..ea9578b5 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A4-enrol-media_b.txt @@ -0,0 +1,5 @@ +15:59:16 candidates -> 200 ['/dev/sdd'] +15:59:16 POST /api/storage/init {"device": "/dev/sdd", "fstype": "ext4", "mount_name": "media_b", "label": "Media B", "set_default": false, "confirmed": false, "durable_id": ""} -> 200 {"data": {"phase": "formatting", "started": true}, "ok": true} +15:59:18 status -> 200 {"device": "/dev/sdd", "durable_id": "", "error": "", "opsign": "", "phase": "mounting", "reason": "", "started_at": "2026-10-14T13:59:16.535337994Z", "updated_at": "2026-10-14T13:59:16.879143306Z", "where": "/mnt/media_b"} +15:59:20 status -> 200 {"device": "/dev/sdd", "durable_id": "", "error": "", "opsign": "", "phase": "done", "reason": "", "started_at": "2026-10-14T13:59:16.535337994Z", "updated_at": "2026-10-14T13:59:18.641630025Z", "where": "/mnt/felhom-drives/media_b"} +RESULT done diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-dashboard-after-enrol.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-dashboard-after-enrol.txt new file mode 100644 index 00000000..d33bd1d5 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-dashboard-after-enrol.txt @@ -0,0 +1,79 @@ +backup-target 200 {"data": {"degraded": true, "known": true, "label": "", "message": "A rendszermentés jelenleg ugyanazon a lemezen van, mint a rendszer — így hibás fájlok ellen véd, lemezhiba ellen nem. Csatlakoztass egy második meghajtót a teljes védelemhez.", "offer_label": "Adatlemez", "offer_message": "Ezt a meghajtót kijelölheted a rendszermentés helyéül — így egy lemezhiba után is vissza tudod állítani a rendszert.", "offer_path": "/mnt/felhom-drives/adatlemez", "target": "local"}, "ok": true} +storage page 200 77 +Tárhely — Felhom.eu +Indítópult +Vezérlőpult +Alkalmazások +Tárhely +Meghajtók +Hálózati tárhely +Biztonsági mentés +Áttekintés +Távoli mentés +Alkalmazások +Visszaállítás +Megosztás +Hálózati megosztás +Rendszermonitor +Hibakeresés +Beállítások +Rendszer +Értesítések +Biztonság és hozzáférés +0.310.0 +Magyar +English +Kijelentkezés ↗ +Tárhely — Meghajtók +Adattárolók +Külső meghajtók kezelése alkalmazásadatok tárolásához. +Megőrzött adatok +Eltávolított alkalmazások lemezen maradt adatai +Adatlemez +/mnt/felhom-drives/adatlemez +Alapértelmezett +Aktív +0,2 GB / 48,9 GB +ext4 · /dev/sdb[/felhom-data] · QEMU HARDDISK +1 alkalmazás használja +Paperless-ngx +6,3 KB +Mozgatás +Új telepítések letiltása +Áthelyezés ide… +Media A (/mnt/felhom-drives/media_a) +Media B (/mnt/felhom-drives/media_b) +Összes adat áthelyezése +Végleges leszerelés +Media A +/mnt/felhom-drives/media_a +Aktív +0,0 GB / 97,9 GB +ext4 · /dev/sdc[/felhom-data] · QEMU HARDDISK +Nincs alkalmazás ezen a tárolón +Legyen alapértelmezett +Új telepítések letiltása +Végleges leszerelés +Media B +/mnt/felhom-drives/media_b +Aktív +0,0 GB / 97,9 GB +ext4 · /dev/sdd[/felhom-data] · QEMU HARDDISK +Nincs alkalmazás ezen a tárolón +Legyen alapértelmezett +Új telepítések letiltása +Végleges leszerelés +Adatok áthelyezése +… +Új meghajtó inicializálása +Meglévő meghajtó csatolása +Nem regisztrált meghajtók +az ügynök által észlelt, még nem regisztrált adatmeghajtók +Rendszermeghajtók +védett — csak operátori aláírással módosíthatók +Már csatlakoztatott tárhely hozzáadása kézzel +Elérési út +Pl. /mnt/hdd_1 — a meghajtónak már csatolva kell lennie +Megnevezés (opcionális) +Legyen alapértelmezett új telepítéseknél +Hozzáadás diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-skeleton-filebrowser.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-skeleton-filebrowser.txt new file mode 100644 index 00000000..2dde950a --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A5-skeleton-filebrowser.txt @@ -0,0 +1,160 @@ +Wed Oct 14 13:59:31 UTC 2026 +TARGET SOURCE FSTYPE PROPAGATION +/mnt/felhom-drives /dev/mapper/pve-root[/mnt/felhom-drives] ext4 shared,slave +|-/mnt/felhom-drives/adatlemez /dev/sdb[/felhom-data] ext4 shared,slave +|-/mnt/felhom-drives/media_a /dev/sdc[/felhom-data] ext4 shared,slave +`-/mnt/felhom-drives/media_b /dev/sdd[/felhom-data] ext4 shared,slave +/mnt/felhom-drives /dev/mapper/pve-root[/mnt/felhom-drives] ext4 shared,slave +|-/mnt/felhom-drives/adatlemez /dev/sdb[/felhom-data] ext4 shared,slave +|-/mnt/felhom-drives/media_a /dev/sdc[/felhom-data] ext4 shared,slave +`-/mnt/felhom-drives/media_b /dev/sdd[/felhom-data] ext4 shared,slave +== media_a +total 12 +drwxr-xr-x 3 root root 4096 Oct 14 13:59 . +drwxr-xr-x 5 nobody nogroup 4096 Oct 14 13:59 .. +drwxrwsr-x 6 root 1000 4096 Oct 14 13:59 userdata +/mnt/felhom-drives/media_a +/mnt/felhom-drives/media_a/userdata +/mnt/felhom-drives/media_a/userdata/documents +/mnt/felhom-drives/media_a/userdata/downloads +/mnt/felhom-drives/media_a/userdata/media +/mnt/felhom-drives/media_a/userdata/media/audiobooks +/mnt/felhom-drives/media_a/userdata/media/books +/mnt/felhom-drives/media_a/userdata/media/comics +/mnt/felhom-drives/media_a/userdata/media/grimmory +/mnt/felhom-drives/media_a/userdata/media/metube +/mnt/felhom-drives/media_a/userdata/media/movies +/mnt/felhom-drives/media_a/userdata/media/music +/mnt/felhom-drives/media_a/userdata/media/nzbget +/mnt/felhom-drives/media_a/userdata/media/photos +/mnt/felhom-drives/media_a/userdata/media/podcasts +/mnt/felhom-drives/media_a/userdata/media/torrents +/mnt/felhom-drives/media_a/userdata/media/transmission +/mnt/felhom-drives/media_a/userdata/media/tv +/mnt/felhom-drives/media_a/userdata/media/usenet +/mnt/felhom-drives/media_a/userdata/roms +== media_b +total 12 +drwxr-xr-x 3 root root 4096 Oct 14 13:59 . +drwxr-xr-x 5 nobody nogroup 4096 Oct 14 13:59 .. +drwxrwsr-x 6 root 1000 4096 Oct 14 13:59 userdata +/mnt/felhom-drives/media_b +/mnt/felhom-drives/media_b/userdata +/mnt/felhom-drives/media_b/userdata/documents +/mnt/felhom-drives/media_b/userdata/downloads +/mnt/felhom-drives/media_b/userdata/media +/mnt/felhom-drives/media_b/userdata/media/audiobooks +/mnt/felhom-drives/media_b/userdata/media/books +/mnt/felhom-drives/media_b/userdata/media/comics +/mnt/felhom-drives/media_b/userdata/media/grimmory +/mnt/felhom-drives/media_b/userdata/media/metube +/mnt/felhom-drives/media_b/userdata/media/movies +/mnt/felhom-drives/media_b/userdata/media/music +/mnt/felhom-drives/media_b/userdata/media/nzbget +/mnt/felhom-drives/media_b/userdata/media/photos +/mnt/felhom-drives/media_b/userdata/media/podcasts +/mnt/felhom-drives/media_b/userdata/media/torrents +/mnt/felhom-drives/media_b/userdata/media/transmission +/mnt/felhom-drives/media_b/userdata/media/tv +/mnt/felhom-drives/media_b/userdata/media/usenet +/mnt/felhom-drives/media_b/userdata/roms +== adatlemez +total 20 +drwxr-xr-x 5 root root 4096 Oct 4 19:47 . +drwxr-xr-x 5 nobody nogroup 4096 Oct 14 13:59 .. +drwxr-xr-x 3 root root 4096 Oct 4 19:45 appdata +drwxr-xr-x 5 root root 4096 Oct 5 03:30 backups +drwxrwsr-x 6 root 1000 4096 Oct 4 19:44 userdata +/mnt/felhom-drives/adatlemez +/mnt/felhom-drives/adatlemez/appdata +/mnt/felhom-drives/adatlemez/appdata/paperless +/mnt/felhom-drives/adatlemez/appdata/paperless/export +/mnt/felhom-drives/adatlemez/appdata/paperless/media +/mnt/felhom-drives/adatlemez/backups +/mnt/felhom-drives/adatlemez/backups/offsite-proof +/mnt/felhom-drives/adatlemez/backups/primary +/mnt/felhom-drives/adatlemez/backups/primary/paperless-ngx +/mnt/felhom-drives/adatlemez/backups/secondary +/mnt/felhom-drives/adatlemez/backups/secondary/bookstack +/mnt/felhom-drives/adatlemez/backups/secondary/privatebin +/mnt/felhom-drives/adatlemez/userdata +/mnt/felhom-drives/adatlemez/userdata/documents +/mnt/felhom-drives/adatlemez/userdata/downloads +/mnt/felhom-drives/adatlemez/userdata/media +/mnt/felhom-drives/adatlemez/userdata/media/audiobooks +/mnt/felhom-drives/adatlemez/userdata/media/books +/mnt/felhom-drives/adatlemez/userdata/media/comics +/mnt/felhom-drives/adatlemez/userdata/media/grimmory +/mnt/felhom-drives/adatlemez/userdata/media/metube +/mnt/felhom-drives/adatlemez/userdata/media/movies +/mnt/felhom-drives/adatlemez/userdata/media/music +/mnt/felhom-drives/adatlemez/userdata/media/nzbget +/mnt/felhom-drives/adatlemez/userdata/media/photos +/mnt/felhom-drives/adatlemez/userdata/media/podcasts +/mnt/felhom-drives/adatlemez/userdata/media/torrents +/mnt/felhom-drives/adatlemez/userdata/media/transmission +/mnt/felhom-drives/adatlemez/userdata/media/tv +/mnt/felhom-drives/adatlemez/userdata/media/usenet +/mnt/felhom-drives/adatlemez/userdata/roms +2026-10-14T13:59:18.752620558Z /mnt/felhom-drives/adatlemez/userdata->/srv/adatlemez true rprivate; /mnt/sys_drive/felhom-data/userdata/import->/srv/beolvasas true rprivate; /mnt/felhom-drives/media_a/userdata->/srv/media_a true rprivate; /mnt/felhom-drives/media_b/userdata->/srv/media_b true rprivate; /opt/docker/stacks/filebrowser/config.yaml->/home/filebrowser/config.yaml false rprivate; /var/lib/docker/volumes/filebrowser_filebrowser_data/_data->/home/filebrowser/data true ; +2026/10/14 13:55:42 [INFO] [scheduler] Job agent-channel-health completed (took 707ms) +2026/10/14 13:55:51 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:00 [INFO] [web] Login from 192.168.0.180 +2026/10/14 13:56:01 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:01 [INFO] Health probes: 1 ok (of 1 probed) +2026/10/14 13:56:11 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:21 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:21 [INFO] Health probes: 2 ok (of 2 probed) +2026/10/14 13:56:31 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:32 [INFO] [web] Login from 192.168.0.180 +2026/10/14 13:56:41 [INFO] [scheduler] Running job: stack-scan +2026/10/14 13:56:41 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:41 [INFO] [stacks] ScanStacks complete: 67 stacks found (3 deployed, 64 available) +2026/10/14 13:56:41 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:56:41 [INFO] [scheduler] Job stack-scan completed (took 125ms) +2026/10/14 13:56:41 [INFO] [scheduler] Running job: agent-channel-health +2026/10/14 13:56:42 [INFO] [scheduler] Job agent-channel-health completed (took 485ms) +2026/10/14 13:56:51 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:01 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:11 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:21 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:31 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:41 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:57:41 [INFO] [scheduler] Running job: agent-channel-health +2026/10/14 13:57:42 [INFO] [scheduler] Job agent-channel-health completed (took 473ms) +2026/10/14 13:57:51 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:01 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:11 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:21 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:31 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:41 [INFO] [scheduler] Running job: stack-scan +2026/10/14 13:58:41 [INFO] [stacks] ScanStacks complete: 67 stacks found (3 deployed, 64 available) +2026/10/14 13:58:41 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:41 [INFO] [scheduler] Job stack-scan completed (took 81ms) +2026/10/14 13:58:41 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:58:41 [INFO] [scheduler] Running job: agent-channel-health +2026/10/14 13:58:42 [INFO] [scheduler] Job agent-channel-health completed (took 480ms) +2026/10/14 13:58:49 [INFO] [web] Login from 192.168.0.180 +2026/10/14 13:58:51 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:59:01 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:59:08 [INFO] [web] Login from 192.168.0.180 +2026/10/14 13:59:08 [INFO] [web] storage init "/mnt/media_a" started (device /dev/sdc, fs ext4) +2026/10/14 13:59:09 [INFO] [web] enroll /dev/sdc: mounting the agent-verified new filesystem 4ebfd36d-87d3-4b63-839e-a5a710fc799a +2026/10/14 13:59:10 [INFO] [web] enroll: drive bound into guest: /mnt/media_a +2026/10/14 13:59:10 [INFO] [settings] Added storage path: /mnt/felhom-drives/media_a +2026/10/14 13:59:10 [INFO] [settings] Settings saved +2026/10/14 13:59:10 [INFO] [web] storage init done: /mnt/felhom-drives/media_a registered (2188ms) +2026/10/14 13:59:10 [INFO] [stacks] ParseComposeHDDMounts: found 2 HDD mounts for /opt/docker/stacks/paperless-ngx/docker-compose.yml +2026/10/14 13:59:11 [INFO] [web] FileBrowser mounts synced (recreated) — 2 storage path(s), config updated +2026/10/14 13:59:11 [INFO] [stacks] Status refresh: 9 containers across 67 stacks +2026/10/14 13:59:16 [INFO] [web] Login from 192.168.0.180 +2026/10/14 13:59:16 [INFO] [web] storage init "/mnt/media_b" started (device /dev/sdd, fs ext4) +2026/10/14 13:59:16 [INFO] [web] enroll /dev/sdd: mounting the agent-verified new filesystem e15652d5-0aa7-4eb8-93a1-afb2839d527c +2026/10/14 13:59:18 [INFO] [web] enroll: drive bound into guest: /mnt/media_b +2026/10/14 13:59:18 [INFO] [settings] Added storage path: /mnt/felhom-drives/media_b +2026/10/14 13:59:18 [INFO] [settings] Settings saved +2026/10/14 13:59:18 [INFO] [web] storage init done: /mnt/felhom-drives/media_b registered (2106ms) +2026/10/14 13:59:18 [INFO] [stacks] ParseComposeHDDMounts: found 2 HDD mounts for /opt/docker/stacks/paperless-ngx/docker-compose.yml +2026/10/14 13:59:19 [INFO] [web] FileBrowser mounts synced (recreated) — 3 storage path(s), config updated +2026/10/14 13:59:21 [INFO] [stacks] Status refresh: 9 containers across 67 stacks + diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-1-demo-hp-backup.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-1-demo-hp-backup.txt new file mode 100644 index 00000000..98670404 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-1-demo-hp-backup.txt @@ -0,0 +1,7 @@ +2026-10-14T15:59:52+02:00 +--- cluster backup jobs: +[] + +ls: cannot access '/etc/pve/jobs.cfg': No such file or directory +scsi2: nvme-scratch:341/vm-341-disk-3.raw,backup=0,size=100G +scsi3: nvme-scratch:341/vm-341-disk-4.raw,backup=0,size=100G diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-2-tester1-wholeguest.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-2-tester1-wholeguest.txt new file mode 100644 index 00000000..09210797 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-2-tester1-wholeguest.txt @@ -0,0 +1,32 @@ +2026-10-14T15:59:55+02:00 +mp0: local-lvm:vm-9201-disk-1,mp=/var/lib/felhom,backup=1,size=70G +mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives +mp9: /var/lib/felhom-agent/guests/9201/bootstrap,mp=/etc/felhom-bootstrap,ro=1 +rootfs: local-lvm:vm-9201-disk-0,size=32G +total 4 +-rw-r--r-- 1 root root 1495 Oct 14 04:44 lxc-9201.log +2026-10-14 04:42:22 INFO: Starting Backup of VM 9201 (lxc) +2026-10-14 04:42:22 INFO: including mount point rootfs ('/') in backup +2026-10-14 04:42:22 INFO: including mount point mp0 ('/var/lib/felhom') in backup +2026-10-14 04:42:22 INFO: excluding bind mount point mp8 ('/mnt/felhom-drives') from backup (not a volume) +2026-10-14 04:42:22 INFO: excluding bind mount point mp9 ('/etc/felhom-bootstrap') from backup (not a volume) +2026-10-14 04:42:22 INFO: backup mode: snapshot +2026-10-14 04:42:24 INFO: creating vzdump archive '/var/lib/vz/dump/vzdump-lxc-9201-2026_10_14-04_42_22.tar.zst' +2026-10-14 04:44:52 INFO: Total bytes written: 5447720960 (5.1GiB, 35MiB/s) +2026-10-14 04:44:53 INFO: Finished Backup of VM 9201 (00:02:31) +total 5245972 +-rw-r--r-- 1 root root 1495 Oct 14 04:44 vzdump-lxc-9201-2026_10_14-04_42_22.log +-rw-r--r-- 1 root root 16 Oct 14 04:44 vzdump-lxc-9201-2026_10_14-04_42_22.tar.zst.notes +-rw-r--r-- 1 root root 1575398000 Oct 14 04:44 vzdump-lxc-9201-2026_10_14-04_42_22.tar.zst +-rw-r--r-- 1 root root 1495 Oct 13 04:40 vzdump-lxc-9201-2026_10_13-04_38_03.log +== UPID:felhom:0014BF84:005ED7D4:6ACBEB45:vzdump:9201:felhom-agent@pve!agent: +INFO: starting new backup job: vzdump 9201 --notes-template 'felhom local-api' --compress zstd --mode snapshot --storage felhom-pbs --node felhom +INFO: Starting Backup of VM 9201 (lxc) +INFO: including mount point rootfs ('/') in backup +INFO: including mount point mp0 ('/var/lib/felhom') in backup +INFO: excluding bind mount point mp8 ('/mnt/felhom-drives') from backup (not a volume) +INFO: excluding bind mount point mp9 ('/etc/felhom-bootstrap') from backup (not a volume) +INFO: Starting backup: [tester-1]:ct/9201/2026-10-11T20:02:13Z +INFO: Starting backup protocol: Sun Oct 11 22:02:14 2026 +INFO: Finished Backup of VM 9201 (00:00:58) +TASK OK diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-3-capture-sets.txt b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-3-capture-sets.txt new file mode 100644 index 00000000..09098fc8 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/A6-3-capture-sets.txt @@ -0,0 +1,50 @@ +# A6(3) — the PRODUCTION classifier (controller main acbbf4b = 0.310.0 source) over template dirs; nsRoot = drive A +# DRILL templates (the layout under test): +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/sonarr backup-block=false classified-binds=0 hasClassification=false + tier2-secondary: hasClassification=false paths=0 skipped=0 + tier3-offsite: hasClassification=false paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/radarr backup-block=false classified-binds=0 hasClassification=false + tier2-secondary: hasClassification=false paths=0 skipped=0 + tier3-offsite: hasClassification=false paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/qbittorrent backup-block=true classified-binds=0 hasClassification=true + tier2-secondary: hasClassification=true paths=0 skipped=0 + tier3-offsite: hasClassification=true paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/transmission backup-block=true classified-binds=0 hasClassification=true + tier2-secondary: hasClassification=true paths=0 skipped=0 + tier3-offsite: hasClassification=true paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/plex backup-block=false classified-binds=0 hasClassification=false + tier2-secondary: hasClassification=false paths=0 skipped=0 + tier3-offsite: hasClassification=false paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/drill/app-catalog-drill/templates/jellyfin backup-block=false classified-binds=0 hasClassification=false + tier2-secondary: hasClassification=false paths=0 skipped=0 + tier3-offsite: hasClassification=false paths=0 skipped=0 + +# CONTROL 1 — LIVE templates (today's shape): +== /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/templates/sonarr backup-block=true classified-binds=2 hasClassification=true + bind root=userdata rel="media/tv" ro=false class="excluded" origin=explicit + bind root=userdata rel="downloads" ro=false class="excluded" origin=explicit + tier2-secondary: hasClassification=true paths=0 skipped=0 + tier3-offsite: hasClassification=true paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/templates/qbittorrent backup-block=true classified-binds=1 hasClassification=true + bind root=userdata rel="media/torrents" ro=false class="optional" origin=explicit + tier2-secondary: hasClassification=true paths=1 skipped=0 + CAPTURED /mnt/felhom-drives/media_a/userdata/media/torrents (optional) + tier3-offsite: hasClassification=true paths=0 skipped=0 +== /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/templates/transmission backup-block=true classified-binds=2 hasClassification=true + bind root=userdata rel="media/transmission" ro=false class="optional" origin=explicit + bind root=userdata rel="media/transmission/watch" ro=false class="mandatory" origin=default_writable + tier2-secondary: hasClassification=true paths=1 skipped=0 + CAPTURED /mnt/felhom-drives/media_a/userdata/media/transmission (optional) + tier3-offsite: hasClassification=true paths=1 skipped=0 + CAPTURED /mnt/felhom-drives/media_a/userdata/media/transmission/watch (mandatory) + +# CONTROL 2 (decoy, must show CAPTURED mandatory) — live sonarr with a backup block that does NOT list its writable ${USERDATA_PATH} binds: +== /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/bf86b82e-7538-4da2-99f7-a6f96249f7fb/scratchpad/decoy/sonarr-unlisted backup-block=true classified-binds=2 hasClassification=true + bind root=userdata rel="media/tv" ro=false class="mandatory" origin=default_writable + bind root=userdata rel="downloads" ro=false class="mandatory" origin=default_writable + tier2-secondary: hasClassification=true paths=2 skipped=0 + CAPTURED /mnt/felhom-drives/media_a/userdata/downloads (mandatory) + CAPTURED /mnt/felhom-drives/media_a/userdata/media/tv (mandatory) + tier3-offsite: hasClassification=true paths=2 skipped=0 + CAPTURED /mnt/felhom-drives/media_a/userdata/downloads (mandatory) + CAPTURED /mnt/felhom-drives/media_a/userdata/media/tv (mandatory) diff --git a/documentation/audits/media-all-drives-2026-10-14/A-drives/PREDICTIONS-A4-A6.md b/documentation/audits/media-all-drives-2026-10-14/A-drives/PREDICTIONS-A4-A6.md new file mode 100644 index 00000000..369ea505 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/A-drives/PREDICTIONS-A4-A6.md @@ -0,0 +1,19 @@ +# Predictions — written before Part A4/A5/A6 ran (2026-10-14 ~16:00 CEST) + +A4 enrolment +- Both disks enrol through /api/storage/init (ext4) and mount at /mnt/felhom-drives/media_a and media_b in the guest. +- Neither becomes the Tier-1 backup target: the target role is written ONLY by /api/storage/backup-target/assign + (the offer's acceptance), never by registration. The target stays `local`. +- Neither becomes the default storage: this run sends set_default=false (the wizard's checkbox is ON by default). + +A5 skeleton and file browser +- Both drives get felhom-data/userdata/{media/{movies,tv,music,...},downloads,...}. +- FileBrowser's container is recreated and binds both new drives' userdata/. +- The dashboard says nothing about apps needing a restart (no such notice exists in v0.310.0). + +A6 backups +- (1) demo-hp: scsi2/scsi3 carry backup=0 (read back), and demo-hp has no vzdump job for VM 341 at all. +- (2) Tester 1's whole-guest backup (vzdump of 9201) does not include /mnt/felhom-drives: mp8 is a host bind mount, + out of vzdump scope (07 §6.1 FACT). The new drives arrive UNDER mp8 (bound_under_parent), so no new mp line. +- (3) A plain-host-path bind is not classified (classifyRoot knows only ${..._PATH}), so it is in no app capture set + at any tier. An unlisted writable ${USERDATA_PATH} bind WOULD be mandatory (Tier 3 included). diff --git a/documentation/audits/media-all-drives-2026-10-14/AFTER-power-cut-1725.txt b/documentation/audits/media-all-drives-2026-10-14/AFTER-power-cut-1725.txt new file mode 100644 index 00000000..f1131158 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/AFTER-power-cut-1725.txt @@ -0,0 +1,37 @@ +bookstack Up 5 minutes (healthy) +bookstack-db Up 5 minutes (healthy) +cloudflared Up 5 minutes (healthy) +felhom-controller Up 5 minutes (healthy) +filebrowser Up 5 minutes (healthy) +paperless-postgres Up 5 minutes (healthy) +paperless-redis Up 5 minutes (healthy) +paperless-webserver Up 5 minutes (healthy) +privatebin Up 5 minutes (healthy) +qbittorrent Up 4 minutes (healthy) +radarr Up 4 minutes (healthy) +sonarr Up 4 minutes (healthy) +traefik Up 5 minutes +transmission Up 4 minutes (healthy) +TARGET SOURCE +/mnt/felhom-drives /dev/mapper/pve-root[/mnt/felhom-drives] +|-/mnt/felhom-drives/media_a /dev/sdc[/felhom-data] +|-/mnt/felhom-drives/adatlemez /dev/sdb[/felhom-data] +`-/mnt/felhom-drives/media_b /dev/sdd[/felhom-data] +sonarr sees: 14 14 +radarr sees: 14 14 +qbittorrent sees: 14 14 +transmission sees: 14 14 +24: repo_url: file:///opt/docker/felhom-controller/drill-catalog.git +2026/10/14 15:28:40 [INFO] [stacks] Stack paperless-ngx started successfully (took 11.8s) +2026/10/14 15:28:40 [INFO] [gate] boot 1791991571-10166: live bind confirmed — recreating drive-backed app radarr (state=starting) onto /mnt/felhom-drives/media_a +2026/10/14 15:28:44 [INFO] [stacks] Stack paperless-ngx post-start status: +2026/10/14 15:28:44 [INFO] [stacks] paperless-postgres postgres:18-alpine@sha256:77f585114c32fbca283dc835b0596f4e52b51b4c6662d7810b2f4084f60a1873 running Up 14 seconds (healthy) +2026/10/14 15:28:44 [INFO] [stacks] paperless-redis redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 running Up 14 seconds (healthy) +2026/10/14 15:28:44 [INFO] [stacks] paperless-webserver ghcr.io/paperless-ngx/paperless-ngx:2.20.15@sha256:6c86cad803970ea782683a8e80e7403444c5bf3cf70de63b4d3c8e87500db92f running Up 4 seconds (health: starting) +2026/10/14 15:28:46 [INFO] [gate] boot 1791991571-10166: live bind confirmed — recreating drive-backed app sonarr (state=starting) onto /mnt/felhom-drives/media_a +2026/10/14 15:28:51 [INFO] [gate] boot 1791991571-10166: live bind confirmed — recreating drive-backed app transmission (state=running) onto /mnt/felhom-drives/media_a +2026/10/14 15:28:58 [INFO] [gate] boot 1791991571-10166: live bind confirmed — recreating drive-backed app qbittorrent (state=running) onto /mnt/felhom-drives/media_a +2026/10/14 15:29:01 [INFO] [gate] boot 1791991571-10166: re-syncing FileBrowser mounts against the live binds +2026/10/14 15:29:01 [INFO] [stacks] ParseComposeHDDMounts: found 2 HDD mounts for /opt/docker/stacks/paperless-ngx/docker-compose.yml +2026/10/14 15:33:14 [INFO] [stacks] ParseComposeHDDMounts: found 2 HDD mounts for /opt/docker/stacks/paperless-ngx/docker-compose.yml + diff --git a/documentation/audits/media-all-drives-2026-10-14/B-templates-mirror.txt b/documentation/audits/media-all-drives-2026-10-14/B-templates-mirror.txt new file mode 100644 index 00000000..18fd8abd --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/B-templates-mirror.txt @@ -0,0 +1,3 @@ +mirror main 0ad6383 DRILL media-all-drives: six media templates bind every test drive's userdata/media at the same path (Tester 1, 2026-10-14) + +drill repo main: 0ad6383 diff --git a/documentation/audits/media-all-drives-2026-10-14/B1-repoint-drill.txt b/documentation/audits/media-all-drives-2026-10-14/B1-repoint-drill.txt new file mode 100644 index 00000000..42688034 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/B1-repoint-drill.txt @@ -0,0 +1,22 @@ +24: repo_url: file:///opt/docker/felhom-controller/drill-catalog.git +24c24 +< repo_url: https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git +--- +> repo_url: file:///opt/docker/felhom-controller/drill-catalog.git + +2026/10/14 14:04:37 [INFO] [sync] Updated radarr/.felhom.yml +2026/10/14 14:04:37 [INFO] [sync] Updated sonarr/docker-compose.yml +2026/10/14 14:04:37 [INFO] [sync] Updated sonarr/.felhom.yml +2026/10/14 14:04:37 [INFO] [sync] Updated transmission/docker-compose.yml +2026/10/14 14:04:37 [INFO] [sync] Updated transmission/.felhom.yml +2026/10/14 14:04:37 [INFO] [sync] catalog moved 8ee687e544e0 -> 0ad638315272 (0 new, 6 stack dir(s) changed) +2026/10/14 14:04:37 [INFO] [sync] Catalog sync complete +2026/10/14 14:04:37 [INFO] [sync] Initial sync: Sablonok frissítve — frissítve: jellyfin, plex, qbittorrent, radarr, sonarr, transmission + +24: repo_url: file:///opt/docker/felhom-controller/drill-catalog.git +25: sync_interval: 15m +-rw------- 1 root root 3044 Oct 4 19:41 /var/lib/docker/volumes/felhom-controller-data/_data/controller.yaml.pre-media-2026-10-14 +mirror main 0ad6383 DRILL media-all-drives: six media templates bind every test drive's userdata/media at the same path (Tester 1, 2026-10-14) +cache HEAD 0ad6383 DRILL media-all-drives: six media templates bind every test drive's userdata/media at the same path (Tester 1, 2026-10-14) +file:///opt/docker/felhom-controller/drill-catalog.git + diff --git a/documentation/audits/media-all-drives-2026-10-14/B1-three-controls.txt b/documentation/audits/media-all-drives-2026-10-14/B1-three-controls.txt new file mode 100644 index 00000000..b376a961 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/B1-three-controls.txt @@ -0,0 +1,43 @@ +2026-10-14T16:05:05+02:00 +== control 1: Tester 1 cache carries the drill layout +perl: warning: Setting locale failed. +perl: warning: Please check that your locale settings: + LANGUAGE = (unset), + LC_ALL = (unset), + LC_CTYPE = "UTF-8", + LC_NUMERIC = (unset), + LC_COLLATE = (unset), + LC_TIME = (unset), + LC_MESSAGES = (unset), + LC_MONETARY = (unset), + LC_ADDRESS = (unset), + LC_IDENTIFICATION = (unset), + LC_MEASUREMENT = (unset), + LC_PAPER = (unset), + LC_TELEPHONE = (unset), + LC_NAME = (unset), + LANG = "en_US.UTF-8" + are supported and installed on your system. +perl: warning: Falling back to a fallback locale ("en_US.UTF-8"). +1 +0ad6383 +== control 2: the other boxes' caches still follow the live catalog +-- demo-hp 9201: +8ee687e +https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git +0 +-- demo-hp 9202: +8ee687e +https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git +0 +-- N100 (felhom-pve) guest: + LANGUAGE = (unset), + LANG = "en_US.UTF-8" +guest 9201 + LANGUAGE = (unset), + LANG = "en_US.UTF-8" +8ee687e +https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git +0 +== control 3: live catalog main +8ee687e544e0bc52980458ebf3c86e9484dba1fa refs/heads/main diff --git a/documentation/audits/media-all-drives-2026-10-14/B4-deploy.txt b/documentation/audits/media-all-drives-2026-10-14/B4-deploy.txt new file mode 100644 index 00000000..cd343d34 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/B4-deploy.txt @@ -0,0 +1,38 @@ +16:05:42 qbittorrent: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "torrent", "HDD_PATH": "/mnt/felhom-drives/media_a", "QBT_ADMIN_PASSWORD": ""} -> 202 {"ok": true, "message": "Telepítés elindítva – az állapot a kártyán követhető"} +16:05:57 qbittorrent: deployed, state=running, pinned={'qbittorrent': 'qbittorrentofficial/qbittorrent-nox:5.2.4-1'} +running /var/lib/docker/volumes/qbittorrent_qbittorrent_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:true /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:true +2026/10/14 14:05:42 [INFO] [api] Deploy requested for stack: qbittorrent +2026/10/14 14:05:42 [INFO] [stacks] Deploying stack qbittorrent with 4 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH, QBT_ADMIN_PASSWORD] +2026/10/14 14:05:42 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: qBittorrent +2026/10/14 14:05:47 [INFO] [stacks] Stack qbittorrent deployed successfully (took 5.5s) +2026/10/14 14:05:48 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: qBittorrent +2026/10/14 14:05:53 [INFO] [stacks] after_install qbittorrent: qbittorrent [/bin/sh -c curl -s -c /tmp/fc -o /dev/null -X POST -d "username=admin&password=${QBT_ADMIN_PASSWORD}" -H 'Referer: http://127.0.0.1:8080' http://127.0.0.1:8080/api/v2/auth/login && curl -s -b /tmp/fc -o /dev/null -X POST --data-urlencode 'json={"upnp":false,"web_ui_domain_list":"${SUBDOMAIN}.${DOMAIN};qbittorrent;localhost;127.0.0.1"}' -H 'Referer: http://127.0.0.1:8080' http://127.0.0.1:8080/api/v2/app/setPreferences && curl -s -b /tmp/fc http://127.0.0.1:8080/api/v2/app/preferences | grep -q '"upnp":false' && curl -s -o /dev/null -w '%{http_code}' -H 'Host: qbittorrent:8080' http://127.0.0.1:8080/ | grep -qE '^(200|401|403)$' && echo FELHOM_AFTER_INSTALL_OK +2026/10/14 14:05:53 [INFO] [stacks] qbittorrent: install hold OPENED by after_install — the app is reached as without a hold + +16:06:01 transmission: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "transmission", "HDD_PATH": "/mnt/felhom-drives/media_a", "TR_ADMIN_PASSWORD": ""} -> 202 {"ok": true, "message": "Telepítés elindítva – az állapot a kártyán követhető"} +16:06:21 transmission: deployed, state=running, pinned={'transmission': 'lscr.io/linuxserver/transmission:version-4.1.3-r0'} +running /var/lib/docker/volumes/transmission_transmission_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media/transmission/watch->/watch:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:true /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:true +2026/10/14 14:06:01 [INFO] [api] Deploy requested for stack: transmission +2026/10/14 14:06:01 [INFO] [stacks] Deploying stack transmission with 4 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH, TR_ADMIN_PASSWORD] +2026/10/14 14:06:01 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Transmission +2026/10/14 14:06:08 [INFO] [stacks] Stack transmission deployed successfully (took 6.7s) +2026/10/14 14:06:08 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Transmission + +16:06:26 sonarr: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "sonarr", "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 +16:07:01 sonarr: deployed, state=running, pinned={'sonarr': 'lscr.io/linuxserver/sonarr:4.0.20'} +running /var/lib/docker/volumes/sonarr_sonarr_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:true /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:true +2026/10/14 14:06:26 [INFO] [api] Deploy requested for stack: sonarr +2026/10/14 14:06:26 [INFO] [stacks] Deploying stack sonarr with 3 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH] +2026/10/14 14:06:26 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Sonarr +2026/10/14 14:06:38 [INFO] [stacks] Stack sonarr deployed successfully (took 12.3s) +2026/10/14 14:06:39 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Sonarr + +16:07:05 radarr: POST deploy {"DOMAIN": "enkicsifelhom.hu", "SUBDOMAIN": "radarr", "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 +16:07:30 radarr: deployed, state=running, pinned={'radarr': 'lscr.io/linuxserver/radarr:6.4.4'} +running /var/lib/docker/volumes/radarr_radarr_config/_data->/config:true /mnt/felhom-drives/media_a/userdata/media->/mnt/felhom-drives/media_a/userdata/media:true /mnt/felhom-drives/media_b/userdata/media->/mnt/felhom-drives/media_b/userdata/media:true +2026/10/14 14:07:05 [INFO] [api] Deploy requested for stack: radarr +2026/10/14 14:07:05 [INFO] [stacks] Deploying stack radarr with 3 env vars: [DOMAIN, SUBDOMAIN, HDD_PATH] +2026/10/14 14:07:05 [INFO] Event pushed: app_deploy_started (info) [hu-only] — Alkalmazás telepítése elindult: Radarr +2026/10/14 14:07:12 [INFO] [stacks] Stack radarr deployed successfully (took 6.6s) +2026/10/14 14:07:12 [INFO] Event pushed: app_deployed (info) [hu-only] — Alkalmazás telepítve: Radarr + diff --git a/documentation/audits/media-all-drives-2026-10-14/C-apikey-at-install.txt b/documentation/audits/media-all-drives-2026-10-14/C-apikey-at-install.txt new file mode 100644 index 00000000..e3edbc9d --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/C-apikey-at-install.txt @@ -0,0 +1,4 @@ +sonarr: env SONARR__AUTH__APIKEY= -> status with that key HTTP 200; with a wrong key HTTP 401; key written to config.xml: 0 +radarr: env RADARR__AUTH__APIKEY= -> status with that key HTTP 200; with a wrong key HTTP 401; key written to config.xml: 0 +0 + diff --git a/documentation/audits/media-all-drives-2026-10-14/C-wire-run1.txt b/documentation/audits/media-all-drives-2026-10-14/C-wire-run1.txt new file mode 100644 index 00000000..adbc5808 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/C-wire-run1.txt @@ -0,0 +1,86 @@ +== qBittorrent http://172.18.0.8:8080 (password: the deploy secret, read from the container's environment) + POST http://172.18.0.8:8080/api/v2/auth/login -> 204 '' + session cookie name: QBT_SID_8080 + GET http://172.18.0.8:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + before: auto_tmm_enabled=False save_path=/mnt/felhom-drives/media_a/userdata/media/torrents temp_path_enabled=True temp_path=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + POST http://172.18.0.8:8080/api/v2/app/setPreferences -> 200 '' + GET http://172.18.0.8:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + after : auto_tmm_enabled=True (a category's save path applies only in automatic mode) + GET http://172.18.0.8:8080/api/v2/torrents/categories -> 200 {} + POST http://172.18.0.8:8080/api/v2/torrents/createCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/createCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/createCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/createCategory -> 200 '' + GET http://172.18.0.8:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b} + category movies-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category movies-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category tv-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category tv-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp +== Transmission http://172.18.0.9:9091/transmission/rpc (password: the deploy secret, read from the container's environment) + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

409: Conflict

Your request had an invalid session_id header.

To fix this, follow these steps:

  1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + before: {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/incomplete", "incomplete-dir-enabled": true, "rename-partial-files": true} + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

    409: Conflict

    Your request had an invalid session_id header.

    To fix this, follow these steps:

    1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

      409: Conflict

      Your request had an invalid session_id header.

      To fix this, follow these steps:

      1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + after : {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir-enabled": false, "rename-partial-files": true} (one incomplete folder for all torrents would be on drive A) + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

        409: Conflict

        Your request had an invalid session_id header.

        To fix this, follow these steps:

        1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + probe Host: transmission:9091 -> HTTP 200 {'arguments': {'version': '4.1.3 (838877323f)'}, 'result': 'success'} +== sonarr http://172.18.0.10:8989/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.10:8989/api/v3/tag -> 200 [list of 0] + POST http://172.18.0.10:8989/api/v3/tag -> 201 {id, label} + POST http://172.18.0.10:8989/api/v3/tag -> 201 {id, label} + tags: {'drive-a': 1, 'drive-b': 2} + GET http://172.18.0.10:8989/api/v3/rootfolder -> 200 [list of 0] + POST http://172.18.0.10:8989/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + POST http://172.18.0.10:8989/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + GET http://172.18.0.10:8989/api/v3/rootfolder -> 200 [list of 2] + root folder: /mnt/felhom-drives/media_a/userdata/media/tv accessible=True freeSpace=99701432320 + root folder: /mnt/felhom-drives/media_b/userdata/media/tv accessible=True freeSpace=99701567488 + GET http://172.18.0.10:8989/api/v3/downloadclient/schema -> 200 [list of 18] + GET http://172.18.0.10:8989/api/v3/downloadclient -> 200 [list of 0] + POST http://172.18.0.10:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.10:8989/api/v3/downloadclient -> 400 [list of 1] + client Transmission drive-a: REFUSED by sonarr's own connection test: [{"isWarning": false, "propertyName": "TvCategory", "errorMessage": "Cannot use Category and Directory", "attemptedValue": "tv-sonarr", "severity": "error"}] + POST http://172.18.0.10:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.10:8989/api/v3/downloadclient -> 400 [list of 1] + client Transmission drive-b: REFUSED by sonarr's own connection test: [{"isWarning": false, "propertyName": "TvCategory", "errorMessage": "Cannot use Category and Directory", "attemptedValue": "tv-sonarr", "severity": "error"}] + GET http://172.18.0.10:8989/api/v3/downloadclient -> 200 [list of 2] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=tv-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=tv-drive-b directory=None + GET http://172.18.0.10:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + before: minimumFreeSpaceWhenImporting=100 MB, copyUsingHardlinks=True + PUT http://172.18.0.10:8989/api/v3/config/mediamanagement/1 -> 202 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + GET http://172.18.0.10:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== radarr http://172.18.0.11:7878/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.11:7878/api/v3/tag -> 200 [list of 0] + POST http://172.18.0.11:7878/api/v3/tag -> 201 {id, label} + POST http://172.18.0.11:7878/api/v3/tag -> 201 {id, label} + tags: {'drive-a': 1, 'drive-b': 2} + GET http://172.18.0.11:7878/api/v3/rootfolder -> 200 [list of 0] + POST http://172.18.0.11:7878/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + POST http://172.18.0.11:7878/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + GET http://172.18.0.11:7878/api/v3/rootfolder -> 200 [list of 2] + root folder: /mnt/felhom-drives/media_a/userdata/media/movies accessible=True freeSpace=99701432320 + root folder: /mnt/felhom-drives/media_b/userdata/media/movies accessible=True freeSpace=99701567488 + GET http://172.18.0.11:7878/api/v3/downloadclient/schema -> 200 [list of 19] + GET http://172.18.0.11:7878/api/v3/downloadclient -> 200 [list of 0] + POST http://172.18.0.11:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.11:7878/api/v3/downloadclient -> 400 [list of 1] + client Transmission drive-a: REFUSED by radarr's own connection test: [{"isWarning": false, "propertyName": "MovieCategory", "errorMessage": "Cannot use Category and Directory", "attemptedValue": "radarr", "severity": "error"}] + POST http://172.18.0.11:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.11:7878/api/v3/downloadclient -> 400 [list of 1] + client Transmission drive-b: REFUSED by radarr's own connection test: [{"isWarning": false, "propertyName": "MovieCategory", "errorMessage": "Cannot use Category and Directory", "attemptedValue": "radarr", "severity": "error"}] + GET http://172.18.0.11:7878/api/v3/downloadclient -> 200 [list of 2] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=movies-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=movies-drive-b directory=None + GET http://172.18.0.11:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + before: minimumFreeSpaceWhenImporting=100 MB, copyUsingHardlinks=True + PUT http://172.18.0.11:7878/api/v3/config/mediamanagement/1 -> 202 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + GET http://172.18.0.11:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== done; Transmission reachable by its container name: True + diff --git a/documentation/audits/media-all-drives-2026-10-14/C-wire-run2.txt b/documentation/audits/media-all-drives-2026-10-14/C-wire-run2.txt new file mode 100644 index 00000000..29a6e5a3 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/C-wire-run2.txt @@ -0,0 +1,78 @@ +== qBittorrent http://172.18.0.8:8080 (password: the deploy secret, read from the container's environment) + POST http://172.18.0.8:8080/api/v2/auth/login -> 204 '' + session cookie name: QBT_SID_8080 + GET http://172.18.0.8:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + before: auto_tmm_enabled=True save_path=/mnt/felhom-drives/media_a/userdata/media/torrents temp_path_enabled=True temp_path=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + POST http://172.18.0.8:8080/api/v2/app/setPreferences -> 200 '' + GET http://172.18.0.8:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + after : auto_tmm_enabled=True (a category's save path applies only in automatic mode) + GET http://172.18.0.8:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b} + POST http://172.18.0.8:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.8:8080/api/v2/torrents/editCategory -> 200 '' + GET http://172.18.0.8:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b} + category movies-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category movies-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category tv-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category tv-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp +== Transmission http://172.18.0.9:9091/transmission/rpc (password: the deploy secret, read from the container's environment) + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

          409: Conflict

          Your request had an invalid session_id header.

          To fix this, follow these steps:

          1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + before: {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/incomplete", "incomplete-dir-enabled": false, "rename-partial-files": true} + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

            409: Conflict

            Your request had an invalid session_id header.

            To fix this, follow these steps:

            1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

              409: Conflict

              Your request had an invalid session_id header.

              To fix this, follow these steps:

              1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + after : {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir-enabled": false, "rename-partial-files": true} (one incomplete folder for all torrents would be on drive A) + POST http://172.18.0.9:9091/transmission/rpc -> 409 '

                409: Conflict

                Your request had an invalid session_id header.

                To fix this, follow these steps:

                1. + POST http://172.18.0.9:9091/transmission/rpc -> 200 {arguments, result} + probe Host: transmission:9091 -> HTTP 200 {'arguments': {'version': '4.1.3 (838877323f)'}, 'result': 'success'} +== sonarr http://172.18.0.10:8989/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.10:8989/api/v3/tag -> 200 [list of 2] + tags: {'drive-a': 1, 'drive-b': 2} + GET http://172.18.0.10:8989/api/v3/rootfolder -> 200 [list of 2] + GET http://172.18.0.10:8989/api/v3/rootfolder -> 200 [list of 2] + root folder: /mnt/felhom-drives/media_a/userdata/media/tv accessible=True freeSpace=99701432320 + root folder: /mnt/felhom-drives/media_b/userdata/media/tv accessible=True freeSpace=99701567488 + GET http://172.18.0.10:8989/api/v3/downloadclient/schema -> 200 [list of 18] + GET http://172.18.0.10:8989/api/v3/downloadclient -> 200 [list of 2] + client qBittorrent drive-a: exists — left as it is + POST http://172.18.0.10:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + client qBittorrent drive-b: exists — left as it is + POST http://172.18.0.10:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + GET http://172.18.0.10:8989/api/v3/downloadclient -> 200 [list of 4] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=tv-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=tv-drive-b directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/tv + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/tv + GET http://172.18.0.10:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.10:8989/api/v3/config/mediamanagement/1 -> 202 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + GET http://172.18.0.10:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== radarr http://172.18.0.11:7878/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.11:7878/api/v3/tag -> 200 [list of 2] + tags: {'drive-a': 1, 'drive-b': 2} + GET http://172.18.0.11:7878/api/v3/rootfolder -> 200 [list of 2] + GET http://172.18.0.11:7878/api/v3/rootfolder -> 200 [list of 2] + root folder: /mnt/felhom-drives/media_a/userdata/media/movies accessible=True freeSpace=99701432320 + root folder: /mnt/felhom-drives/media_b/userdata/media/movies accessible=True freeSpace=99701567488 + GET http://172.18.0.11:7878/api/v3/downloadclient/schema -> 200 [list of 19] + GET http://172.18.0.11:7878/api/v3/downloadclient -> 200 [list of 2] + client qBittorrent drive-a: exists — left as it is + POST http://172.18.0.11:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + client qBittorrent drive-b: exists — left as it is + POST http://172.18.0.11:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + GET http://172.18.0.11:7878/api/v3/downloadclient -> 200 [list of 4] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=movies-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=movies-drive-b directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/movies + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/movies + GET http://172.18.0.11:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.11:7878/api/v3/config/mediamanagement/1 -> 202 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + GET http://172.18.0.11:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== done; Transmission reachable by its container name: True + diff --git a/documentation/audits/media-all-drives-2026-10-14/README.md b/documentation/audits/media-all-drives-2026-10-14/README.md new file mode 100644 index 00000000..c9670165 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/README.md @@ -0,0 +1,22 @@ +# Media apps on every drive — the rig on the Tester 1 box (2026-10-14) + +Task: `TASK-media-all-drives` (operator rulings 2026-10-14, `09` §3). Box: Tester 1 (VM 341 on demo-hp, guest 9201, +`enkicsifelhom.hu`), controller 0.310.0, agent 0.156.1. Design written from this: `architecture/15-media-library.md`. +Predictions were written before each measurement: `A-drives/PREDICTIONS-A4-A6.md`, `S-PREDICTIONS.md`. + +| Part | Result | Evidence | +|---|---|---| +| A1 | Tester 1 read first: one drive `adatlemez` (default; not the backup target — that is `local`), apps bookstack, paperless-ngx, privatebin; guest 4096 MB (max 5824), host VM 8 GB; off-site on; demo-hp `/mnt/hdd_1` 764 G free of 938 G | `A-drives/A1-*.txt` | +| A2 | Two 100 GB disks hot-added as `scsi2`/`scsi3`, `backup=0`, on `nvme-scratch` (`/mnt/hdd_1` root). Worst case (every sparse image full) leaves ≥ 391 G free = 42 %. Memory NOT raised: the six media apps' summed peaks are 1199 MB (`sizing.json`); with the box's three apps and the platform ~2.7 GB < 4096 MB | `A-drives/A2-disks-added.txt` | +| A4 | Both enrolled through `POST /api/storage/init` (the wizard's call), ~2 s each: `media_a` (sdc), `media_b` (sdd). Neither became the default (sent `set_default:false`; **the wizard's checkbox is ON by default**) nor the backup target (`target: local` before and after) | `A-drives/A4-*.txt` | +| A5 | Skeleton on both (`userdata/{documents,downloads,media/{movies,tv,torrents,transmission,photos,books,…},roms}`); FileBrowser recreated with both drives (`/srv/media_a`, `/srv/media_b`). The dashboard: „Aktív", „Nincs alkalmazás ezen a tárolón" — **nothing about apps that would need a restart** | `A-drives/A5-*.txt` | +| A6 | (1) demo-hp has no backup job at all; `backup=0` read back. (2) Tester 1's whole-guest backups — local AND off-site PBS — log „excluding bind mount point mp8 ('/mnt/felhom-drives')". (3) The controller's own classifier over the test templates: no media bind in any capture set (control: the live templates DO capture — and a decoy with unlisted writable binds is captured mandatory) | `A-drives/A6-*.txt` | +| B1 | Tester 1 points at a **local mirror** of the drill catalog (`file:///opt/docker/felhom-controller/drill-catalog.git`) — no credential on the box (R-992: a read-only token could not be minted, the 403 is quoted in `tools/repoint.py`). Three controls pass: Tester 1's cache = drill `0ad6383`; demo-hp 9201, 9202 and the N100 guest = live `8ee687e`; live `main` unchanged | `B1-*.txt` | +| B2 | Drill commit `0ad6383`: six templates bind `/mnt/felhom-drives/{media_a,media_b}/userdata/media` at the same path (`:ro` for Plex/Jellyfin); images unchanged | `tools/make_drill.py` | +| B4 | qBittorrent, Transmission, Sonarr, Radarr deployed through `POST /api/stacks//deploy`; plain host paths accepted, no warning | `B4-deploy.txt` | +| C | `tools/wire.py`: per drive a qBittorrent category, Sonarr/Radarr root folder + tag + two client entries; min free space 10 GB; hardlinks on (default) | `C-wire-run*.txt` | +| C | API key: read from `/config/config.xml`; it CAN be set at install: `SONARR__AUTH__APIKEY` / `RADARR__AUTH__APIKEY` env → 200 with that key, 401 with another | `C-apikey-at-install.txt` | +| S1 | Today's two-bind shape: `ln: … Cross-device link` (EXDEV) on one filesystem → copy (2 inodes). One bind per drive: link count 2, one inode | `S1-hardlink.txt` | +| S2 | One public-domain film (archive.org, CC PD Mark) on drive B: through Transmission and through qBittorrent → **hardlink** in `media_b/…/movies`. archive.org's web seed failed the last piece for qBittorrent; a local peer (the same torrent in Transmission) finished it — recorded | `S2-*.txt` | +| S3 | Tag drive-a, no client forced → „Transmission drive-a", hardlink on A. Mismatch (root B, tag A) → download on A, **copy** on B (link count 1 each) | `S3-*.txt` | +| — | **17:25 CEST: demo-hp was power-cut by another session's Immich drill** (`plugcut.py --plug hp`) — Tester 1 went down with it and came back by itself; the boot gate recreated the four media apps onto drive A; all test films then removed (0 files left) | `AFTER-power-cut-1725.txt` | diff --git a/documentation/audits/media-all-drives-2026-10-14/S-PREDICTIONS.md b/documentation/audits/media-all-drives-2026-10-14/S-PREDICTIONS.md new file mode 100644 index 00000000..dd0e4460 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S-PREDICTIONS.md @@ -0,0 +1,14 @@ +# Predictions — written before S2/S3 ran (2026-10-14 ~16:30 CEST) + +S2 (root B, tag drive-b; once through qBittorrent drive-b, once through Transmission drive-b) +- Radarr approves the pushed release, sends it to the one enabled drive-b client. +- qBittorrent: the torrent lands in media_b/userdata/media/torrents/movies// (category save path, automatic mode); + incomplete pieces in media_b/.../torrents/.temp. Transmission: in media_b/.../transmission/movies//. +- Radarr imports by HARDLINK into media_b/userdata/media/movies/Night of the Living Dead (1968)/: link count 2, same inode. + No remote path mapping is needed, because every container sees the same path. + +S3 +- tag routing: root A + tag drive-a, no client forced → one of the drive-a clients takes it (never a drive-b client); + download on A, hardlink in A's movies. +- the mismatch: root B + tag drive-a → download on A, import into B as a COPY (link count 1, two inodes); the A file + keeps seeding. Radarr does not refuse it. diff --git a/documentation/audits/media-all-drives-2026-10-14/S1-hardlink.txt b/documentation/audits/media-all-drives-2026-10-14/S1-hardlink.txt new file mode 100644 index 00000000..76d5510b --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S1-hardlink.txt @@ -0,0 +1,30 @@ +### PREDICTION: old shape (two binds) -> ln fails EXDEV, mv falls back to copy (link count 1, two inodes); new shape (one bind per drive) -> hardlink, link count 2, same inode +### OLD SHAPE (live templates): -v A/media/movies:/media/movies -v A/downloads:/downloads +ln-exit=1 +copied +/downloads/s1-probe/film.mkv links=1 inode=4194328 dev=2080 +/media/movies/s1-probe/film.mkv links=1 inode=4194329 dev=2080 +### guest view after OLD shape: +/mnt/felhom-drives/media_a/userdata/downloads/s1-probe/film.mkv links=1 inode=4194328 +/mnt/felhom-drives/media_a/userdata/media/movies/s1-probe/film.mkv links=1 inode=4194329 +### NEW SHAPE: -v A/media:A/media (same path) +ln-exit=0 +/mnt/felhom-drives/media_a/userdata/media/torrents/s1-probe/film.mkv links=2 inode=4194328 dev=2080 +/mnt/felhom-drives/media_a/userdata/media/movies/s1-probe/film.mkv links=2 inode=4194328 dev=2080 +### guest view after NEW shape: +/mnt/felhom-drives/media_a/userdata/media/torrents/s1-probe/film.mkv links=2 inode=4194328 +/mnt/felhom-drives/media_a/userdata/media/movies/s1-probe/film.mkv links=2 inode=4194328 +### cleanup +/mnt/felhom-drives/media_a/userdata/downloads: + +/mnt/felhom-drives/media_a/userdata/media/movies: + +/mnt/felhom-drives/media_a/userdata/media/torrents: + +ln: failed to create hard link '/media/movies/f' => '/downloads/s1-probe/f': Cross-device link +mv-exit=0 +/media/movies/f-moved links=1 inode=4194328 +/mnt/felhom-drives/media_a/userdata/downloads: + +/mnt/felhom-drives/media_a/userdata/media/movies: + diff --git a/documentation/audits/media-all-drives-2026-10-14/S2-qbittorrent.txt b/documentation/audits/media-all-drives-2026-10-14/S2-qbittorrent.txt new file mode 100644 index 00000000..71e72680 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S2-qbittorrent.txt @@ -0,0 +1,57 @@ +# ATTEMPT 1 (16:13–16:27 CEST) — stopped by CC. Radarr approved and grabbed (qBittorrent drive-b took it: category +# movies-drive-b, save path media_b/.../torrents/movies, incomplete path media_b/.../torrents/.temp — read from +# qBittorrent's own torrents/info). The download stalled at 6.09 % (32 MiB): 0 peers, and archive.org's web seed +# answered "500 Internal Server Error" (qBittorrent log, URL https://dn721601.ca.archive.org/); stop/start did not +# revive it. A plain curl range request to the same file from the same container answered 206 at the same time. +# So the source, not the chain, stalled. The run was stopped and retried (below). +# ATTEMPT 2 (16:38–17:09 CEST), item night-of-the-living-dead-1968_202508: +=== s2-qbit: root=/mnt/felhom-drives/media_b/userdata/media/movies tag=drive-b client=qBittorrent drive-b item=archive.org/night-of-the-living-dead-1968_202508 + clients: [('qBittorrent drive-a', True, [1]), ('qBittorrent drive-b', True, [2]), ('Transmission drive-a', True, [1]), ('Transmission drive-b', False, [2])] + movie added: id=3 'Night of the Living Dead' (1968) path=/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968) tags=[2] profile=Any + release push -> 200 approved=True rejections=[] downloadAllowed=True + +11s grabbed: downloadClient=qBittorrent downloadClientName=qBittorrent drive-b downloadId=28766E9244F6920800B29298E9BBF00E41185CBA + +61s queue: status=warning trackedState=downloading left=596675329 client=qBittorrent drive-b output=None msgs=[] + +121s queue: status=downloading trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +181s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +241s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +302s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +362s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +422s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +482s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +542s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +602s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +662s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +722s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +782s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +842s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +902s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +962s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1022s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1082s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1142s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1202s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1262s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1322s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1382s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1442s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1502s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1562s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1622s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1682s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1742s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1802s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1862s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-b output=None msgs=[] + +1872s history downloadFolderImported: {"downloadClient": "qBittorrent", "downloadClientName": "qBittorrent drive-b", "droppedPath": "/mnt/felhom-drives/media_b/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4", "importedPath": "/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4", "size": "596645320"} + download file: /mnt/felhom-drives/media_b/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 links=2 inode=131097 size=596645320 + library file : /mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 links=2 inode=131097 size=596645320 + RESULT s2-qbit: HARDLINK (same inode); download on media_b, library on media_b +[finished] + +# NOTE on attempt 2: qBittorrent reached 99.994 % at ~16:41 (7.7 MB/s from archive.org's web seed) and then stalled on +# the LAST piece (the film's tail + archive.org's three side files: __ia_thumb.jpg, _meta.sqlite, _meta.xml). Its log +# shows "Received error message from URL seed ... dn721505.ca.archive.org ... 500 Internal Server Error"; 8 stop/start +# retries over 6 min did not help, while curl range requests for the same three files from DooPlex answered 206. To +# finish, CC added the SAME public-domain torrent to Transmission (download dir media_b/.../transmission/s2-helper) and +# gave qBittorrent Transmission's container address as a peer (POST /api/v2/torrents/addPeers 172.18.0.9:51413) at +# 17:03; qBittorrent completed within 40 s and Radarr imported. The chain itself (grab by client, category save path, +# move from .temp, hardlink import) is unaffected; the source's web seed was the fault. diff --git a/documentation/audits/media-all-drives-2026-10-14/S2-transmission.txt b/documentation/audits/media-all-drives-2026-10-14/S2-transmission.txt new file mode 100644 index 00000000..630db504 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S2-transmission.txt @@ -0,0 +1,16 @@ +=== s2-trans: root=/mnt/felhom-drives/media_b/userdata/media/movies tag=drive-b client=Transmission drive-b item=archive.org/night-of-the-living-dead-1968_202508 + clients: [('qBittorrent drive-a', True, [1]), ('qBittorrent drive-b', False, [2]), ('Transmission drive-a', True, [1]), ('Transmission drive-b', True, [2])] + movie added: id=2 'Night of the Living Dead' (1968) path=/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968) tags=[2] profile=Any + release push -> 200 approved=True rejections=[] downloadAllowed=True + +13s grabbed: downloadClient=Transmission downloadClientName=Transmission drive-b downloadId=28766E9244F6920800B29298E9BBF00E41185CBA + +63s queue: status=downloading trackedState=downloading left=595626753 client=Transmission drive-b output=/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +123s queue: status=downloading trackedState=downloading left=328141569 client=Transmission drive-b output=/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +183s queue: status=downloading trackedState=downloading left=50383617 client=Transmission drive-b output=/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +243s queue: status=downloading trackedState=downloading left=50383617 client=Transmission drive-b output=/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +303s queue: status=downloading trackedState=downloading left=35585 client=Transmission drive-b output=/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +353s history downloadFolderImported: {"downloadClient": "Transmission", "downloadClientName": "Transmission drive-b", "droppedPath": "/mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4", "importedPath": "/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4", "size": "596645320"} + download file: /mnt/felhom-drives/media_b/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 links=2 inode=131097 size=596645320 + library file : /mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 links=2 inode=131097 size=596645320 + RESULT s2-trans: HARDLINK (same inode); download on media_b, library on media_b +[finished] + diff --git a/documentation/audits/media-all-drives-2026-10-14/S2-where-after-qbittorrent.txt b/documentation/audits/media-all-drives-2026-10-14/S2-where-after-qbittorrent.txt new file mode 100644 index 00000000..7509c3a9 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S2-where-after-qbittorrent.txt @@ -0,0 +1,20 @@ +== drive adatlemez (/mnt/felhom-drives/adatlemez/userdata/media) +== drive media_a (/mnt/felhom-drives/media_a/userdata/media) +== drive media_b (/mnt/felhom-drives/media_b/userdata/media) + -- movies/ (1 file(s)) + 596.6 MB links=2 inode=131097 movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 + -- torrents/ (2 file(s)) + 0.0 MB links=1 inode=131104 torrents/movies/.28766e9244f6920800b29298e9bbf00e41185cba.parts + 596.6 MB links=2 inode=131097 torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 + -- transmission/ (4 file(s)) + 596.6 MB links=1 inode=131100 transmission/s2-helper/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 + 0.0 MB links=1 inode=131101 transmission/s2-helper/night-of-the-living-dead-1968_202508/__ia_thumb.jpg + 0.0 MB links=1 inode=131102 transmission/s2-helper/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.sqlite + 0.0 MB links=1 inode=131103 transmission/s2-helper/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.xml + +== 7 file(s) on 3 drive(s) +== ONE FILE, TWO NAMES (hardlink — no extra copy): + drive media_b inode 131097 (596.6 MB): movies/…/night-of-the-living-dead-1968_202508.mp4 <-> torrents/…/Night of the Living Dead 1968.mp4 + 1 pair(s) +== LIBRARY FILES WITH LINK COUNT 1 (a copy, or its download is gone): + diff --git a/documentation/audits/media-all-drives-2026-10-14/S2-where-after-transmission.txt b/documentation/audits/media-all-drives-2026-10-14/S2-where-after-transmission.txt new file mode 100644 index 00000000..60d2e966 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S2-where-after-transmission.txt @@ -0,0 +1,17 @@ +== drive adatlemez (/mnt/felhom-drives/adatlemez/userdata/media) +== drive media_a (/mnt/felhom-drives/media_a/userdata/media) +== drive media_b (/mnt/felhom-drives/media_b/userdata/media) + -- movies/ (1 file(s)) + 596.6 MB links=2 inode=131097 movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 + -- transmission/ (4 file(s)) + 596.6 MB links=2 inode=131097 transmission/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 + 0.0 MB links=1 inode=131098 transmission/movies/night-of-the-living-dead-1968_202508/__ia_thumb.jpg + 0.0 MB links=1 inode=131099 transmission/movies/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.sqlite + 0.0 MB links=1 inode=131100 transmission/movies/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.xml + +== 5 file(s) on 3 drive(s) +== ONE FILE, TWO NAMES (hardlink — no extra copy): + drive media_b inode 131097 (596.6 MB): movies/…/night-of-the-living-dead-1968_202508.mp4 <-> transmission/…/Night of the Living Dead 1968.mp4 + 1 pair(s) +== LIBRARY FILES WITH LINK COUNT 1 (a copy, or its download is gone): + diff --git a/documentation/audits/media-all-drives-2026-10-14/S3-mismatch.txt b/documentation/audits/media-all-drives-2026-10-14/S3-mismatch.txt new file mode 100644 index 00000000..38dfcd38 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S3-mismatch.txt @@ -0,0 +1,29 @@ +=== s3-mis: root=/mnt/felhom-drives/media_b/userdata/media/movies tag=drive-a client=(by tag) item=archive.org/night-of-the-living-dead-1968_202508 + clients: [('qBittorrent drive-a', True, [1]), ('qBittorrent drive-b', True, [2]), ('Transmission drive-a', True, [1]), ('Transmission drive-b', True, [2])] + movie added: id=5 'Night of the Living Dead' (1968) path=/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968) tags=[1] profile=Any + release push -> 200 approved=True rejections=[] downloadAllowed=True + +14s grabbed: downloadClient=qBittorrent downloadClientName=qBittorrent drive-a downloadId=28766E9244F6920800B29298E9BBF00E41185CBA + +64s queue: status=downloading trackedState=downloading left=595528449 client=qBittorrent drive-a output=None msgs=[] + +124s queue: status=downloading trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +185s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +245s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +305s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +365s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +425s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +485s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +545s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +605s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +665s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +725s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +785s queue: status=warning trackedState=downloading left=35585 client=qBittorrent drive-a output=None msgs=[] + +805s history downloadFolderImported: {"downloadClient": "qBittorrent", "downloadClientName": "qBittorrent drive-a", "droppedPath": "/mnt/felhom-drives/media_a/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4", "importedPath": "/mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4", "size": "596645320"} + download file: /mnt/felhom-drives/media_a/userdata/media/torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 links=1 inode=4194355 size=596645320 + library file : /mnt/felhom-drives/media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 links=1 inode=131097 size=596645320 + RESULT s3-mis: COPY (two inodes); download on media_a, library on media_b +[finished] + +# NOTE: qBittorrent drive-a stalled on the LAST piece again (archive.org web seed, as in S2 attempt 2). At 17:21 CC added +# the same public-domain torrent to Transmission (media_a/.../transmission/s3-helper) and gave qBittorrent its address +# as a peer; qBittorrent completed at 17:23 and Radarr imported. Routing and import are unaffected by the helper. +# Radarr's own log around the import: + diff --git a/documentation/audits/media-all-drives-2026-10-14/S3-tag-routing.txt b/documentation/audits/media-all-drives-2026-10-14/S3-tag-routing.txt new file mode 100644 index 00000000..f21b7de4 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S3-tag-routing.txt @@ -0,0 +1,16 @@ +=== s3-tag: root=/mnt/felhom-drives/media_a/userdata/media/movies tag=drive-a client=(by tag) item=archive.org/night-of-the-living-dead-1968_202508 + clients: [('qBittorrent drive-a', True, [1]), ('qBittorrent drive-b', True, [2]), ('Transmission drive-a', True, [1]), ('Transmission drive-b', True, [2])] + movie added: id=4 'Night of the Living Dead' (1968) path=/mnt/felhom-drives/media_a/userdata/media/movies/Night of the Living Dead (1968) tags=[1] profile=Any + release push -> 200 approved=True rejections=[] downloadAllowed=True + +11s grabbed: downloadClient=Transmission downloadClientName=Transmission drive-a downloadId=28766E9244F6920800B29298E9BBF00E41185CBA + +61s queue: status=downloading trackedState=downloading left=587385601 client=Transmission drive-a output=/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +122s queue: status=downloading trackedState=downloading left=309381889 client=Transmission drive-a output=/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +182s queue: status=downloading trackedState=downloading left=52677377 client=Transmission drive-a output=/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +242s queue: status=downloading trackedState=downloading left=52677377 client=Transmission drive-a output=/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +302s queue: status=downloading trackedState=downloading left=35585 client=Transmission drive-a output=/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508 msgs=[] + +362s history downloadFolderImported: {"downloadClient": "Transmission", "downloadClientName": "Transmission drive-a", "droppedPath": "/mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4", "importedPath": "/mnt/felhom-drives/media_a/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4", "size": "596645320"} + download file: /mnt/felhom-drives/media_a/userdata/media/transmission/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 links=2 inode=4194354 size=596645320 + library file : /mnt/felhom-drives/media_a/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 links=2 inode=4194354 size=596645320 + RESULT s3-tag: HARDLINK (same inode); download on media_a, library on media_a +[finished] + diff --git a/documentation/audits/media-all-drives-2026-10-14/S3-where-mismatch.txt b/documentation/audits/media-all-drives-2026-10-14/S3-where-mismatch.txt new file mode 100644 index 00000000..c4785eea --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/S3-where-mismatch.txt @@ -0,0 +1,22 @@ +== drive adatlemez (/mnt/felhom-drives/adatlemez/userdata/media) +== drive media_a (/mnt/felhom-drives/media_a/userdata/media) + -- torrents/ (4 file(s)) + 596.6 MB links=1 inode=4194355 torrents/movies/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 + 0.0 MB links=1 inode=4194364 torrents/movies/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.xml + 0.0 MB links=1 inode=4194362 torrents/movies/night-of-the-living-dead-1968_202508/__ia_thumb.jpg + 0.0 MB links=1 inode=4194363 torrents/movies/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.sqlite + -- transmission/ (4 file(s)) + 596.6 MB links=1 inode=4194358 transmission/s3-helper/night-of-the-living-dead-1968_202508/Night of the Living Dead 1968.mp4 + 0.0 MB links=1 inode=4194361 transmission/s3-helper/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.xml + 0.0 MB links=1 inode=4194359 transmission/s3-helper/night-of-the-living-dead-1968_202508/__ia_thumb.jpg + 0.0 MB links=1 inode=4194360 transmission/s3-helper/night-of-the-living-dead-1968_202508/night-of-the-living-dead-1968_202508_meta.sqlite +== drive media_b (/mnt/felhom-drives/media_b/userdata/media) + -- movies/ (1 file(s)) + 596.6 MB links=1 inode=131097 movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 + +== 9 file(s) on 3 drive(s) +== ONE FILE, TWO NAMES (hardlink — no extra copy): + 0 pair(s) +== LIBRARY FILES WITH LINK COUNT 1 (a copy, or its download is gone): + drive media_b: media_b/userdata/media/movies/Night of the Living Dead (1968)/night-of-the-living-dead-1968_202508.mp4 (596.6 MB) + diff --git a/documentation/audits/media-all-drives-2026-10-14/tools/chain.py b/documentation/audits/media-all-drives-2026-10-14/tools/chain.py new file mode 100644 index 00000000..247c48cb --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/tools/chain.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +"""chain.py — S2/S3: one public-domain film through the WHOLE path, run INSIDE the guest (run_in_guest.py). + + Radarr (movie added with a root folder + tag) -> release push API -> download client (picked by the tag; for S2 the + other same-tag client is switched off for the grab so the named one is used) -> download on a drive -> Radarr's + completed-download import -> library file. Then the evidence: download path, library path, link counts, inodes, + Radarr's history entry. + +Content: „Night of the Living Dead" (1968), public domain (archive.org items carry the CC Public Domain Mark), < 1 GB. + +runs: + s2-qbit root B, tag drive-b, only „qBittorrent drive-b" enabled among the drive-b clients + s2-trans root B, tag drive-b, only „Transmission drive-b" enabled among the drive-b clients + s3-tag root A, tag drive-a, both drive-a clients enabled (the tag alone picks) + s3-mis root B, tag drive-a (the mismatch) + cleanup delete the movie with its files in Radarr, remove every torrent with its data in both clients +Every key/password is read at runtime and redacted from the output. +""" +import base64, datetime, json, os, re, subprocess, sys, time, urllib.error, urllib.request + +SECRETS = [] +TMDB = 10331 +ITEMS = {"s2-qbit": "night-of-the-living-dead-1968_202508", "s2-trans": "night-of-the-living-dead-1968_202508", + "s3-tag": "night-of-the-living-dead-1968_202508", "s3-mis": "night-of-the-living-dead-1968_202508"} +RUNS = {"s2-qbit": ("media_b", "drive-b", "qBittorrent drive-b"), "s2-trans": ("media_b", "drive-b", "Transmission drive-b"), + "s3-tag": ("media_a", "drive-a", None), "s3-mis": ("media_b", "drive-a", None)} + + +def red(s): + s = str(s) + for x in SECRETS: + if x: + s = s.replace(x, "") + return s + + +def say(*a): + print(red(" ".join(str(x) for x in a)), flush=True) + + +def sh(c): + return subprocess.run(c, shell=True, capture_output=True, text=True).stdout.strip() + + +def ip(c): + return sh(f"docker inspect {c} -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}} {{{{end}}}}'").split()[0] + + +def call(method, url, headers=None, data=None, form=False): + h = dict(headers or {}) + if data is not None: + if form: + data = "&".join(f"{k}={urllib.request.quote(str(v))}" for k, v in data.items()) + h["Content-Type"] = "application/x-www-form-urlencoded" + else: + data = json.dumps(data) + h["Content-Type"] = "application/json" + data = data.encode() + req = urllib.request.Request(url, data=data, headers=h, method=method) + try: + with urllib.request.urlopen(req, timeout=120) as r: + code, txt, hd = r.status, r.read().decode("utf-8", "replace"), {k.lower(): v for k, v in r.headers.items()} + except urllib.error.HTTPError as e: + code, txt, hd = e.code, e.read().decode("utf-8", "replace"), {k.lower(): v for k, v in e.headers.items()} + try: + body = json.loads(txt) if txt else None + except ValueError: + body = txt + return code, body, hd + + +key = re.search(r"([^<]+)", open("/var/lib/docker/volumes/radarr_radarr_config/_data/config.xml").read()).group(1) +SECRETS.append(key) +RA = f"http://{ip('radarr')}:7878/api/v3" +H = {"X-Api-Key": key} + + +def ra(method, path, data=None): + c, b, _ = call(method, RA + path, H, data) + if c >= 300: + say(f" !! radarr {method} {path} -> {c} {red(json.dumps(b))[:500]}") + return c, b + + +def movie(): + c, ms = ra("GET", "/movie") + return next((m for m in ms if m.get("tmdbId") == TMDB), None) + + +def clients(): + return ra("GET", "/downloadclient")[1] + + +def set_enable(cl, on): + if cl["enable"] != on: + cl = dict(cl, enable=on) + ra("PUT", f"/downloadclient/{cl['id']}", cl) + + +def tag_id(label): + return next(t["id"] for t in ra("GET", "/tag")[1] if t["label"] == label) + + +def st(p): + try: + s = os.stat(p) + return f"links={s.st_nlink} inode={s.st_ino} size={s.st_size}" + except OSError as e: + return f"stat failed: {e}" + + +# ---------------------------------------------------------------- client side (for cleanup and the download path) +def qb(): + QB = f"http://{ip('qbittorrent')}:8080" + pw = sh("docker exec qbittorrent printenv QBT_ADMIN_PASSWORD"); SECRETS.append(pw) + c, _, hd = call("POST", QB + "/api/v2/auth/login", {"Referer": QB}, {"username": "admin", "password": pw}, form=True) + m = re.search(r"((?:QBT_)?SID[A-Za-z0-9_]*)=([^;]+)", hd.get("set-cookie", "")); SECRETS.append(m.group(2)) + return QB, {"Referer": QB, "Cookie": f"{m.group(1)}={m.group(2)}"} + + +def tr(method, args=None): + TR = f"http://{ip('transmission')}:9091/transmission/rpc" + pw = sh("docker exec transmission printenv PASS"); SECRETS.append(pw) + h = {"Authorization": "Basic " + base64.b64encode(f"admin:{pw}".encode()).decode()} + SECRETS.append(h["Authorization"].split()[1]) + c, b, hd = call("POST", TR, h, {"method": method, "arguments": args or {}}) + if c == 409: + h["X-Transmission-Session-Id"] = hd.get("x-transmission-session-id", "") + c, b, hd = call("POST", TR, h, {"method": method, "arguments": args or {}}) + return b + + +def cleanup(): + m = movie() + if m: + ra("DELETE", f"/movie/{m['id']}?deleteFiles=true&addImportExclusion=false") + say(f"radarr: movie {m['id']} deleted with its files") + QB, QH = qb() + c, ts, _ = call("GET", QB + "/api/v2/torrents/info", QH) + for t in ts or []: + call("POST", QB + "/api/v2/torrents/delete", QH, {"hashes": t["hash"], "deleteFiles": "true"}, form=True) + say(f"qbittorrent: removed {t['name']} with its data ({t['content_path']})") + b = tr("torrent-get", {"fields": ["id", "name", "downloadDir"]}) + for t in (b or {}).get("arguments", {}).get("torrents", []): + tr("torrent-remove", {"ids": [t["id"]], "delete-local-data": True}) + say(f"transmission: removed {t['name']} with its data ({t['downloadDir']})") + for c in ra("GET", "/downloadclient")[1]: + set_enable(c, True) + say("all download clients enabled again") + + +def run(name): + drive, tag, only = RUNS[name] + item = ITEMS[name] + root = f"/mnt/felhom-drives/{drive}/userdata/media/movies" + say(f"=== {name}: root={root} tag={tag} client={'(by tag)' if not only else only} item=archive.org/{item}") + m = movie() + if m: + sys.exit(say("STOP: the film is already in Radarr — run cleanup first") or 1) + cls = clients() + if only: + for c in cls: + if tag_id(tag) in c["tags"]: + set_enable(c, c["name"] == only) + say(" clients:", [(c["name"], c["enable"], c["tags"]) for c in clients()]) + c, lk = ra("GET", f"/movie/lookup/tmdb?tmdbId={TMDB}") + qp = ra("GET", "/qualityprofile")[1][0] + body = dict(lk, qualityProfileId=qp["id"], rootFolderPath=root, monitored=True, tags=[tag_id(tag)], + minimumAvailability="released", addOptions={"searchForMovie": False}) + c, m = ra("POST", "/movie", body) + say(f" movie added: id={m['id']} '{m['title']}' ({m['year']}) path={m['path']} tags={m['tags']} profile={qp['name']}") + sz = {"night-of-the-living-dead-1968_202312": 550604180, "night-of-the-living-dead-1968_202508": 596600000, + "night-of-the-living-dead_202309": 598500000}[item] + rel = {"title": "Night.of.the.Living.Dead.1968.480p.WEB-DL.x264-ARCHIVEORG", + "downloadUrl": f"https://archive.org/download/{item}/{item}_archive.torrent", + "protocol": "torrent", "publishDate": datetime.datetime.now(datetime.timezone.utc).isoformat(), + "size": sz, "indexer": "archive.org (release push)"} + t0 = time.time() + c, d = ra("POST", "/release/push", rel) + dd = d[0] if isinstance(d, list) else d + say(f" release push -> {c} approved={dd.get('approved')} rejections={dd.get('rejections')} downloadAllowed={dd.get('downloadAllowed')}") + grabbed = imported = None + for i in range(240): + time.sleep(10) + c, h = ra("GET", f"/history/movie?movieId={m['id']}") + for e in h or []: + if e["eventType"] == "grabbed" and not grabbed: + grabbed = e + say(f" +{int(time.time()-t0)}s grabbed: downloadClient={e['data'].get('downloadClient')} " + f"downloadClientName={e['data'].get('downloadClientName')} downloadId={e.get('downloadId')}") + if e["eventType"] == "downloadFolderImported": + imported = e + if imported: + break + if i % 6 == 5: + c, q = ra("GET", f"/queue?movieIds={m['id']}") + for r in (q or {}).get("records", []): + say(f" +{int(time.time()-t0)}s queue: status={r.get('status')} trackedState={r.get('trackedDownloadState')} " + f"left={r.get('sizeleft')} client={r.get('downloadClient')} output={r.get('outputPath')} " + f"msgs={[s.get('messages') for s in r.get('statusMessages', [])]}") + if not imported: + say(f" RESULT {name}: NOT imported within {int(time.time()-t0)}s") + return + e = imported["data"] + say(f" +{int(time.time()-t0)}s history downloadFolderImported: {json.dumps({k: e.get(k) for k in ('downloadClient', 'downloadClientName', 'droppedPath', 'importedPath', 'size')})}") + say(f" download file: {e['droppedPath']} {st(e['droppedPath'])}") + say(f" library file : {e['importedPath']} {st(e['importedPath'])}") + a, b = os.stat(e["droppedPath"]), os.stat(e["importedPath"]) + say(f" RESULT {name}: {'HARDLINK (same inode)' if (a.st_ino, a.st_dev) == (b.st_ino, b.st_dev) else 'COPY (two inodes)'}" + f"; download on {e['droppedPath'].split('/')[3]}, library on {e['importedPath'].split('/')[3]}") + for c in clients(): + set_enable(c, True) + + +if sys.argv[1] == "cleanup": + cleanup() +else: + run(sys.argv[1]) diff --git a/documentation/audits/media-all-drives-2026-10-14/tools/deploy.py b/documentation/audits/media-all-drives-2026-10-14/tools/deploy.py new file mode 100644 index 00000000..9abca0c1 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/tools/deploy.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +"""deploy.py [ ...] — Part B4: install an app on the Tester 1 box the way the deploy wizard does: +GET /api/stacks//deploy-fields, POST /api/stacks//deploy, then wait for the deploy's own completion mark +(the pin in app.yaml), as box_walk.deploy does. + +Values: DOMAIN, the app's default SUBDOMAIN, HDD_PATH = drive A (the drive the household would pick; the drill +templates bind both drives by plain path and do not read it), and a generated value for each password field — kept +ONLY in the 0600 file ~/.felhom-tester1/secrets.json (never printed, never in evidence). box_walk.deploy is not used +because its deploy_values makes a folder on the scratch box's drive path, which does not exist here. + +Live-probes rule: never send a deploy for an app that is not meant to be installed. This script refuses an app that is +already installed and installs only the apps named on the command line. +""" +import json, os, secrets, sys, time +sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") +import box_walk as w + +HDD = "/mnt/felhom-drives/media_a" +SEC = os.path.expanduser("~/.felhom-tester1/secrets.json") + + +def load(): + try: + return json.load(open(SEC)) + except FileNotFoundError: + return {} + + +def save(d): + fd = os.open(SEC, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + with os.fdopen(fd, "w") as f: + json.dump(d, f) + + +w.login() +for app in sys.argv[1:]: + st = w.stack(app) + if st.get("deployed"): + w.say(f"{app}: already installed — not touched") + continue + c, d = w.ctl("GET", f"/api/stacks/{app}/deploy-fields") + fields = (((d.get("data") or {}).get("metadata") or {}).get("deploy_fields")) or [] + vals, gen = {"DOMAIN": w.DOMAIN}, [] + for f in fields: + ev, ty = f.get("env_var"), f.get("type") + if ev == "DOMAIN": + continue + if ev == "SUBDOMAIN": + vals[ev] = f.get("default") or st.get("meta", {}).get("subdomain") or app + elif ev == "HDD_PATH": + vals[ev] = HDD + elif ty == "password": + vals[ev] = "T1-" + secrets.token_urlsafe(15) + gen.append(ev) + elif f.get("required"): + sys.exit(w.say(f"{app}: required field {ev} ({ty}) has no rule here — STOP, not guessing") or 1) + s = load(); s[app] = {k: vals[k] for k in gen}; save(s) + shown = {k: ("" if k in gen else v) for k, v in vals.items()} + c, d = w.ctl("POST", f"/api/stacks/{app}/deploy", {"values": vals}) + w.say(f"{app}: POST deploy {json.dumps(shown)} -> {c} {json.dumps(d, ensure_ascii=False)[:200]}") + if c != "202": + continue + for _ in range(120): + time.sleep(5) + st = w.stack(app) + pins = (st.get("app_config") or {}).get("pinned_images") + if st.get("deployed") and pins and st.get("state") in ("running", "unhealthy", "degraded"): + w.say(f"{app}: deployed, state={st.get('state')}, pinned={pins}") + break + else: + w.say(f"{app}: never became deployed (state={st.get('state')})") + print(w.guest(f"docker inspect {app} -f '{{{{.State.Status}}}} {{{{range .Mounts}}}}{{{{.Source}}}}->{{{{.Destination}}}}:{{{{.RW}}}} {{{{end}}}}'; " + f"docker logs --since 10m felhom-controller 2>&1 | grep -i '{app}' | grep -v DEBUG | grep -iE 'after_install|deploy|error|warn' | tail -8")) diff --git a/documentation/audits/media-all-drives-2026-10-14/tools/enrol.py b/documentation/audits/media-all-drives-2026-10-14/tools/enrol.py new file mode 100644 index 00000000..67c744e9 --- /dev/null +++ b/documentation/audits/media-all-drives-2026-10-14/tools/enrol.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""enrol.py