Media build docs: 15 BUILT + §11 (evidence per part, measured traps); capability D13; 03 eject with media apps; 07 per-drive binds in no tier; 13 the fresh-Plex boundary; register: 9 rows closed (FIXED v0.313.0), R-1023/R-1045/R-1049 waiting on the operator, R-1028 narrowed, R-1022 teardown + Media D
gates / gates (push) Successful in 6m46s
gates / gates (push) Successful in 6m46s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -177,6 +177,7 @@
|
||||
| <a id="d10"></a>[D10](capability-map-detail/D.md#d10) | USB drive enrollment + unplug detection + recommission | controller, agent | **PROVEN-LIVE** | — | `audits/storage-lifecycle-acceptance-2026-06-15.md` (2026-06-15); `audits/CAMPAIGN-4-2026-07-13.md` (2026-07-13) |
|
||||
| <a id="d11"></a>[D11](capability-map-detail/D.md#d11) | Decommission (migrate-first and anyway-paths), eject | agent, controller | **PROVEN-LIVE** | — | `audits/storage-lifecycle-acceptance-2026-06-15.md` (2026-06-15) |
|
||||
| <a id="d12"></a>[D12](capability-map-detail/D.md#d12) | Boot ordering: automount + networking survive reboot; appliance self-heal watchdog | agent v0.85 | **PROVEN-LIVE** | — | `audits/CAMPAIGN-4-2026-07-13.md` (2026-07-13); `audits/CAMPAIGN-6A-2026-07-14.md` (2026-07-14) |
|
||||
| <a id="d13"></a>[D13](capability-map-detail/D.md#d13) | Media apps see EVERY drive; a drive added, pulled, back or safely removed is handled per app (notice, real restart, wiring, torrents paused/resumed) | controller v0.313.0, catalog `0188430`, hub v0.151.0 | **PROVEN-LIVE** | Tier-0 boxes (Tester 1, demo-hp 9201); the fleet with golden 0.313.0 + the floor | `audits/media-build-2026-10-15/G/` (2026-10-15); `audits/media-drive-events-2026-10-14/` (2026-10-14) |
|
||||
|
||||
## E. Access, networking & household use
|
||||
|
||||
|
||||
@@ -253,6 +253,11 @@ The controller (in its LXC) reaches the agent (on the host) over the local bridg
|
||||
/disks/eject` (safe-unmount, **data preserved**, returns the dependent guests so the controller
|
||||
warns which apps lose that storage — benign), `POST /disks/format` (see the reframed principle
|
||||
below). The controller is Docker-only (de-privileged, slice 8C); **execution is the agent's**.
|
||||
- **Eject with media apps (controller v0.313.0, R-1044, `15` §10/§11):** before the agent's `POST /disks/eject` the
|
||||
controller pauses that drive's torrents and recreates every media app and the file browser WITHOUT the drive, and
|
||||
afterwards KEEPS the drive registered (removed); its return is found by the agent's report — presence is a non-empty
|
||||
`backing_device`, because `state` stays „attached" for a registered disk that is gone (measured on Tester 1). No
|
||||
agent change was needed.
|
||||
|
||||
**The principle (reframed for 8C):** a controller may do **non-data-destructive** storage setup
|
||||
**self-serve** (list, assign, eject, format a *blank* drive); **anything that can lose customer data
|
||||
|
||||
@@ -332,6 +332,12 @@ Recorded here so the encryption policy is not read as covering it. → **R-108**
|
||||
|
||||
The tiers are **inputs to recovery**, not recovery routes. §7 and §8 say what they can actually do.
|
||||
|
||||
**[FACT, controller v0.313.0] The per-drive media binds are in NO tier.** A media app's binds for the other drives live
|
||||
in the controller-owned `docker-compose.override.yml`, which no classifier, export or removal reads (pinned by
|
||||
`TestMediaLibrary_OverrideIsInNoCaptureSet`); the template's own one-drive media line stays `excluded` as before. The
|
||||
family's media library is no single app's data (`15-media-library.md` §7); a household-level backup of it is a separate
|
||||
decision, not made.
|
||||
|
||||
**[RULING 2026-09-16, operator] Tier 3 (off-site) is ON for every new customer — shared, 100 GB soft
|
||||
quota.** Opting a customer out stays the per-customer exception, as DR-tier-by-default has been since
|
||||
2026-07-12. **The reason is this document's own [FACT] two sections down:** the whole-guest tiers do
|
||||
|
||||
@@ -59,6 +59,7 @@ OS packages (exact versions, apt-signed), the installer script (by tag, R-980).
|
||||
|---|---|---|
|
||||
| end-user ↔ apps | holds, with gaps | traefik routing and header stripping hold; homepage has the Docker socket and no gate (R-981) |
|
||||
| **added 2026-10-14 (R-1033, P1):** a visitor ↔ Cloudflare over plain http | holds on boxes with controller ≥ 0.312.0 | before: every public name answered `http://` (login pages too), and Sonarr/Radarr/Prowlarr redirected https visitors to an absolute `http://` login; a password could cross an untrusted network unencrypted (attacker: anyone on the visitor's network). Now traefik sends every `http://` request to https (301) before an app sees it, HSTS one day; boxes below 0.312.0 (Tester 2 at 0.292.0) keep the gap until the floor delivers it. `audits/https-only-2026-10-14/` |
|
||||
| **added 2026-10-15 (R-1045):** a stranger ↔ a fresh Plex | holds on controller ≥ 0.313.0; on older ones only while the household does not press „Kész" before claiming | an unclaimed Plex answers everything to a private address with no token, and through the tunnel every request comes from one. The setup gate (catalog `9a23ee5`) closes the public path; 0.313.0 opens it only when Plex itself says claimed. A status probe meant to cover older controllers OPENED an unclaimed Plex while it started (`audits/media-build-2026-10-15/G7/`) and was removed |
|
||||
| customer ↔ controller UI | partly | bcrypt and session hold; small gaps (R-983); family sessions survive a reset (R-976) |
|
||||
| controller ↔ agent | partly | every route bound to the caller's guest; a compromised controller can still flush restore points (R-973), void the recovery code (R-978), and — since agent v0.155.0 — switch the whole machine off once per power-on, holding the hub's silence alarms for up to 14 days (R-998; mitigations and residual risk: `14-host-power-off.md` §5) |
|
||||
| **added (R-1007):** controller → an app's own job queue | holds — nothing new | the `after_unclean_boot` commands run inside the app's container through the docker socket the controller already holds, and read the app's database with a read-only session: no key into the household's account, nothing a compromised controller could not already do (exec in any app, read its database). The household's lost-file names stay on the box (`app.yaml`, the app page); the operator event carries counts only, and a failing command's output is never logged (security review of controller `8d862e4`, fixed in `3b85a0c`) |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
> learn. **No document covered this before 2026-10-14** — itself a finding: the four templates were written one by one
|
||||
> and never tested together (R-1014). Rulings: `09` §3 decisions 237–241. Measured on Tester 1:
|
||||
> `audits/media-all-drives-2026-10-14/`. Marks as in `01`: **[DESIGN]** a decision or proposal; **[FACT]** read in a
|
||||
> named file or measured. **Status: DRAFT** — measured end to end on Tester 1; the layout of §2 is RULED (decision 242) and live for one drive; the drive events of §10 measured (decision 245, option A).
|
||||
> named file or measured. **Status: BUILT (controller v0.313.0, 2026-10-15)** — §2–§10 are built and proven on Tester 1 and demo-hp 9201 (§11, with the evidence per part); delivery to the fleet is golden 0.313.0 → the operator's vouch → the floor (`09` §3 decision 247). Plex's real claim on the running server is proven by hand (K6-2); through the dashboard it waits on a household code (§11).
|
||||
|
||||
## 1. The problem
|
||||
|
||||
@@ -228,3 +228,26 @@ WITHOUT a token — it must never be routed publicly while unclaimed; `POST /myp
|
||||
claims it — PROVEN 2026-10-15 with a real code (`K6-2`): 200, `claimed` 0→1, `PlexOnlineToken` written, and the open door
|
||||
closed (no token → 401). `ADVERTISE_IP` was not needed for the claim. So the box asks for the code only once the server is up:
|
||||
no 4-minute race with the pull.
|
||||
|
||||
## 11. Built — controller v0.313.0 (2026-10-15) [FACT; evidence `audits/media-build-2026-10-15/`]
|
||||
|
||||
| Part | What is built | Where | Proven |
|
||||
|---|---|---|---|
|
||||
| §2/§3 the marker | `media_library:` (`folder:`, `read_only:`) on Sonarr, Radarr, qBittorrent, Transmission, Plex, Jellyfin, Emby (catalog `0188430`); one bind per PRESENT drive in the controller-owned `docker-compose.override.yml`, rewritten before every `compose up`; absent, dead (`shutdown`) and safely removed drives never bound; network shares never | `stacks/media_library.go` | G1: every stored path, library item, watch mark and all 69 files identical after the move; G7: controller 0.312.0 ignores the marker (one drive, no override) |
|
||||
| §4 restart | `up -d --force-recreate` with the stack's env; start times before/after; an error when nothing started | `stacks/manager.go` `RestartStack` | G1/G2/G3: six apps restarted in ~30 s, each start time moved |
|
||||
| §6 links | qBittorrent categories + automatic management; Transmission incomplete folder off (template entrypoint + belt); Sonarr/Radarr tag, root, clients, minimum free space; Prowlarr → Sonarr/Radarr; Plex folders + scan + „scan on change" with the server's own token read at use (`09` §3 decision 248); Jellyfin folders + scan with the API key named „felhom" read at use from Jellyfin's database (no key → one household step on the page) | `internal/mediawire` | G2: Media D wired into every app in one run; two more runs wrote nothing; G7: live catalog on 9201 |
|
||||
| §7/§8 backup, export, removal | the override is read by no classifier, export or removal; „delete my data" never deletes a `:ro` or shared-library bind (R-1027); the safety list knows the userdata layout (R-1032) | `stacks/delete.go` | unit tests, each red-proved |
|
||||
| §8 the wizard | a media app is not asked for a drive; its config goes on the default drive (R-1031) | `stacks/metadata.go`, `deploy.go` | unit test |
|
||||
| §9 old installs | Sonarr/Radarr from before `bc54c01`: same-path root, `moveFiles:false`, rescan, old root removed, Radarr collections — only when `/media` is no longer bound | `mediawire` `repairOldRoot` | the K7 sequence as a unit test |
|
||||
| §10 events | added / removed / returned recorded on the drive; the notice on the storage page and the dashboard with „Újraindítás most" (Plex/Jellyfin stream check first) and „Később" (03:30); per drive „restarted since"; containers holding a dead drive listed | `web/media_events.go`, `media_notice.html` | G2, G3: the notice within 6 s of a pull; one restart cured every app |
|
||||
| §10 returned | the file browser is recreated at once (R-1038); a dead or removed drive is not bound into it | `web/handlers.go` | G3: `/srv/media_b` readable the second the drive was back |
|
||||
| §10 safe removal | pause that drive's torrents → recreate the apps and the file browser without it → unmount → keep it registered; its return needs no household step; the torrents resume after the restart (R-1044) | `web/media_events.go`, `storage_handlers.go` | G4: only D's 2 torrents paused, every app let go before the unmount, back by itself 7 s after the disk, torrents seeding with no re-check |
|
||||
| Plex | code asked after the start (`after_start` field) on the Plex page; the gate opens only when Plex says claimed, „Kész" refused before (R-1028, R-1045) | `web/media_events.go`, `setup_gate.go` | G5 on 9201: no code at install, gated, „Kész" 409; the claim itself: K6-2 by hand (the dashboard claim waits on a household code) |
|
||||
|
||||
**Measured traps, kept here because each would be rebuilt:** the agent's disk `state` stays „attached" for a registered
|
||||
disk that is gone — presence is the backing device; the periodic drive gate must leave a safely removed drive alone (it
|
||||
raced the 32-second removal); qBittorrent 5 answers a good login with 204; Sonarr creates a qBittorrent category with no
|
||||
save path when its client is saved before the wiring; **a Plex status probe (`/library/sections`, done 401) OPENED an
|
||||
unclaimed Plex because Plex answers 401 for a moment while it starts — never gate on a status a starting app also
|
||||
returns** (G7f).
|
||||
|
||||
|
||||
@@ -213,3 +213,27 @@
|
||||
**Gap / roadmap (as written):** (`CAMPAIGN-3` F10/F11/F12 were the CRITICAL/HIGH *failures*; fixes shipped in agent v0.85 and were re-validated live in 4/6A — cite the validation, not the finding.) Residual: `skip-active` on `pct reboot` carried by the heal path; a NAS outage spanning a guest reboot can strand the share until agent restart (6A)
|
||||
|
||||
---
|
||||
|
||||
<a id="d13"></a>
|
||||
## D13 — Media apps see every drive; drive events handled per app
|
||||
|
||||
*Map row: [D13](../00-capability-map.md#d13) · added 2026-10-15.*
|
||||
|
||||
**Scenario:** Media apps see every drive; a drive added, pulled, back or safely removed is handled per app.
|
||||
|
||||
**Components:** controller v0.313.0 (`stacks/media_library.go`, `internal/mediawire`, `web/media_events.go`), catalog
|
||||
`0188430` (the `media_library` marker), hub v0.151.0 (the „drive came back" mail, R-1049).
|
||||
|
||||
**Status:** **PROVEN-LIVE** on Tier-0 boxes (Tester 1, demo-hp 9201), 2026-10-15. Fleet delivery: golden 0.313.0 → the
|
||||
operator's vouch → the floor (`09` §3 decision 247).
|
||||
|
||||
**Evidence:** `audits/media-build-2026-10-15/G/` — G1 every stored path, library item and file identical after the move;
|
||||
G2 a new drive wired into every app in one run, later runs write nothing; G3 a pulled drive loses nothing, the file browser
|
||||
returns at once, one restart cures the apps; G4 a safe removal pauses only that drive's torrents, every app lets go before
|
||||
the unmount, the drive returns by itself and the torrents resume; G6 13 hardlink pairs intact. Design and §11:
|
||||
`architecture/15-media-library.md`.
|
||||
|
||||
**Gap:** Plex's dashboard claim with a real code (the code is the household's); Prowlarr in the live catalog (R-1048).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user