K6-2: Plex claimed AFTER start with the household's code — 200, claimed 0->1, open door closed (401); plex-k6 removed; R-1028 proven path, R-1022 step 7 done
gates / gates (push) Successful in 6m41s

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-15 08:01:50 +02:00
parent 0e88b7eb99
commit 3e5b92225d
5 changed files with 19 additions and 6 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ Media C was added. One script run made it ready in every app. A second run chang
- **Safe removal (K3):** pausing only that drive's torrents works. But the eject does **not** free the drive from the running apps: they still read its files. After the eject the drive is also forgotten, so the household must attach it again. A restart after the eject fails, because the bind path is gone. → R-1044.
- **Dead-drive check (K4):** read `/proc/self/mountinfo`. A dead drive shows `shutdown`. No disk access is needed. Each container's own mountinfo shows if it still holds the dead drive. A timed `stat` is **not** reliable: it said „ok” on a dead drive.
- **Real restart (K5):** stop + start, or recreate, works in 2–10 s per app. FileBrowser: 5 s. Today's dashboard restart does nothing when the compose did not change (R-1026). A restart cuts a playing stream; Jellyfin answered again after 22 s.
- **Plex without the race (K6):** Plex starts with no code. The code can be given later to the running server. So the box can ask for the code last. But an unclaimed Plex opens everything without a password to a private address. Through the tunnel every request comes from a private address. So an unclaimed Plex must never get a public name (R-1045, P2). `ADVERTISE_IP`: still open; it needs the claim test.
- **Plex without the race (K6):** Plex starts with no code. The code can be given later to the running server. So the box can ask for the code last. But an unclaimed Plex opens everything without a password to a private address. Through the tunnel every request comes from a private address. So an unclaimed Plex must never get a public name (R-1045, P2). **Claim test done 2026-10-15:** your code, given to the running server: claimed, token written, the open door closed (no token → 401). `ADVERTISE_IP` was not needed. Whether clients find the server without it could not be measured (the throwaway had no open port). `plex-k6` is removed.
- **Old installs (K7):** Sonarr and Radarr from before `bc54c01` lose their library folder after the update. Nothing is deleted. Repair by the apps' own API, with no file moved: proven on the bench. → R-1047.
- **Hub mails (K8):** a pulled drive sends an error mail. When it comes back, no mail is sent. → R-1049.
+1 -1
View File
@@ -44,7 +44,7 @@
## Waiting on you — do
- **Vouch controller 0.312.0 and raise the floor** (hub). It makes every box https only, Tester 2 included. If not: the other boxes keep answering plain http (R-1033, P1).
- **Plex claim test (5 min):** open plex.tv/claim, put the code in `~/.felhom-tester1/plex-claim` on DooPlex (`chmod 600`, not in chat), then tell me „done”. If not: the Plex claim fix stays unproven (R-1028).
- **Remove the test server „plex-k6” from your Plex account** (plex.tv → Settings → Authorized Devices). The claim test passed and the server is gone from Tester 1. If not: a dead server stays listed in your Plex apps.
- System poster needs a refresh: every public name answers only over https (http → 301).
- **Clear demo-hp's own floor** (hub → Demo HP → floor: empty → Save floor). It still reads 0.311.0. If not: the next global raise passes demo-hp by.
- **Leftovers moved out of the shared checkout** to `/mnt/5_hdd/felhom.eu/leftovers/2026-10-14/` (nothing deleted): the Facebook `first-post/` folder is not in git — keep, commit or delete it. The rest is already in git. If not: it just stays there.
@@ -225,5 +225,6 @@ old warning until its next check. File and inode unchanged.
**Plex without the race (K6):** an unclaimed Plex starts with no code and answers every request from a private address
WITHOUT a token — it must never be routed publicly while unclaimed; `POST /myplex/claim?token=<code>` on the RUNNING server
claims it (the route answered and called plex.tv; a real code needs the household's account — the morning step). So the box
asks for the code only once the server is up: no 4-minute race with the pull.
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.
@@ -0,0 +1,12 @@
# K6-2 — claim the RUNNING unclaimed plex-k6 with a household code, 2026-10-15 morning (code given out-of-band in a 0600 file, shredded after; never printed)
# call: POST http://<container>:32400/myplex/claim?token=<code> (no token needed: the server was unclaimed)
before: claimed="0"
claim -> HTTP 200
after: claimed="1"
PlexOnlineToken present: yes length 20
no-token /library/sections -> 401
with-token /library/sections -> 200
myplex/account username set: 1
env ADVERTISE_IP: unset
containers: 0 volumes: 0
1
+2 -2
View File
@@ -135,7 +135,7 @@ stopping line that lies.
| **R-1018** | Apps & catalog | P3 | **The OnlyOffice links (FileBrowser, Nextcloud) have no recorded live proof since the move to Proxmox.** `controller/internal/integrations/` holds two handlers; no audit after the Proxmox move shows a document opened from FileBrowser or Nextcloud in OnlyOffice (searched 2026-10-14). The media-app links of `15` §6 will copy this code, including its revoke-on-stop rule (`lifecycle.go:10-13`). | **OPEN** — filed 2026-10-14 | — | One live walk on a Tier-0 box: install OnlyOffice + FileBrowser, open a document, save, read it back; then stop one app and record what the link does | CC |
| **R-1026** | Apps & catalog | P3 | **The dashboard's app „restart” restarts nothing.** `RestartStack` runs `docker compose up -d`, which changes nothing when the compose did not change; it logged „Stack qbittorrent restarted successfully (took 0.1s)” while the container's StartedAt stayed the same (Tester 1, 2026-10-14, `audits/media-all-drives-2026-10-14/``D-plex-remove-deleted.txt`). So the button cannot make an app see a new drive or recover from a deleted bind source, and its success line is presence, not result. Stop + start did work. **Night 2026-10-14 (K5):** stop+start, or `up -d --force-recreate`, works in 2–10 s per app — but ONLY with the controller's environment: a bare `docker compose up -d --force-recreate` by hand lost every `${…}` value (Sonarr's and qBittorrent's routes became `Host(`.`)` → public 404, qBittorrent's password empty); the dashboard restart repaired it. FileBrowser has no `${…}` and recreated in 5.2 s. | **READY** — filed 2026-10-14 | — | Make restart a real restart through `composeExecCustomEnv` (`up -d --force-recreate` with the stack env), and make its log line state what changed; a test that asserts a new container start time. The restart notice of R-1023 depends on it | CC |
| **R-1024** | Apps & catalog | P3 | **Links for the media apps: Sonarr and Radarr connected to the download apps and to a drive by the box.** Measured calls and answer shapes: `audits/media-all-drives-2026-10-14/``tools/wire.py` + `C-wire-run2.txt`. Facts the code needs: the API key can be SET at install (`SONARR__AUTH__APIKEY`/`RADARR__AUTH__APIKEY`, measured 200/401); qBittorrent needs automatic torrent management on, or a category's folder is ignored; a Transmission entry must clear the default category („Cannot use Category and Directory”); Transmission's one incomplete folder must be off; a show with no tag gets no download app when every entry is tagged — SEEN LIVE 2026-10-14 18:50: the operator added two shows with a root folder each and no tag, and Sonarr queued 11 episodes as „Pending - Download client is unavailable” while all four entries passed Sonarr's own test (`audits/media-all-drives-2026-10-14/I-client-unavailable.txt`), so the drive choice must SET the tag; the category folders (`torrents/{tv,movies}`, `transmission/{tv,movies}`) must exist before the first grab or Sonarr raises a „remote path mapping” health error; the OnlyOffice revoke-on-stop rule (`lifecycle.go:10-13`) must NOT apply (it would delete the download entries at every restart). **Night 2026-10-14 (K2, `audits/media-drive-events-2026-10-14/``K2-6/K2-7`):** a NEW drive (Media C) made ready in every app by script in one run — qBittorrent categories, Sonarr/Radarr root folder + tag + two download entries, Plex and Jellyfin library folders + a scan, Plex „scan on change” on; the second run changed nothing (it re-sent equal qBittorrent category edits — the build checks before it writes). Two gaps stay: Plex needs a token (R-1046) and Jellyfin an API key (needs an admin session: the box has one only if it made the first admin). Narrowed 2026-10-14 night. | **NARROWED** — filed 2026-10-14; per-drive wiring proven idempotent on a third drive | R-1023 | Build with R-1023 from `15` §10 (the „added” rows) | CC |
| **R-1028** | Apps & catalog | P3 | **Plex's claim code can run out during the install, and the box cannot recover it.** plex.tv's code lives 4 minutes from the moment the household copies it; the clock covers the wizard, the image pull (`plexinc/pms-docker` is 351 MB, measured) and the first start. Plex reads `PLEX_CLAIM` only on its first run, and the field is `locked_after_deploy` — so a late code leaves an unclaimed server and the only way back is remove + install again. Reported by the operator during the 2026-10-14 test; on Tester 1's line the pull+start took 8.5 s and the claim worked (`audits/media-all-drives-2026-10-14/``G-auth/G3-plex-claim.txt`), so it bites on a slow line or a slow hand. **K6, 2026-10-14 night (`audits/media-drive-events-2026-10-14/``K6-1-unclaimed-plex.txt`):** an unclaimed Plex starts with no code at all, and `POST /myplex/claim?token=` on the RUNNING server reaches plex.tv (a wrong code → 500 after plex.tv's 401). So the box can pull + start first and ask for the code after: no race. A real claim needs the household's code — the morning step on the throwaway `plex-k6` on Tester 1. Narrowed 2026-10-14 night. | **NARROWED** — the claim-on-a-running-server call exists; a real claim not yet seen | the operator's claim code (morning step) | Claim `plex-k6` with a real code through that call, read `PlexOnlineToken` back; then the build: install unclaimed, never routed publicly until claimed (R-1045), ask for the code last | CC |
| **R-1028** | Apps & catalog | P3 | **Plex's claim code can run out during the install, and the box cannot recover it.** plex.tv's code lives 4 minutes from the moment the household copies it; the clock covers the wizard, the image pull (`plexinc/pms-docker` is 351 MB, measured) and the first start. Plex reads `PLEX_CLAIM` only on its first run, and the field is `locked_after_deploy` — so a late code leaves an unclaimed server and the only way back is remove + install again. Reported by the operator during the 2026-10-14 test; on Tester 1's line the pull+start took 8.5 s and the claim worked (`audits/media-all-drives-2026-10-14/``G-auth/G3-plex-claim.txt`), so it bites on a slow line or a slow hand. **K6, 2026-10-14 night (`audits/media-drive-events-2026-10-14/``K6-1-unclaimed-plex.txt`):** an unclaimed Plex starts with no code at all, and `POST /myplex/claim?token=` on the RUNNING server reaches plex.tv (a wrong code → 500 after plex.tv's 401). So the box can pull + start first and ask for the code after: no race. A real claim needs the household's code — the morning step on the throwaway `plex-k6` on Tester 1. Narrowed 2026-10-14 night. **PROVEN 2026-10-15 morning (`K6-2-claim-after-start.txt`):** the operator's real code, given to the RUNNING unclaimed `plex-k6` → 200, `claimed` 0→1, `PlexOnlineToken` written, and the open door closed (no token → 401). `ADVERTISE_IP` was unset and the claim did not need it; whether clients find the server without it was not measurable (the throwaway had no published port). | **READY** — the claim-after-start path is proven; the build is owed | — | The build: install unclaimed with NO public route (R-1045), pull + start, then ask the household for the code and `POST /myplex/claim?token=` on the running server; read `claimed=1` back | CC |
| **R-1029** | Apps & catalog | P3 | **Plex's folder picker shows the drives as identical „media” folders.** With one bind per drive at the same path (`/mnt/felhom-drives/<drive>/userdata/media`), the leaf of every library folder is `media`; the drive's name sits two levels up, so in Plex's picker the household sees several folders all called „media” (reported by the operator during the 2026-10-14 test on Tester 1). It conflicts with `15` §2 reason 2 (the same path everywhere). | **READY** — filed 2026-10-14 | the layout decision (STATUS D9) | Decide in `15` §2: the players (`:ro`, never linked) may get a readable path per drive (e.g. `/media/<drive label>`) without breaking the hardlink rule, which binds only the download/library apps | CC |
| **R-1036** | Apps & catalog | P3 | **The apps page lags the catalog by two apps.** `app-catalog-felhom.eu/templates/` holds **64** apps; `/alkalmazasok` and `/en/apps` render **63** cards, one of which (`filebrowser`) is built in and not a catalog template — so **62 catalog apps are listed**. Missing: **`plant-it`** and **`wger`**, both in the catalog since 2026-10-06/08. Their website assets are ALREADY present (`plant-it-logo.svg` + 3 screenshots, `wger-logo.png` + 3 screenshots, renamed to the catalog slug by `a3507761`, 2026-10-08) — only the cards were never written, so this is not a new-asset job. Found 2026-10-14 while reconciling the app count for the „felhő tárhely” page, where four places said **60** and the rest said **62**. **Done in that session:** every stated count on the site set to **62** (`index.html`, `szolgaltatasok.html`, `en/services.html` were 60), which is what the apps page actually shows — so no visitor can count the cards and find the number wrong. **62 is still not the catalog's number.** **To close:** write the two cards on both apps pages (gate 15 keeps the two pages' app sets equal, so they move together), then raise every stated count 62 → 64 in the same commit. **The operator chose this split at the 2026-10-14 STOP** over doing it inside the SEO change. | **OPEN** | — | write the `plant-it` + `wger` cards on both apps pages, then 62 → 64 everywhere, one commit | CC |
| **R-1037** | Apps & catalog | P3 | **Plex never notices new files: its automatic scan is off by default.** MEASURED 2026-10-14 on Tester 1 (`audits/media-all-drives-2026-10-14/K-plex-empty.txt`): the operator made the TV and Movies libraries with both drives' folders at ~18:35; 16 episodes and 2 films were imported from 19:07 on; at 19:15 Plex still showed 0 items, no scan running, and `Preferences.xml` carries neither `FSEventLibraryUpdatesEnabled` nor `ScheduledLibraryUpdatesEnabled` (Plex's defaults: both off). One manual `/library/sections/<n>/refresh` → 3 shows and 2 films. A household sees an empty Plex and concludes the box is broken. **Night 2026-10-14 (K2):** the box can switch it on by script — `PUT /:/prefs FSEventLibraryUpdatesEnabled=1` (+ `FSEventLibraryPartialScanEnabled=1`), read back from `Preferences.xml`. Narrowed 2026-10-14 night. | **NARROWED** — the call is measured; needs the Plex token of R-1046 | R-1046 | Seed it at install with R-1024's wiring | CC |
@@ -309,7 +309,7 @@ stopping line that lies.
|---|---|---|---|---|---|---|---|
| **R-992** | Process & tooling | P3 | **The drill-catalog route that `09` §6.5 documents cannot be followed: `admin/app-catalog-drill` is PRIVATE and no box has catalog credentials.** Measured 2026-10-12 while publishing the Usenet pair: the box's clone fails with `fatal: could not read Username for 'https://gitea.dooplex.hu'` (exit 128), the Gitea API reports the drill repo `private=true` and the live catalog `private=false`, and NEITHER the current controller.yaml on 9202 NOR any of its 30 saved copies has ever carried `git.username` or `git.token` (the controller supports both — `internal/config/config.go` GitConfig). The route worked for the campaigns of 2026-10-10 and the torrent pair earlier the same day, so the repository's visibility changed between then and now. **Worked around, not fixed:** that campaign staged its two templates in the box's own catalog cache (`audits/usenet-2026-10-12/tools/stage_in_cache.py`), which is where the sync copies a catalog change anyway, with the sync interval raised to 12h for the duration and rescan used in place of sync. The real fix is an operator decision: either a read-only catalog credential on the scratch box (a secret on a Tier-0 guest) or making the drill repository readable. Until then every new-app campaign hits this. | WAITING-ON-OPERATOR | the operator's choice between a box credential and a readable drill repo | Decide; then either set `git.username`/`git.token` on 9202 from the credentials store, or flip the drill repo, and correct `09` §6.5 either way | operator |
| **R-1017** | Process & tooling | P3 | **A comment names a guard test that does not exist.** `felhom-controller/controller/internal/appbackup/paths.go:58` says „`TestNoBareDrivePathToUserdataDir` is the guard that keeps the count from growing”; no test of that name (or `NoBareDrive*`) exists in the repo (grep 2026-10-14; positive control: `TestRemoveStack_R800_UserdataKeptIsNamed` is found). A comment asserting an invariant with no test pinning it — the class in the `felhom-testing` skill. | **READY** — filed 2026-10-14 | — | Write the guard (a test that fails when a bare drive path reaches `UserdataDir`), red-proof it, or correct the comment | CC |
| **R-1022** | Process & tooling | P3 | **Tester 1 follows the drill catalog and carries two test drives — teardown owed (operator ruling (e), 2026-10-14).** Steps, only on the operator's word: (1) remove the test apps (Sonarr, Radarr, qBittorrent, Transmission, Plex, Jellyfin) through the dashboard WITHOUT „delete data” (R-1027); (2) decommission Media A and Media B through Tester 1's dashboard; (3) `repoint.py restore` — `controller.yaml` from `controller.yaml.pre-media-2026-10-14`, read back byte-identical, `repo_url` the live catalog; remove `drill-catalog.git` from the controller volume; (4) on demo-hp: `qm set 341 --delete scsi2,scsi3`, then delete the two volumes BY NAME (`nvme-scratch:341/vm-341-disk-3.raw`, `-disk-4.raw`) — never `qm destroy`; (5) VM memory unchanged (8192, not raised); (6) drill repo reset to live `main`. Tools: `audits/media-all-drives-2026-10-14/``tools/`. **Added 2026-10-14 night:** (4b) Media C — `qm set 341 --delete scsi4`, then the volume `nvme-scratch:341/vm-341-disk-5.raw` by name (decommission it on the dashboard first; it holds 4 test files); (7) the throwaway Plex `plex-k6` on Tester 1 (`docker rm -f plex-k6`, `docker volume rm plex-k6-config`) after the morning claim step; (8) drill commit `925f3fd` (media_c binds) goes with step 6; Prowlarr and the test drive tag „driva-a” go with step 1. Tools also in `audits/media-drive-events-2026-10-14/tools/`. | **WAITING-ON-OPERATOR** — filed 2026-10-14 | the operator's teardown word | Run the six steps; quote each read-back | CC |
| **R-1022** | Process & tooling | P3 | **Tester 1 follows the drill catalog and carries two test drives — teardown owed (operator ruling (e), 2026-10-14).** Steps, only on the operator's word: (1) remove the test apps (Sonarr, Radarr, qBittorrent, Transmission, Plex, Jellyfin) through the dashboard WITHOUT „delete data” (R-1027); (2) decommission Media A and Media B through Tester 1's dashboard; (3) `repoint.py restore` — `controller.yaml` from `controller.yaml.pre-media-2026-10-14`, read back byte-identical, `repo_url` the live catalog; remove `drill-catalog.git` from the controller volume; (4) on demo-hp: `qm set 341 --delete scsi2,scsi3`, then delete the two volumes BY NAME (`nvme-scratch:341/vm-341-disk-3.raw`, `-disk-4.raw`) — never `qm destroy`; (5) VM memory unchanged (8192, not raised); (6) drill repo reset to live `main`. Tools: `audits/media-all-drives-2026-10-14/``tools/`. **Added 2026-10-14 night:** (4b) Media C — `qm set 341 --delete scsi4`, then the volume `nvme-scratch:341/vm-341-disk-5.raw` by name (decommission it on the dashboard first; it holds 4 test files); (7) DONE 2026-10-15: `plex-k6` and its volume removed after the claim (0 left) — the operator removes its server entry „plex-k6” from plex.tv (Settings → Authorized Devices); (8) drill commit `925f3fd` (media_c binds) goes with step 6; Prowlarr and the test drive tag „driva-a” go with step 1. Tools also in `audits/media-drive-events-2026-10-14/tools/`. | **WAITING-ON-OPERATOR** — filed 2026-10-14 | the operator's teardown word | Run the six steps; quote each read-back | CC |
| **R-1034** | Process & tooling | P3 | **Gate 17 checks the FAQ JSON-LD on two pages only, by a hard-coded list — a FAQPage anywhere else is unchecked.** `scripts/site_gates.py` gate 17 loops `for p in ("gyik.html", os.path.join("en", "faq.html"))`, so its „JSON-LD equals the visible questions and answers” guarantee stops at those two files. **Since 2026-10-14 two more pages carry a FAQPage block the gate never reads:** `/sajat-felho-tarhely` and `/en/own-cloud-storage`. Their JSON-LD was *derived* from the visible headings and paragraphs by a script and then verified verbatim against the rendered text, so it is correct today — but nothing stops the next copy edit moving the visible text and leaving the structured data behind, which is exactly the failure gate 17 exists to catch, and the one Google penalises. **Why it is not a one-line fix:** gate 17's extractor is written for the FAQ pages' accordion markup (`<button class="faq-question">` … `<div class="faq-answer-inner">`); the new pages use plain `<h2>` + `<h3>`/`<p>` sections, so widening the scope needs a second extractor beside the first. **Scope is a fact** (repo `CLAUDE.md`: prefer a walk over a hand-maintained list) — the gate should scan **every page containing a FAQPage block**, not a named pair, or the next page to carry one is unguarded in the same way. A decoy is owed for the widened gate (R-421). | **OPEN** | — | widen gate 17 to every page carrying a FAQPage block, second extractor + decoy | CC |
| **R-987** | Process & tooling | P4 | **The 53 catalog app images have never been scanned for known vulnerabilities.** Fix: a monthly trivy pass over the images the catalog pins, reported, not gating. | OPEN | — | Add to the monthly re-test | CC |
| **R-986** | Process & tooling | P3 | **No dependency or secret scanner runs on any push, so a known-vulnerable library or a committed key is found only by a review like this one.** Proposed gate: govulncheck (called vulns only), gitleaks on the diff, trivy on our own images; start as WARN with an accepted-hits file, then FAIL. Tools and pinned versions: `audits/security-review-2026-10-12/SCANNERS.md`. Note: gitleaks' default rules did not find R-908's key. | OPEN | — | Build the gate, warn-first | CC |