D39 = B: 09 §3 decision 255 (the security-fix step) + a dated note on decision 13; capability B23; R-1239 closed, R-1240 opened, R-1238 updated (register 186 -> 186); STATUS/CONTEXT; nodes.md (9202 password, Tester 1 DHCP); evidence security-lane-2026-10-23 (controller 0.341.0, global floor 11:24 UTC)
gates / gates (push) Successful in 13m49s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
2026-10-23 13:35:38 +02:00
parent 4b9b0af3cb
commit 87dc50743c
37 changed files with 1104 additions and 8 deletions
@@ -124,6 +124,7 @@
| <a id="b20"></a>[B20](capability-map-detail/B.md#b20) | App crashes → customer notified (one event per transition, no flapping spam) | controller v0.120, hub v0.48 | **IMPLEMENTED** | crash→customer mail never live-confirmed | `audits/CAMPAIGN-3-2026-07-11.md` (2026-07-11) |
| <a id="b21"></a>[B21](capability-map-detail/B.md#b21) | Post-deploy optional config (API keys etc.) with restart | controller, catalog `.felhom.yml` | **IMPLEMENTED** | config-save + restart in no campaign | `tests/CAMPAIGN-2-2026-07-07.md` (2026-07-07) |
| <a id="b22"></a>[B22](capability-map-detail/B.md#b22) | Backup classification: 13 bind-bearing apps carry mandatory/optional/excluded classes | catalog, controller v0.132–133 | **PROVEN-LIVE** | — | `audits/SPIKE-backup-classification-2026-07-14.md` (2026-07-14); `audits/CAMPAIGN-6D-2026-07-15.md` (2026-07-15) |
| <a id="b23"></a>[B23](capability-map-detail/B.md#b23) | An urgent security fix reaches the boxes at night — a `security_fix` ladder step (box walk required, bench optional, a CVE/GHSA notice), taken by the night update (`09` §3 decision 255, D39 = B) | catalog `3898593`, controller v0.341.0 | **PROVEN-LIVE** | scratch 9202 (drill catalog), Plex 1.43.4 → 1.43.5; an app with no box fixture is checked by health, version and a soak only (`seed: none`) | `audits/security-lane-2026-10-23/` (2026-10-23) |
## C. Protection & recovery (the product promise)
@@ -286,6 +286,9 @@ builds them (`audits/update-rulings-2026-09-23/`).
update night measured the proxy wrong in both directions — minor moves that held
(adventurelog, outline) and a tag shape it cannot read at all (§3b Q3). The test record is the
thing the proxy was guessing at.
**Dated note 2026-10-23 (decision 255, D39 = B):** a second step kind now exists — the `security_fix` step, proven
on the box with the bench optional, for an urgent upstream fix with a public notice. Decision 13's rule is
unchanged for every other move: a step a box takes was still tested, on at least the box venue.
14. **The update ladder** (Q3). A box more than one step behind **climbs one tested step at a time, in
order, and never jumps**. Each step is the full guarded update of §6.1. **Why:** a step was tested
@@ -1031,6 +1034,52 @@ its length, and both fixes cost something the household would notice — operato
Built in controller v0.314.0 (`internal/stacks/restore_gate.go`); pinned by `restore_gate_test.go` and
`r1057_unit_gate_test.go` (ten mutations each seen failing). Proof: `audits/gate-restore-2026-10-15/9202/`.
### 2026-10-23 (~12:00) — D39 = B: the security-fix step (operator ruling by starting the run; R-1239)
255. **An urgent upstream security fix reaches the boxes at night as a `security_fix` ladder step: the box walk is
required, the bench is optional, and a public notice is the authority.** *Operator ruling 2026-10-23 ~12:00 (D39 =
B, R-1239), taken by starting the D39 brief; its design is Claude.ai's recommendation, taken with it.* Why: on
2026-10-23 the only route for the Plex2Shell fix was an operator word, `git push --no-verify` past the move gate,
and a hand press on each box — and the night leg never takes a step with no test record (R-1238). That does not
scale past the first customers and needs the operator awake.
1. **The entry** (`scripts/ladder.py` `check_security`, the one reader): `"verdict": "security_fix"` with `security`
`{ids: [CVE-…|GHSA-…], advisory: "https://…", fixed_in}`, `box_evidence` (required), `seed` `read_back`|`none`,
`memory_peak_pct` from the BOX soak with `memory_basis: "box_soak"`; `evidence` = the bench record or null — a
bench given must be `proven` (`bench_verdict`, `harness_version` ≥ 2 ride with it). Never `backfilled`,
`digest_from` (a same-tag fix stays decision 52's lane), `engine_conversion`, nor a database engine MAJOR (any
engine — MariaDB included; the judgement is `check-engine-major.py`'s own `engine_of`/`major_of`, imported).
2. **No operator word per incident.** The session that writes the step reports it to the operator after the push.
3. **The gates:** the move gate accepts a `security_fix` move exactly as a proven one (from/to per service, digests
the registry still serves); `check-test-record.py` WARNs on every push while a template's head is a security-fix
step with no bench test („<app>: security-fix step <to> has no bench test (since <tested_at>)"). Seven decoys
(`test_gate_decoys.py`), each seen failing with the rules switched off.
4. **The box venue:** `box_walk.py --security` on 9202 through the product's Update; verdict `proven` only if the
update ended `done` with no undo and no hold, the app runs, the four version observables are TO, the seed (if
any) reads back, and a soak (default 600 s, cgroup `anon` against the limit, OOM kills and restarts counted)
sees no kill and no restart. A negative control (a TO image that exits at once) comes back `failed`.
5. **The writer:** `upgrade-test.py --write-ladder --security …`, refusing a box walk not proven, a bench given and
not proven, a template not at the walk's FROM, a digest that moved since the walk, an engine major, a missing
field. The compose move, `catalog_since` and the superseded step's own files are the shared `apply_entry`.
6. **The controller (v0.341.0):** the leg takes `proven` OR `security_fix`; every other guard runs unchanged, in
the same order; it logs `[update-leg] <app>: security fix <ids> taken (<from> → <to>)` and the hub report's
step carries the ids. A person's press and the badge are unchanged. **A controller ≤ 0.340.0 skips the step at
night (`not_proven`) and a hand press takes it** — proven on 9202, below.
7. **The trade, stated.** A security-fix step skipped the bench. For an app with a box fixture the data is still
read back after the update; for an app without one (Plex today) the step is checked only by „it starts, it is
healthy, it runs the new version, and a 10-minute soak sees no kill or restart". `files_may_change` is a bench
measurement: with no bench the step carries `false`. That is accepted because the leg's own guard is not the only
net — the update takes a per-app backup first, the undo puts the pre-update data back on a failed health check
(decision 15), and a security fix that rewrites files without breaking the health check is the case the bench
would catch and this lane accepts the risk of. A session that knows a fix migrates files runs the bench.
**Proof (9202, drill catalog, 2026-10-23):** `audits/security-lane-2026-10-23/` — the negative control (TO
`busybox:1.36.1`) came back `failed` (the box undid it after 335 s) and the writer refused it; Plex 1.43.4.10903 →
1.43.5.11029 `proven` (done in 36.9 s, the four observables at TO, digest `sha256:01bdf0ad…5c5f`, soak 600 s: anon
peak 10.7 % of 2 GiB, 0 kills, 0 restarts, `seed: none`); the writer wrote the drill step (CVE-2026-96652, -96654,
-96655, -96656; Plex's own notice as CISA KEV cites it, added 2026-10-20; fixed in 1.43.3.10861); on 0.340.0 the
night leg logged `plex: skipped — not_proven` and a hand press took the step; on 0.341.0-rc.1 the night leg logged
`plex: security fix CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken` and ended `done` in 30.1 s
after the update's second-drive copy and undo copy. Released as controller 0.341.0, global floor 11:24 UTC.
### 2026-10-15 (07:48–08:20) — bake first; Plex through its own key; HSTS one day (recorded before the media build)
247. ~~**Bake first.** A controller version is RELEASED only when its golden is baked.~~ **REVERSED by decision 250,
@@ -379,3 +379,28 @@
**Gap / roadmap (as written):** Remaining ~39 apps are legacy-class by design (unit-only offsite)
---
## B23 — An urgent security fix reaches the boxes at night (the security-fix step)
*Map row: [B23](../00-capability-map.md#b23) · added 2026-10-23.*
**Scenario:** an app has a published hole (a CVE or GHSA id with its notice) and a fixed upstream version. CC walks the
step on scratch 9202 through the product's Update (`box_walk.py --security`: install at FROM, the guarded Update, the
four version observables at TO, a 600 s memory soak), and the only writer turns a proven walk into a `security_fix`
ladder step. Every box on controller >= 0.341.0 takes it at night with the usual guards (backup first, undo on a
failed health check, held / stopped / needs_person / W+5h respected).
**Components:** catalog `3898593` (`ladder.py check_security`, the two test-record gates, `upgrade-test.py --write-ladder
--security`, `box_walk.py --security`); controller v0.341.0 (`legCandidate`).
**Status:** **PROVEN-LIVE** on 9202 with the drill catalog, 2026-10-23.
**Evidence:** `audits/security-lane-2026-10-23/` — the negative control (TO `busybox:1.36.1`) `failed` and the writer
refused it; Plex 1.43.4.10903 → 1.43.5.11029 `proven`; on 0.340.0 the night leg logged `plex: skipped — not_proven` and
a hand press took the step; on 0.341.0-rc.1 the night leg logged `plex: security fix CVE-2026-96652,… taken` and ended
`done` after a second-drive copy and an undo copy.
**Gap:** an app with no box fixture (Plex today, R-1238) is checked only by health, version and the soak — no data is
read back. `files_may_change` is a bench measurement and reads `false` when no bench ran (decision 255 item 7).
---
@@ -0,0 +1,19 @@
{
"app": "plex",
"walk": "security",
"box_walk_version": 1,
"verdict": "inconclusive",
"venue": "box 9202 (drill catalog), the guarded Update",
"from": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"to": {
"plex": "busybox:1.36.1"
},
"why": "the install at FROM did not complete — nothing was tested",
"log": [
"12:26:15 [1] made the drive paths this app requires: ['/mnt/felhom-drives/scratch_hdd/userdata/plex']",
"12:26:15 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['PLEX_CLAIM', 'HDD_PATH']",
"12:26:15 [1] deploy -> 409 {'ok': False, 'error': 'Az alkalmazás adatainak helye egy meghajtó lehet, nem egy mappa a meghajtón belül. Válaszd ezt: "
]
}
@@ -0,0 +1,5 @@
12:26:15 [1] made the drive paths this app requires: ['/mnt/felhom-drives/scratch_hdd/userdata/plex']
12:26:15 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['PLEX_CLAIM', 'HDD_PATH']
12:26:15 [1] deploy -> 409 {'ok': False, 'error': 'Az alkalmazás adatainak helye egy meghajtó lehet, nem egy mappa a meghajtón belül. Válaszd ezt:
12:26:15 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/P2-negative/verdict.json
12:26:15 RESULT inconclusive — the install at FROM did not complete — nothing was tested
@@ -0,0 +1,188 @@
{
"app": "plex",
"walk": "security",
"box_walk_version": 1,
"verdict": "failed",
"venue": "box 9202 (drill catalog), the guarded Update",
"from": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"to": {
"plex": "busybox:1.36.1"
},
"observables_before": {
"pinned_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"installed_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"catalog_images": null,
"live_compose_image_lines": [
"image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"
],
"docker_inspect": [
"plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"
]
},
"setup_gate_before": {
"browser": {
"status": "302",
"redirect": "https://felhom.enkisfelhom.hu/__gate/start?…"
},
"script": {
"status": "401",
"redirect": null
}
},
"seed": "none",
"seed_why": "the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue",
"drill_commit": "577947198bff",
"sync_waited_s": 4.5,
"badge_before": {
"hu": [
{
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
"text": "Frissítés elérhető — ma"
}
],
"en": [
{
"title": "A newer version of this app is available. Select the Update button to start it.",
"text": "Update available — today"
}
]
},
"measured_at": "2026-10-23T10:27:24Z",
"update": {
"accepted": true,
"http": "202",
"phases": [
{
"t": 0.0,
"phase": "backing-up",
"label": "Biztonsági mentés készül a frissítés előtt…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 11.3,
"phase": "safety-dump",
"label": "Adatbázis pillanatkép…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 12.3,
"phase": "pulling",
"label": "Új verzió letöltése…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 14.4,
"phase": "copying",
"label": "Az adatok másolása a frissítés előtt…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 24.7,
"phase": "starting",
"label": "Indítás az új verzióval…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 25.7,
"phase": "verifying",
"label": "Működés ellenőrzése…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 327.2,
"phase": "undoing",
"label": "Visszaállítás az előző változatra…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 335.4,
"phase": "undone",
"label": "Visszaállítva az előző változatra",
"updating": false,
"error": null,
"hold": null
}
],
"duration_s": 335.4,
"final_phase": "undone",
"update_error": null,
"hold_reason": null,
"state": "running"
},
"observables_after": {
"pinned_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"installed_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"catalog_images": {
"plex": "busybox:1.36.1"
},
"live_compose_image_lines": [
"image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"
],
"docker_inspect": [
"plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"
]
},
"badge_after": {
"hu": [
{
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
"text": "Frissítés elérhető — ma"
}
],
"en": [
{
"title": "A newer version of this app is available. Select the Update button to start it.",
"text": "Update available — today"
}
]
},
"state_after": "running",
"hold_after": null,
"to_digests": {
"plex": "sha256:e0ab27395614a8e1a4fdf84c6bc60ac664915cfdde70c52d030c7728a1c48e14"
},
"why": "the update ended 'undone' (error None, hold None)",
"log": [
"12:26:46 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']",
"12:26:46 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}",
"12:27:06 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}",
"12:27:06 gate: plex is gated — passed as the household (cookie set)",
"12:27:10 [2] setup gate as installed: {'browser': {'status': '302', 'redirect': 'https://felhom.enkisfelhom.hu/__gate/start?…'}, 'script': {'status': '401', 'redirect': None}}",
"12:27:10 [3] seed: none — the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue",
"12:27:11 [5] drill commit 577947198bff: plex plexinc/pms-docker:1.43.4.10903-e5521bd8c -> busybox:1.36.1 (push rc=0)",
"12:27:19 [5] the box's drill mirror now at 57794719",
"12:27:24 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}",
"12:27:24 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None",
"12:27:35 + 11.3s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None",
"12:27:37 + 12.3s phase=pulling label=Új verzió letöltése… err=None hold=None",
"12:27:39 + 14.4s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None",
"12:27:49 + 24.7s phase=starting label=Indítás az új verzióval… err=None hold=None",
"12:27:50 + 25.7s phase=verifying label=Működés ellenőrzése… err=None hold=None",
"12:32:51 + 327.2s phase=undoing label=Visszaállítás az előző változatra… err=None hold=None",
"12:33:00 + 335.4s phase=undone label=Visszaállítva az előző változatra err=None hold=None"
]
}
@@ -0,0 +1,22 @@
12:26:46 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']
12:26:46 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
12:27:06 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}
12:27:06 gate: plex is gated — passed as the household (cookie set)
12:27:10 [2] setup gate as installed: {'browser': {'status': '302', 'redirect': 'https://felhom.enkisfelhom.hu/__gate/start?…'}, 'script': {'status': '401', 'redirect': None}}
12:27:10 [3] seed: none — the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue
12:27:11 [5] drill commit 577947198bff: plex plexinc/pms-docker:1.43.4.10903-e5521bd8c -> busybox:1.36.1 (push rc=0)
12:27:19 [5] the box's drill mirror now at 57794719
12:27:24 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
12:27:24 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
12:27:35 + 11.3s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
12:27:37 + 12.3s phase=pulling label=Új verzió letöltése… err=None hold=None
12:27:39 + 14.4s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
12:27:49 + 24.7s phase=starting label=Indítás az új verzióval… err=None hold=None
12:27:50 + 25.7s phase=verifying label=Működés ellenőrzése… err=None hold=None
12:32:51 + 327.2s phase=undoing label=Visszaállítás az előző változatra… err=None hold=None
12:33:00 + 335.4s phase=undone label=Visszaállítva az előző változatra err=None hold=None
12:33:18 [9] verdict failed -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/P2-negative/verdict.json
12:33:18 RESULT failed — the update ended 'undone' (error None, hold None)
12:33:27 [X] stop -> 200 {'ok': True, 'message': 'Stack plex stop completed'}
12:33:59 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'plex', 'volumes_removed': ['plex_plex_config', 'plex_plex_transcode'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'us
12:34:08 [X] after remove: deployed=False leftovers='/opt/docker/stacks/plex'
@@ -0,0 +1 @@
REFUSED plex: the box walk is 'security' with verdict 'failed' — only a PROVEN `box_walk.py --security` walk is written as a security-fix step
@@ -0,0 +1,9 @@
12:57:50 [5] drill commit f8b700e1d889: plex plexinc/pms-docker:1.43.5.11029-6bf8af3f0 -> plexinc/pms-docker:1.43.4.10903-e5521bd8c (push rc=0)
12:57:58 [5] the box's drill mirror now at f8b700e1
f8b700e1d889
13:00:27 [sync] the badge NEVER caught up to plexinc/pms-docker:1.43.4.10903-e5521bd8c in 148.8s — catalog_images = None
13:00:27 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']
13:00:27 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
13:00:47 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}
13:00:47 gate: plex is gated — passed as the household (cookie set)
deployed True {"pinned_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "catalog_images": null, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"], "docker_inspect": ["plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"]}
@@ -0,0 +1,15 @@
13:01:23 [5] the box's drill mirror now at f46e1bc6
before: {"pinned_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"], "docker_inspect": ["plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"]}
badge before: {'hu': [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}], 'en': [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]}
night chain -> 202 {'data': {'legs': ['db-dump', 'tier2', 'update-leg']}, 'message': 'started', 'ok': True}
the leg's own lines:
2026/10/23 11:01:34 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.6:46254: [db-dump tier2 update-leg]
2026/10/23 11:02:40 night_chain.go:77: [INFO] [night-chain] update-leg: started
2026/10/23 11:02:40 unattended.go:270: [INFO] [update-leg] started (manual-chain): window 02:30, no step starts at or after 16:17
2026/10/23 11:02:41 unattended.go:316: [INFO] [update-leg] plex: skipped — not_proven
2026/10/23 11:02:41 unattended.go:316: [INFO] [update-leg] privatebin: skipped — stopped_by_household
2026/10/23 11:02:41 unattended.go:248: [INFO] [update-leg] update leg (manual-chain): done=0 undone=0 held=0 failed=0 skipped=2 in 0s [skipped: plex=not_proven, privatebin=stopped_by_household]
2026/10/23 11:02:41 night_chain.go:82: [INFO] [night-chain] update-leg: done in 0s
after: {"pinned_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"], "docker_inspect": ["plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"]}
state running hold None phase None
@@ -0,0 +1,11 @@
13:03:07 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
13:03:07 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
13:03:08 + 1.0s phase=pulling label=Új verzió letöltése… err=None hold=None
13:03:16 + 9.2s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
13:03:26 + 19.5s phase=starting label=Indítás az új verzióval… err=None hold=None
13:03:27 + 20.5s phase=verifying label=Működés ellenőrzése… err=None hold=None
13:03:32 + 25.6s phase=done label=Frissítve err=None hold=None
{"accepted": true, "final_phase": "done", "update_error": null, "hold_reason": null, "state": "running", "duration_s": 25.7}
after: {"pinned_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.5.11029-6bf8af3f0@sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f"], "docker_inspect": ["plex plexinc/pms-docker:1.43.5.11029-6bf8af3f0@sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f running=true restarts=0"]}
13:03:51 gate: plex is gated — passed as the household (cookie set)
identity 200 {"MediaContainer":{"size":0,"apiVersion":"1.2.3","claimed":false,"machineIdentifier":"f34aaee0f637bb3bf168e5b4e5ce2223c7c40c5f","version":"1.43.5.11029-6bf8af3f0"}}
@@ -0,0 +1,227 @@
{
"app": "plex",
"walk": "security",
"box_walk_version": 1,
"verdict": "proven",
"venue": "box 9202 (drill catalog), the guarded Update",
"from": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"to": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"observables_before": {
"pinned_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"installed_images": {
"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"
},
"catalog_images": null,
"live_compose_image_lines": [
"image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"
],
"docker_inspect": [
"plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"
]
},
"setup_gate_before": {
"browser": {
"status": "302",
"redirect": "https://felhom.enkisfelhom.hu/__gate/start?…"
},
"script": {
"status": "401",
"redirect": null
}
},
"seed": "none",
"seed_why": "the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue",
"drill_commit": "b5e2406d3b49",
"sync_waited_s": 4.5,
"badge_before": {
"hu": [
{
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
"text": "Frissítés elérhető — ma"
}
],
"en": [
{
"title": "A newer version of this app is available. Select the Update button to start it.",
"text": "Update available — today"
}
]
},
"measured_at": "2026-10-23T10:36:22Z",
"update": {
"accepted": true,
"http": "202",
"phases": [
{
"t": 0.0,
"phase": "backing-up",
"label": "Biztonsági mentés készül a frissítés előtt…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 11.3,
"phase": "safety-dump",
"label": "Adatbázis pillanatkép…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 12.3,
"phase": "pulling",
"label": "Új verzió letöltése…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 20.5,
"phase": "copying",
"label": "Az adatok másolása a frissítés előtt…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 30.8,
"phase": "starting",
"label": "Indítás az új verzióval…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 31.8,
"phase": "verifying",
"label": "Működés ellenőrzése…",
"updating": true,
"error": null,
"hold": null
},
{
"t": 36.9,
"phase": "done",
"label": "Frissítve",
"updating": false,
"error": null,
"hold": null
}
],
"duration_s": 36.9,
"final_phase": "done",
"update_error": null,
"hold_reason": null,
"state": "running"
},
"observables_after": {
"pinned_images": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"installed_images": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"catalog_images": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"live_compose_image_lines": [
"image: plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
],
"docker_inspect": [
"plex plexinc/pms-docker:1.43.5.11029-6bf8af3f0 running=true restarts=0"
]
},
"badge_after": {
"hu": [
{
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
"text": "Naprakész"
}
],
"en": [
{
"title": "This app is running the newest version available.",
"text": "Up to date"
}
]
},
"state_after": "running",
"hold_after": null,
"to_digests": {
"plex": "sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f"
},
"four_observables": {
"pinned": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"installed": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"catalog": {
"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
},
"live_compose": [
"plexinc/pms-docker:1.43.5.11029-6bf8af3f0"
]
},
"setup_gate_after": {
"browser": {
"status": "302",
"redirect": "https://felhom.enkisfelhom.hu/__gate/start?…"
},
"script": {
"status": "401",
"redirect": null
}
},
"memory": {
"soak_s": 600,
"interval_s": 10,
"samples": 60,
"containers": {
"plex": {
"samples": 60,
"anon_peak_bytes": 229015552,
"limit_bytes": 2147483648,
"anon_peak_pct": 10.7,
"cgroup_peak_pct": 11.5,
"oom_kill_delta": 0,
"oom_killed_flag": false,
"restarts_delta": 0,
"running_all_samples": true,
"cgroup": "/sys/fs/cgroup/system.slice/docker-f699bdec12560064fa6bd64884d941915072974ca27d389bcb3e3b917861ac88.scope"
}
},
"oom_kills": 0,
"restarts": 0,
"peak_pct": 10.7
},
"why": "the security-fix step ran on the box through the guarded Update; healthy, TO on all four observables, no undo, no hold, a clean soak",
"log": [
"12:35:39 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']",
"12:35:40 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}",
"12:36:05 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}",
"12:36:05 gate: plex is gated — passed as the household (cookie set)",
"12:36:08 [2] setup gate as installed: {'browser': {'status': '302', 'redirect': 'https://felhom.enkisfelhom.hu/__gate/start?…'}, 'script': {'status': '401', 'redirect': None}}",
"12:36:08 [3] seed: none — the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue",
"12:36:09 [5] drill commit b5e2406d3b49: plex plexinc/pms-docker:1.43.4.10903-e5521bd8c -> plexinc/pms-docker:1.43.5.11029-6bf8af3f0 (push rc=0)",
"12:36:18 [5] the box's drill mirror now at b5e2406d",
"12:36:23 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}",
"12:36:23 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None",
"12:36:34 + 11.3s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None",
"12:36:35 + 12.3s phase=pulling label=Új verzió letöltése… err=None hold=None",
"12:36:43 + 20.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None",
"12:36:53 + 30.8s phase=starting label=Indítás az új verzióval… err=None hold=None",
"12:36:54 + 31.8s phase=verifying label=Működés ellenőrzése… err=None hold=None",
"12:36:59 + 36.9s phase=done label=Frissítve err=None hold=None",
"12:37:18 [7] memory soak 600 s …",
"12:47:24 [7] soak: peak 10.7% of the limit (anon), kills 0, restarts 0"
]
}
@@ -0,0 +1,23 @@
12:35:39 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']
12:35:40 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
12:36:05 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}
12:36:05 gate: plex is gated — passed as the household (cookie set)
12:36:08 [2] setup gate as installed: {'browser': {'status': '302', 'redirect': 'https://felhom.enkisfelhom.hu/__gate/start?…'}, 'script': {'status': '401', 'redirect': None}}
12:36:08 [3] seed: none — the first-run claim needs a token minted at plex.tv by a real Plex account; no account exists for this venue
12:36:09 [5] drill commit b5e2406d3b49: plex plexinc/pms-docker:1.43.4.10903-e5521bd8c -> plexinc/pms-docker:1.43.5.11029-6bf8af3f0 (push rc=0)
12:36:18 [5] the box's drill mirror now at b5e2406d
12:36:23 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
12:36:23 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
12:36:34 + 11.3s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
12:36:35 + 12.3s phase=pulling label=Új verzió letöltése… err=None hold=None
12:36:43 + 20.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
12:36:53 + 30.8s phase=starting label=Indítás az új verzióval… err=None hold=None
12:36:54 + 31.8s phase=verifying label=Működés ellenőrzése… err=None hold=None
12:36:59 + 36.9s phase=done label=Frissítve err=None hold=None
12:37:18 [7] memory soak 600 s …
12:47:24 [7] soak: peak 10.7% of the limit (anon), kills 0, restarts 0
12:47:24 [9] verdict proven -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/P2-walk/verdict.json
12:47:24 RESULT proven — the security-fix step ran on the box through the guarded Update; healthy, TO on all four observables, no undo, no hold, a clean soak
12:47:33 [X] stop -> 200 {'ok': True, 'message': 'Stack plex stop completed'}
12:48:05 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'plex', 'volumes_removed': ['plex_plex_config', 'plex_plex_transcode'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'us
12:48:13 [X] after remove: deployed=False leftovers='/opt/docker/stacks/plex'
@@ -0,0 +1 @@
WROTE plex: {'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'} -> {'plex': 'plexinc/pms-docker:1.43.5.11029-6bf8af3f0'} peak 10.7% marks {'files_may_change': False, 'needs_person': None, 'memory_tight': False}
@@ -0,0 +1,21 @@
13:04:15 [X] stop -> 200 {'ok': True, 'message': 'Stack plex stop completed'}
13:04:47 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'plex', 'volumes_removed': ['plex_plex_config', 'plex_plex_transcode'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'us
13:04:56 [X] after remove: deployed=False leftovers='/opt/docker/stacks/plex'
remote: . Processing 1 references
remote: Processed 1 references in total
13:05:05 [5] the box's drill mirror now at 42945c94
13:07:34 [sync] the badge NEVER caught up to plexinc/pms-docker:1.43.4.10903-e5521bd8c in 149.0s — catalog_images = None
13:07:34 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH', 'PLEX_CLAIM']
13:07:34 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
13:07:54 [1] deployed, controller state=running, pinned={'plex': 'plexinc/pms-docker:1.43.4.10903-e5521bd8c'}
deploy True
13:07:55 gate: plex is gated — passed as the household (cookie set)
remote: . Processing 1 references
remote: Processed 1 references in total
17e0da0 Revert "DRILL plex: compose image line back to 1.43.4 ONLY (the ladder untouched) — a fresh install at FROM for the Part 3 proof; reverted right after"
42945c9 DRILL plex: compose image line back to 1.43.4 ONLY (the ladder untouched) — a fresh install at FROM for the Part 3 proof; reverted right after
f46e1bc DRILL plex: security_fix step 1.43.4 -> 1.43.5 written by upgrade-test.py --write-ladder --security (D39 = B proof)
13:08:04 [5] the box's drill mirror now at 17e0da02
obs {"pinned_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"], "docker_inspect": ["plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"]}
badge {'hu': [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}], 'en': [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]}
@@ -0,0 +1,17 @@
13:12:03 [5] the box's drill mirror now at 17e0da02
before: {"pinned_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.4.10903-e5521bd8c"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.4.10903-e5521bd8c"], "docker_inspect": ["plex plexinc/pms-docker:1.43.4.10903-e5521bd8c running=true restarts=0"]}
badge before: {'hu': [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}], 'en': [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]}
night chain -> 202 {'data': {'legs': ['db-dump', 'tier2', 'update-leg']}, 'message': 'started', 'ok': True}
the leg's own lines:
2026/10/23 11:12:13 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.6:38520: [db-dump tier2 update-leg]
2026/10/23 11:13:15 night_chain.go:77: [INFO] [night-chain] update-leg: started
2026/10/23 11:13:15 unattended.go:279: [INFO] [update-leg] started (manual-chain): window 02:30, no step starts at or after 16:28
2026/10/23 11:13:15 unattended.go:345: [INFO] [update-leg] plex: step pressed plex=plexinc/pms-docker:1.43.4.10903-e5521bd8c → plex=plexinc/pms-docker:1.43.5.11029-6bf8af3f0
2026/10/23 11:13:15 unattended.go:351: [INFO] [update-leg] plex: security fix CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken (plex=plexinc/pms-docker:1.43.4.10903-e5521bd8c → plex=plexinc/pms-docker:1.43.5.11029-6bf8af3f0)
2026/10/23 11:13:45 unattended.go:359: [INFO] [update-leg] plex: step ended done after 30.1 s
2026/10/23 11:13:45 unattended.go:325: [INFO] [update-leg] privatebin: skipped — stopped_by_household
2026/10/23 11:13:45 unattended.go:257: [INFO] [update-leg] update leg (manual-chain): done=1 undone=0 held=0 failed=0 skipped=1 in 30s [skipped: privatebin=stopped_by_household] [security fix plex: CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 done]
2026/10/23 11:13:45 night_chain.go:82: [INFO] [night-chain] update-leg: done in 30s
after: {"pinned_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "installed_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "catalog_images": {"plex": "plexinc/pms-docker:1.43.5.11029-6bf8af3f0"}, "live_compose_image_lines": ["image: plexinc/pms-docker:1.43.5.11029-6bf8af3f0@sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f"], "docker_inspect": ["plex plexinc/pms-docker:1.43.5.11029-6bf8af3f0@sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f running=true restarts=0"]}
state running hold None phase done
@@ -0,0 +1,28 @@
2026/10/23 11:13:15 tier2.go:507: [INFO] [backup] Tier 2 copied plex → /mnt/sizing-bench/backups/secondary/plex (2.3 MB, 0 leg(s), 0s)
2026/10/23 11:13:15 update.go:537: [INFO] [stacks] update plex: accepted — guarded update started
2026/10/23 11:13:15 unattended.go:345: [INFO] [update-leg] plex: step pressed plex=plexinc/pms-docker:1.43.4.10903-e5521bd8c → plex=plexinc/pms-docker:1.43.5.11029-6bf8af3f0
2026/10/23 11:13:15 unattended.go:351: [INFO] [update-leg] plex: security fix CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken (plex=plexinc/pms-docker:1.43.4.10903-e5521bd8c → plex=plexinc/pms-docker:1.43.5.11029-6bf8af3f0)
2026/10/23 11:13:15 update.go:1378: [INFO] [stacks] update plex: phase checking
2026/10/23 11:13:15 update.go:795: [INFO] [stacks] update plex: ladder — the last step (1 of 1) — the catalog's current definition
2026/10/23 11:13:15 update.go:823: [INFO] [stacks] update plex: precondition met — Tier 2 (second drive) copy from 2026-10-23T11:13:04Z (0s old, limit 24h0m0s)
2026/10/23 11:13:15 update.go:1378: [INFO] [stacks] update plex: phase safety-dump
2026/10/23 11:13:16 update_guard.go:497: [INFO] [backup] update safety dump for plex: the app has no database — nothing to copy (no-op)
2026/10/23 11:13:16 update.go:855: [INFO] [stacks] update plex: safety dump done (0 file(s)) []
2026/10/23 11:13:17 undo.go:287: [INFO] [stacks] update plex: the undo copy will hold 2 named volume(s), 2.8 MiB
2026/10/23 11:13:17 update.go:1378: [INFO] [stacks] update plex: phase pinning
2026/10/23 11:13:17 pin.go:385: [INFO] [stacks] update plex: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/plex/docker-compose.yml (plex=plexinc/pms-docker:1.43.5.11029-6bf8af3f0)
2026/10/23 11:13:17 update.go:1378: [INFO] [stacks] update plex: phase pulling
2026/10/23 11:13:28 update.go:1378: [INFO] [stacks] update plex: phase copying
2026/10/23 11:13:38 update.go:1378: [INFO] [stacks] update plex: phase copying
2026/10/23 11:13:38 undo.go:437: [INFO] [stacks] update plex: copied plex_plex_config → plex_plex_config.pre-update-20261023T111338Z in 520ms
2026/10/23 11:13:38 update.go:1378: [INFO] [stacks] update plex: phase copying
2026/10/23 11:13:39 undo.go:437: [INFO] [stacks] update plex: copied plex_plex_transcode → plex_plex_transcode.pre-update-20261023T111338Z in 465ms
2026/10/23 11:13:39 update.go:1378: [INFO] [stacks] update plex: phase starting
2026/10/23 11:13:39 update.go:1378: [INFO] [stacks] update plex: phase verifying
2026/10/23 11:13:44 update.go:1006: [INFO] [stacks] update plex: healthy after 5s (the app's health check passed)
2026/10/23 11:13:45 update.go:1043: [INFO] [stacks] update plex: DONE in 29s
2026/10/23 11:13:45 image_retention.go:238: [INFO] [stacks] image retention (update of plex): pass over 40 image(s) of [plexinc/pms-docker] — 0 candidate(s), 0 deleted, the rest kept
2026/10/23 11:13:45 unattended.go:359: [INFO] [update-leg] plex: step ended done after 30.1 s
2026/10/23 11:13:45 unattended.go:257: [INFO] [update-leg] update leg (manual-chain): done=1 undone=0 held=0 failed=0 skipped=1 in 30s [skipped: privatebin=stopped_by_household] [security fix plex: CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 do
identity 200 {"MediaContainer":{"size":0,"apiVersion":"1.2.3","claimed":false,"machineIdentifier":"de0d1cd3ffa436835af296505a1e2534be391d1f","version":"1.43.5.11029-6bf8af3f
state running health None
@@ -0,0 +1,27 @@
### root@192.168.0.154
containers=10 unhealthy=0 restarting=0 exited=[] gitea.dooplex.hu/admin/felhom-controller:0.341.0 Up 35 seconds (healthy)
started=2026-10-23T11:15:27.242236608Z
errors=0 info=126
2026/10/23 11:15:28 [INFO] Self-update enabled (check every 6h, auto-update: false, auto-update time: 04:30)
2026/10/23 11:15:28 [INFO] [scheduler] Registered periodic job: selfupdate-check (every 6h0m0s)
2026/10/23 11:15:59 [INFO] [selfupdate] Current version 0.341.0 is up to date
2026/10/23 11:15:29 [INFO] [sync] Initial sync: Sablonok frissítve — a katalógus új változata betöltve; a telepített alkalmazások fájljai nem változt
### hp
containers=24 unhealthy=0 restarting=0 exited=[] gitea.dooplex.hu/admin/felhom-controller:0.341.0 Up 36 seconds (healthy)
started=2026-10-23T11:15:28.047379649Z
errors=0 info=135
2026/10/23 11:15:29 deploy.go:1442: [WARN] [stacks] Stack bookstack: new field ADMIN_PASSWORD (type=password) requires manual configuration
2026/10/23 11:15:29 deploy.go:1442: [WARN] [stacks] Stack calibre-web: new field ADMIN_PASSWORD (type=password) requires manual configuration
2026/10/23 11:15:29 deploy.go:1442: [WARN] [stacks] Stack plex: new field PLEX_CLAIM (type=text) requires manual configuration
2026/10/23 11:15:29 main.go:833: [INFO] Self-update enabled (check every 6h, auto-update: false, auto-update time: 04:30)
2026/10/23 11:15:29 scheduler.go:120: [INFO] [scheduler] Registered periodic job: selfupdate-check (every 6h0m0s)
2026/10/23 11:16:01 updater.go:196: [INFO] [selfupdate] Current version 0.341.0 is up to date
2026/10/23 11:15:30 sync.go:142: [INFO] [sync] Initial sync: Sablonok frissítve — a katalógus új változata betöltve; a telepített alkalmazások fájljai
### felhom-pve
containers=9 unhealthy=0 restarting=0 exited=[] gitea.dooplex.hu/admin/felhom-controller:0.341.0 Up 40 seconds (healthy)
started=2026-10-23T11:15:11.64495822Z
errors=0 info=141
2026/10/23 11:15:12 [INFO] Self-update enabled (check every 6h, auto-update: false, auto-update time: 04:30)
2026/10/23 11:15:12 [INFO] [scheduler] Registered periodic job: selfupdate-check (every 6h0m0s)
2026/10/23 11:15:44 [INFO] [selfupdate] Current version 0.341.0 is up to date
2026/10/23 11:15:13 [INFO] [sync] Initial sync: Sablonok frissítve — a katalógus új változata betöltve; a telepített alkalmazások fájljai nem változt
@@ -0,0 +1,10 @@
2026-10-23T11:24:47Z
HTTP/1.1 303 See Other
Location: /configuration?flash=floor_set
Effective floor: v0.341.0 — source: DB (hub_settings) ; env fallback would be v0.120.0 Save global floor… DB override: v
clear tester-1 Location: /customers/tester-1?flash=floor_set
clear demo-hp Location: /customers/demo-hp?flash=floor_set
clear demo-felhom Location: /customers/demo-felhom?flash=floor_set
2026/10/23 13:15:19 [INFO] managed floor SERVED for tester-1: floor 0.341.0, agent requirement "0.158.1" from declared (golden 0.331.0)
2026/10/23 13:15:20 [INFO] managed floor SERVED for demo-felhom: floor 0.341.0, agent requirement "0.158.1" from declared (golden 0.331.0)
2026/10/23 13:15:20 [INFO] managed floor SERVED for demo-hp: floor 0.341.0, agent requirement "0.158.1" from declared (golden 0.331.0)
@@ -0,0 +1,33 @@
control NEGATIVE (a stub answering 200 over plain http): convicted, as it must (200 -)
== tester-1 (enkicsifelhom.hu): 6 public name(s)
OK control POSITIVE http://felhom.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkicsifelhom.hu/r1033-check?q=1
OK http://files.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://files.enkicsifelhom.hu/r1033-check?q=1
OK http://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkicsifelhom.hu/r1033-check?q=1
OK http://paperless.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paperless.enkicsifelhom.hu/r1033-check?q=1
OK http://paste.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://paste.enkicsifelhom.hu/r1033-check?q=1
OK http://wiki.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkicsifelhom.hu/r1033-check?q=1
== demo-hp (enkisfelhom.hu): 14 public name(s)
OK http://arcade.enkisfelhom.hu/r1033-check?q=1 -> 301 https://arcade.enkisfelhom.hu/r1033-check?q=1
OK http://books.enkisfelhom.hu/r1033-check?q=1 -> 301 https://books.enkisfelhom.hu/r1033-check?q=1
OK http://docs.enkisfelhom.hu/r1033-check?q=1 -> 301 https://docs.enkisfelhom.hu/r1033-check?q=1
OK http://felhom.enkisfelhom.hu/r1033-check?q=1 -> 301 https://felhom.enkisfelhom.hu/r1033-check?q=1
OK http://files.enkisfelhom.hu/r1033-check?q=1 -> 301 https://files.enkisfelhom.hu/r1033-check?q=1
OK http://gist.enkisfelhom.hu/r1033-check?q=1 -> 301 https://gist.enkisfelhom.hu/r1033-check?q=1
OK http://ha.enkisfelhom.hu/r1033-check?q=1 -> 301 https://ha.enkisfelhom.hu/r1033-check?q=1
OK http://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1 -> 301 https://no-such-name-r1033.enkisfelhom.hu/r1033-check?q=1
OK http://papir.enkisfelhom.hu/r1033-check?q=1 -> 301 https://papir.enkisfelhom.hu/r1033-check?q=1
OK http://paste.enkisfelhom.hu/r1033-check?q=1 -> 301 https://paste.enkisfelhom.hu/r1033-check?q=1
OK http://pdf.enkisfelhom.hu/r1033-check?q=1 -> 301 https://pdf.enkisfelhom.hu/r1033-check?q=1
OK http://plex.enkisfelhom.hu/r1033-check?q=1 -> 301 https://plex.enkisfelhom.hu/r1033-check?q=1
OK http://travel.enkisfelhom.hu/r1033-check?q=1 -> 301 https://travel.enkisfelhom.hu/r1033-check?q=1
OK http://wiki.enkisfelhom.hu/r1033-check?q=1 -> 301 https://wiki.enkisfelhom.hu/r1033-check?q=1
== demo-felhom (demo-felhom.eu): 8 public name(s)
OK http://felhom.demo-felhom.eu/r1033-check?q=1 -> 301 https://felhom.demo-felhom.eu/r1033-check?q=1
OK http://files.demo-felhom.eu/r1033-check?q=1 -> 301 https://files.demo-felhom.eu/r1033-check?q=1
OK http://gist.demo-felhom.eu/r1033-check?q=1 -> 301 https://gist.demo-felhom.eu/r1033-check?q=1
OK http://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1 -> 301 https://no-such-name-r1033.demo-felhom.eu/r1033-check?q=1
OK http://prowlarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://prowlarr.demo-felhom.eu/r1033-check?q=1
OK http://radarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://radarr.demo-felhom.eu/r1033-check?q=1
OK http://sonarr.demo-felhom.eu/r1033-check?q=1 -> 301 https://sonarr.demo-felhom.eu/r1033-check?q=1
OK http://torrent.demo-felhom.eu/r1033-check?q=1 -> 301 https://torrent.demo-felhom.eu/r1033-check?q=1
== every public name redirects http -> https
@@ -0,0 +1,3 @@
9201 login ok, /api/stacks 200
login failed: no session cookie
n100 /api/health 200tester-1 (BASE .148) login ok, /api/stacks 200
@@ -0,0 +1,3 @@
tester-1 HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
demo-hp HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
demo-felhom HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
@@ -0,0 +1,74 @@
# D39 = B — the security-fix lane, built and proven (2026-10-23)
Decision: `architecture/09-update-architecture.md` §3 decision 255 (the operator started the D39 brief ~12:00; R-1239
closed). Read before the work: `09` §3 decisions 13–17, 22, 25–26, 52 and §6.4 parts 4–7; `00-capability-map.md` (the
B rows); `13-threat-model.md`; `runbooks/target-selection.md`; `runbooks/publish-train-rules.md`.
Baselines at start: catalog `cde26ab`, controller `32e61d5` (0.340.0, global floor 06:41 UTC — read from the hub before
the first controller commit), felhom.eu `4b9b0af3`. Machines: DooPlex (builds, the writer), demo-hp (9202 scratch; the
release on 9201), Tester 1, demo-felhom (release only). Not touched: ep0, any household box.
## Venue
9202 was pointed at the drill catalog through its LOCAL mirror (`tools/repoint.py`, copied from
`audits/burndown-3-2026-10-17/tools/`, R-992 — no catalog credential on the box): `repoint-drill.txt`. The drill was
reset to live `main` (`cde26ab`) first. 9202's dashboard password is its own (`REHEARSAL_2026_10_20_9202_PW`; the
demo-hp one was refused — five failed logins hit the one-minute limit; `operations/nodes.md` corrected).
## Part 2 — the lane on the old controller (9202 on 0.340.0)
| Step | Result | File |
|---|---|---|
| First try of the negative control | `inconclusive` — the install was refused before anything was made (Plex wants the drive itself as `HDD_PATH`); the walk gained `--value` | `P2-negative-try1-install-refused/` |
| **Negative control**: FROM Plex 1.43.4.10903, TO `busybox:1.36.1` (exits at once) | **`failed`** — backing-up → … → verifying → undoing → **undone** after 335 s; Plex 1.43.4 back | `P2-negative/walk.txt`, `box-verdict-plex.json` |
| The writer on that walk | **refused**: „the box walk is 'security' with verdict 'failed' — only a PROVEN `box_walk.py --security` walk is written" | `P2-negative/writer-refusal.txt` |
| **The walk**: Plex 1.43.4.10903-e5521bd8c → 1.43.5.11029-6bf8af3f0 | **`proven`** — done in 36.9 s, no undo, no hold; pinned = installed = catalog = live compose = running = TO; digest `sha256:01bdf0ad6d53198da4331540c1e73ab65db7a72e961fff97ecb0847d46055c5f` (the registry's, re-read by the writer); soak 600 s, 60 samples, anon peak 10.7 % of 2 GiB (cgroup peak 11.5 %), 0 OOM kills, 0 restarts; `seed: none` (Plex's fixture is a `NoRoute` stub) | `P2-walk/` |
| Setup gate (unclaimed Plex) | a browser (`Accept: text/html`) → 302 to `felhom.<domain>/__gate/start`; a script → 401; the same after the update | `P2-walk/box-verdict-plex.json` |
| The writer, into the DRILL only | wrote the `security_fix` entry: ids CVE-2026-96652/-96654/-96655/-96656; advisory Plex's notice `https://forums.plex.tv/t/important-security-update-for-plex-media-server-v1-43-2-and-earlier/942319` (the one CISA KEV cites, all four added 2026-10-20 — `cisa-kev-plex2shell.json`, from CISA's own feed); `fixed_in` 1.43.3.10861; the gate accepts it with its WARN | `P2-walk/writer.txt`; drill `f46e1bc` |
| Old controller, night (`POST /api/debug/backup/night-chain`) | **`[update-leg] plex: skipped — not_proven`**; Plex stays 1.43.4 (control: the same leg judged privatebin) | `P2-oldctl/2-night-chain-0.340.0.txt` |
| Old controller, hand press | **done** in 25.7 s; Plex 1.43.5 at the step's digest; `/identity` 200 with 1.43.5 | `P2-oldctl/3-hand-press-0.340.0.txt` |
**How Plex was put back at 1.43.4 for Part 3:** removed through the product (with its data); drill commit `42945c9`
moved ONLY the compose's image line back to 1.43.4 (the ladder line untouched); a fresh install pinned 1.43.4; `17e0da0`
reverted that commit, so the step is the head again and the badge read „Frissítés elérhető" (`P3-newctl/0-…`).
## Part 3 — the new controller (0.341.0-rc.1 on 9202)
Controller `63dcccf` (CI run 2264 / #471 success), image `0.341.0-rc.1` set by hand (9202's self-update is off). The
night chain: `[update-leg] plex: step pressed …`, **`[update-leg] plex: security fix
CVE-2026-96652,CVE-2026-96654,CVE-2026-96655,CVE-2026-96656 taken (plex=…1.43.4… → plex=…1.43.5…)`**, `step ended done
after 30.1 s`; the summary line ends `[security fix plex: … done]`. Backup first: the update's precondition met by a
second-drive copy 0 s old, the undo copy of 2 named volumes (2.8 MiB), then the pin. `/identity` 200 with 1.43.5
(`P3-newctl/1-…`, `2-…`).
## Part 4 — release 0.341.0
Image `0.341.0` (`sha256:8aa4ce60…c0bc`) from `63dcccf`; per-box floors on tester-1, demo-hp, demo-felhom (303
`floor_set`); all three ran 0.341.0 by 11:15:31 UTC. Box walk: 0 unhealthy, 0 restarting, 0 exited, 0 `[ERROR]` lines since
the start (126 / 135 / 141 `[INFO]` lines — the control); selfupdate „0.341.0 is up to date"; the https-only check passes
on every public name; the dashboard login works on demo-hp and Tester 1 (`/api/stacks` 200), the N100's `/api/health`
200 (`P4-release/`). **The operator said yes in chat; the global floor 0.341.0 / MinAgent 0.158.1 was set at 11:24:47
UTC** (303 `floor_set`; the running hub's page reads „Effective floor: v0.341.0 — source: DB"); the per-box floors
cleared.
## Teardown (three layers)
- **Machine (9202):** Plex removed through the product (no plex container or volume left); `controller.yaml` restored
byte-identical (`cmp`) and the catalog cache re-cloned from the live catalog (R-615); this run's saved copy deleted
after the `cmp`; image `0.341.0`. 34 older saved copies from earlier sessions stay — filed R-1240. The drill
repository keeps this run's commits as the record.
- **Host (demo-hp):** nothing provisioned.
- **Hub:** the global floor 0.341.0 (the operator's yes); per-box floors cleared.
## Observations
- FILED: R-1240 — 9202 holds 34 saved `controller.yaml` copies with the catalog token.
- FIXED without a row: drill commits never reached a box pointed at the drill's local mirror (`box_walk.refresh_box_mirror`);
two stale catalog tests (9202 address, gate count + missing sibling in the shallow clone); Tester 1's guest moved
`.101` → `.148` and the box walk had it typed (now read from the guest); `operations/nodes.md` (9202's password,
Tester 1's address).
- NOT-A-FINDING: the writer's own engine-major check is redundant with `check_entry` (two layers on purpose; the test
asserts the refusal); `/opt/docker/stacks/plex` keeps the catalog's template files after a removal, as every catalog
app's folder does (72); the demo-hp start WARNs „new field … requires manual configuration" come from code 0.341.0 did
not change; a waiter of mine matched its own command line (a known trap, memory `pgrep-waiter-matches-itself`) and
was stopped.
@@ -0,0 +1,70 @@
{
"catalogVersion": "2026.10.21",
"dateReleased": "2026-10-21T14:04:39.8983Z",
"entries": [
{
"cveID": "CVE-2026-96656",
"vendorProject": "Plex",
"product": "Media Server",
"vulnerabilityName": "Plex Media Server External Control of File Name or Path Vulnerability",
"dateAdded": "2026-10-20",
"shortDescription": "Plex Media Server contains an external control of file name or path vulnerability which can allow an admin user to write arbitrary files that may be executed on load. This vulnerability can be chained with CVE-2026-96652, CVE-2026-96654, and CVE-2026-96655.",
"requiredAction": "Apply mitigations in accordance with vendor instructions, ensuring compliance with CISA\u2019s BOD 26-04 Prioritizing Security Updates Based on Risk (see URL in Notes) guidance and CISA\u2019s \u201cForensics Triage Requirements\u201d (see URL in Notes). Follow applicable BOD 26-04 guidance for cloud services or discontinue use of the product if mitigations are unavailable. Stakeholders are responsible for evaluating each asset's internet exposure and ensuring adherence to BOD 26-04 patching guidelines.",
"dueDate": "2026-10-23",
"knownRansomwareCampaignUse": "Unknown",
"forensicTriage": "Yes",
"notes": "https://forums.plex.tv/t/important-security-update-for-plex-media-server-v1-43-2-and-earlier/942319 ; ; BOD 26-04: https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk ; Forensics Triage Requirements: https://www.cisa.gov/news-events/directives/bod-26-04-implementation-guidance-prioritizing-security-updates-based-risk ; https://nvd.nist.gov/vuln/detail/CVE-2026-96656",
"cwes": [
"CWE-73"
]
},
{
"cveID": "CVE-2026-96655",
"vendorProject": "Plex",
"product": "Media Server",
"vulnerabilityName": "Plex Media Server Server-Side Request Forgery (SSRF) Vulnerability",
"dateAdded": "2026-10-20",
"shortDescription": "Plex Media Server contains a server-side request forgery (SSRF) vulnerability that could allow an authenticated user to request arbitrary internal or external addresses via the '/video/:/transcode/universal' path parameter. This vulnerability can be chained with CVE-2026-96652, CVE-2026-96654, and CVE-2026-96656.",
"requiredAction": "Apply mitigations in accordance with vendor instructions, ensuring compliance with CISA\u2019s BOD 26-04 Prioritizing Security Updates Based on Risk (see URL in Notes) guidance and CISA\u2019s \u201cForensics Triage Requirements\u201d (see URL in Notes). Follow applicable BOD 26-04 guidance for cloud services or discontinue use of the product if mitigations are unavailable. Stakeholders are responsible for evaluating each asset's internet exposure and ensuring adherence to BOD 26-04 patching guidelines.",
"dueDate": "2026-11-03",
"knownRansomwareCampaignUse": "Unknown",
"forensicTriage": "No",
"notes": "https://forums.plex.tv/t/important-security-update-for-plex-media-server-v1-43-2-and-earlier/942319 ; ; BOD 26-04: https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk ; Forensics Triage Requirements: https://www.cisa.gov/news-events/directives/bod-26-04-implementation-guidance-prioritizing-security-updates-based-risk ; https://nvd.nist.gov/vuln/detail/CVE-2026-96655",
"cwes": [
"CWE-918"
]
},
{
"cveID": "CVE-2026-96654",
"vendorProject": "Plex",
"product": "Media Server",
"vulnerabilityName": "Plex Media Server Improper Neutralization of Encoded URI Schemes in a Web Page Vulnerability",
"dateAdded": "2026-10-20",
"shortDescription": "Plex Media Server contains an improper neutralization of encoded URI schemes in a web page vulnerability that could allow an attacker to call other plugins' functions and supply their own parameters. This vulnerability can be chained with CVE-2026-96652, CVE-2026-96655, and CVE-2026-96656.",
"requiredAction": "Apply mitigations in accordance with vendor instructions, ensuring compliance with CISA\u2019s BOD 26-04 Prioritizing Security Updates Based on Risk (see URL in Notes) guidance and CISA\u2019s \u201cForensics Triage Requirements\u201d (see URL in Notes). Follow applicable BOD 26-04 guidance for cloud services or discontinue use of the product if mitigations are unavailable. Stakeholders are responsible for evaluating each asset's internet exposure and ensuring adherence to BOD 26-04 patching guidelines.",
"dueDate": "2026-10-23",
"knownRansomwareCampaignUse": "Unknown",
"forensicTriage": "No",
"notes": "https://forums.plex.tv/t/important-security-update-for-plex-media-server-v1-43-2-and-earlier/942319 ; ; BOD 26-04: https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk ; Forensics Triage Requirements: https://www.cisa.gov/news-events/directives/bod-26-04-implementation-guidance-prioritizing-security-updates-based-risk ; https://nvd.nist.gov/vuln/detail/CVE-2026-96654",
"cwes": [
"CWE-84"
]
},
{
"cveID": "CVE-2026-96652",
"vendorProject": "Plex",
"product": "Media Server",
"vulnerabilityName": "Plex Media Server Server-Side Request Forgery (SSRF) Vulnerability",
"dateAdded": "2026-10-20",
"shortDescription": "Plex Media Server contains a server-side request forgery (SSRF) vulnerability which could allow an attacker using any X-Plex-Token value to include a full URL in the 'protocol' parameter and force the Plex server to POST to the attacker's chosen destination. This vulnerability can be chained with CVE-2026-96654, CVE-2026-96655, and CVE-2026-96656. ",
"requiredAction": "Apply mitigations in accordance with vendor instructions, ensuring compliance with CISA\u2019s BOD 26-04 Prioritizing Security Updates Based on Risk (see URL in Notes) guidance and CISA\u2019s \u201cForensics Triage Requirements\u201d (see URL in Notes). Follow applicable BOD 26-04 guidance for cloud services or discontinue use of the product if mitigations are unavailable. Stakeholders are responsible for evaluating each asset's internet exposure and ensuring adherence to BOD 26-04 patching guidelines.",
"dueDate": "2026-11-03",
"knownRansomwareCampaignUse": "Unknown",
"forensicTriage": "No",
"notes": "https://forums.plex.tv/t/important-security-update-for-plex-media-server-v1-43-2-and-earlier/942319 ; ; BOD 26-04: https://www.cisa.gov/news-events/directives/bod-26-04-prioritizing-security-updates-based-risk ; Forensics Triage Requirements: https://www.cisa.gov/news-events/directives/bod-26-04-implementation-guidance-prioritizing-security-updates-based-risk ; https://nvd.nist.gov/vuln/detail/CVE-2026-96652",
"cwes": [
"CWE-918"
]
}
]
}
@@ -0,0 +1,33 @@
mirror main cde26ab plex: guide checked against 1.43.5.11029-6bf8af3f0 (every HU + EN label read from the new web client, all equal); CHANGELOG + REPORT for the Plex2Shell move
drill repo main: cde26ab
26: repo_url: file:///opt/docker/felhom-controller/drill-catalog.git
26c26
< repo_url: https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git
---
> repo_url: file:///opt/docker/felhom-controller/drill-catalog.git
Traceback (most recent call last):
File "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/tools/repoint.py", line 86, in <module>
{"mirror": mirror, "drill": drill, "restore": restore, "status": status}[sys.argv[1]]()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/tools/repoint.py", line 74, in drill
restart_sync()
~~~~~~~~~~~~^^
File "/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/security-lane-2026-10-23/tools/repoint.py", line 64, in restart_sync
w.sync_rescan()
~~~~~~~~~~~~~^^
File "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/box_walk.py", line 466, in sync_rescan
ctl("POST", "/api/sync")
~~~^^^^^^^^^^^^^^^^^^^^^
File "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/box_walk.py", line 132, in ctl
sess = open(f"{SC}/sess{os.getpid()}.txt").read().strip()
~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/63b6c84e-0c09-4657-a160-855d2e4fe326/scratchpad/sc9202/sess3574518.txt'
26: repo_url: file:///opt/docker/felhom-controller/drill-catalog.git
27: sync_interval: 15m
-rw------- 1 root root 1994 Oct 22 14:47 /var/lib/docker/volumes/felhom-controller-data/_data/controller.yaml.pre-seclane1023
mirror main cde26ab plex: guide checked against 1.43.5.11029-6bf8af3f0 (every HU + EN label read from the new web client, all equal); CHANGELOG + REPORT for the Plex2Shell move
cache HEAD cde26ab plex: guide checked against 1.43.5.11029-6bf8af3f0 (every HU + EN label read from the new web client, all equal); CHANGELOG + REPORT for the Plex2Shell move
file:///opt/docker/felhom-controller/drill-catalog.git
@@ -0,0 +1,3 @@
13:25:23 [X] stop -> 200 {'ok': True, 'message': 'Stack plex stop completed'}
13:25:55 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'plex', 'volumes_removed': ['plex_plex_config', 'plex_plex_transcode'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'us
13:26:04 [X] after remove: deployed=False leftovers='/opt/docker/stacks/plex'
@@ -0,0 +1,19 @@
RESTORED-IDENTICAL
26: repo_url: https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git
2026/10/23 11:26:11 sync.go:344: [WARN] [sync] git.repo_url changed (cache was cloned from file:///opt/docker/felhom-controller/drill-catalog.git, config says https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git) — re-cloning the catalog cache from the configured repository (R-615)
2026/10/23 11:26:11 sync.go:318: [INFO] [sync] Cloning https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git → /opt/docker/felhom-controller/data/catalog-cache
2026/10/23 11:26:13 sync.go:511: [WARN] [sync] immich: catalog definition REFUSED by the allow-list — the app keeps its last good compose, nothing written: the catalog definition of immich asks for container rights the box does not allow: immich-server: bind /mnt/sizing-bench/appdata/immich -> /usr/src/app/upload rw; immich-server: bind /mnt/sizing-bench/userdata/media/photos -> /external/photos ro
2026/10/23 11:26:13 sync.go:535: [INFO] [sync] Updated plex/docker-compose.yml
2026/10/23 11:26:13 sync.go:535: [INFO] [sync] Updated plex/.felhom.yml
2026/10/23 11:26:13 sync.go:261: [INFO] [sync] catalog moved 17e0da02fd61 -> 389859359491 (0 new, 1 stack dir(s) changed)
2026/10/23 11:26:13 sync.go:299: [INFO] [sync] Catalog sync complete
2026/10/23 11:26:13 sync.go:142: [INFO] [sync] Initial sync: Sablonok frissítve — frissítve: plex
26: repo_url: https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git
27: sync_interval: 15m
-rw------- 1 root root 1994 Oct 22 14:47 /var/lib/docker/volumes/felhom-controller-data/_data/controller.yaml.pre-seclane1023
mirror main 17e0da02 Revert "DRILL plex: compose image line back to 1.43.4 ONLY (the ladder untouched) — a fresh install at FROM for the Part 3 proof; reverted right after"
cache HEAD 3898593 D39 = B: the security-fix lane — a `security_fix` ladder step (box walk required, bench optional, CVE/GHSA notice), its gates + 8 decoys, the writer (--write-ladder --security), the box venue (box_walk.py --security, drill-mirror refresh); two stale tests fixed
https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git
@@ -0,0 +1,4 @@
felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.341.0 Up 25 seconds (healthy)
0
removed-identical-save
34
@@ -0,0 +1,25 @@
"""chain.py — press "run tonight's chain now" (POST /api/debug/backup/night-chain, R-705) on the box and quote the update
leg's own lines; then read Plex's four observables. D39 = B proof (felhom.eu audits/security-lane-2026-10-23)."""
import json, re, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
w.LOG = []
w.login()
w.refresh_box_mirror()
w.sync_rescan("plex", "plexinc/pms-docker:1.43.5.11029-6bf8af3f0", tries=20, delay=6)
print("before:", json.dumps(w.observables("plex")))
print("badge before:", w.badges("plex"))
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
code, d = w.ctl("POST", "/api/debug/backup/night-chain")
print("night chain ->", code, str(d)[:200])
leg = ""
for _ in range(240):
time.sleep(10)
leg = w.guest("docker logs --since %s felhom-controller 2>&1 | grep -E 'update-leg' | grep -v DEBUG | cut -c1-400" % since)
if re.search(r"update leg .*: done=", leg):
break
print("the leg's own lines:\n" + leg)
time.sleep(10)
print("after:", json.dumps(w.observables("plex")))
st = w.stack("plex")
print("state", st.get("state"), "hold", st.get("hold_reason"), "phase", st.get("update_phase"))
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""repoint.py mirror|drill|restore|status — point a box (scratch 9202; copied from audits/burndown-3-2026-10-17/tools/ for the D39 security lane, 2026-10-23) at the drill catalog WITHOUT a credential (R-992).
WHY A LOCAL MIRROR. `09` §6.5's route needs a catalog credential on the box: the drill repo is private (R-992). This
session could not mint a READ-ONLY one — the Gitea credential on DooPlex is itself a token, and Gitea answers 403 to a
token asking to list or create tokens (measured 2026-10-14) — and the night of 2026-10-06 put the admin WRITE token
into the box's controller.yaml, which this task forbids. The cache-staging workaround of 2026-10-12 does not hold for a
test that lasts days: every sync (and every controller start) re-applies the live catalog over the staged templates
and over the installed apps' compose files (Syncer.copyTemplates, no deployed check).
So the drill catalog is MIRRORED onto the box: a bare git repository inside the controller's own data volume, and
`git.repo_url: file:///opt/docker/felhom-controller/drill-catalog.git`. The controller's sync runs exactly as for a
remote (clone/fetch/reset, the R-615 re-clone on a changed URL); no secret is on the box. DooPlex updates the mirror
from the drill repo with a git bundle (`mirror`). The drill repo on Gitea stays the record.
SC=~/.felhom-tester1 TARGET=tester-1 python3 repoint.py mirror # (re)load the mirror from the drill repo main
... repoint.py drill # save controller.yaml (once), set repo_url to the mirror, restart, sync, rescan
... repoint.py restore # put the saved controller.yaml back (byte-identical), restart, sync, rescan
... repoint.py status
"""
import os, subprocess, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
DRILL = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data" # guest path of the controller's data volume
CVOL = "/opt/docker/felhom-controller" # the same, inside the controller container
SAVE = f"{VOL}/controller.yaml.pre-seclane1023"
MIRROR_URL = f"file://{CVOL}/drill-catalog.git"
def status():
print(w.guest(f"""grep -nE 'repo_url|sync_interval' {VOL}/controller.yaml; ls -l {SAVE} 2>&1 | tail -1
docker exec felhom-controller sh -c 'git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main 2>&1;
git -C {CVOL}/data/catalog-cache log -1 --format="cache HEAD %h %s" 2>&1; git -C {CVOL}/data/catalog-cache remote get-url origin'"""))
def mirror():
b = "/tmp/drill-seclane1023.bundle"
subprocess.run(["git", "-C", DRILL, "bundle", "create", b, "main"], check=True, capture_output=True)
head = subprocess.run(["git", "-C", DRILL, "rev-parse", "--short", "main"], capture_output=True, text=True).stdout.strip()
ssh = w.ssh_args()
# bundle -> PVE host -> guest -> controller volume; the bare repo is made and fetched BY the controller's own user
subprocess.run(["scp", "-q", "-o", "StrictHostKeyChecking=accept-new"] + (["-J", w._T["jump"]] if w._T["jump"] else [])
+ [b, f"{w._T['ssh']}:{b}"], check=True)
subprocess.run(ssh + [f"pct push {w.GUEST} {b} {VOL}/drill-seclane1023.bundle && rm -f {b}"], check=True)
print(w.guest(f"""set -e
docker exec felhom-controller sh -c 'test -d {CVOL}/drill-catalog.git || git init -q --bare -b main {CVOL}/drill-catalog.git
git -C {CVOL}/drill-catalog.git fetch -q {CVOL}/drill-seclane1023.bundle +main:main && rm -f {CVOL}/drill-seclane1023.bundle
git -C {CVOL}/drill-catalog.git log -1 --format="mirror main %h %s" main'"""))
print("drill repo main:", head)
os.remove(b)
def restart_sync():
w.guest("docker restart felhom-controller >/dev/null")
for _ in range(60):
time.sleep(3)
try:
w.login()
break
except SystemExit:
continue
w.sync_rescan()
time.sleep(3)
print(w.guest("docker logs --since 3m felhom-controller 2>&1 | grep -E '\\[sync\\]' | grep -v DEBUG | tail -8"))
def drill():
print(w.guest(f"""set -e
test -f {SAVE} || cp -p {VOL}/controller.yaml {SAVE}
sed -i -E 's#^(\\s+repo_url: ).*$#\\1{MIRROR_URL}#' {VOL}/controller.yaml
grep -n 'repo_url' {VOL}/controller.yaml; diff {SAVE} {VOL}/controller.yaml || true"""))
restart_sync()
status()
def restore():
print(w.guest(f"""set -e
cp -p {SAVE} {VOL}/controller.yaml
cmp {SAVE} {VOL}/controller.yaml && echo RESTORED-IDENTICAL; grep -n repo_url {VOL}/controller.yaml"""))
restart_sync()
status()
{"mirror": mirror, "drill": drill, "restore": restore, "status": status}[sys.argv[1]]()
+6
View File
@@ -26,6 +26,12 @@
---
## 2026-10-23 day (later) — D39 = B: the security-fix lane
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1239** | **The catalog has no fast lane for an urgent security fix — a decision for the operator.** (P2) | CLOSED 2026-10-23 — **D39 = B** (operator ruling by starting the D39 brief; `09` §3 decision 255): a `security_fix` ladder step, box walk required, bench optional, a CVE/GHSA notice as the authority, no operator word per incident. Catalog `3898593` (format, gates, 8 decoys, writer `--write-ladder --security`, `box_walk.py --security`); controller **0.341.0** (the leg takes it; global floor 11:24 UTC, MinAgent 0.158.1). **Reasoning kept:** a security-fix step never crosses a database engine major and is never a same-tag fix (decision 52's lane); a controller ≤ 0.340.0 skips it at night and a hand press takes it. | `audits/security-lane-2026-10-23/` (negative control `failed` and refused; Plex 1.43.4 → 1.43.5 proven on 9202; 0.340.0 night `not_proven` + hand press; rc.1 night `security fix … taken`) |
## 2026-10-23 day — the releases and the DooPlex hour (the operator's „go")
| Row | What | Closed | Evidence |
File diff suppressed because one or more lines are too long
+10
View File
@@ -5,6 +5,16 @@ Moved here byte for byte on 2026-10-14 (night session), newest first, so that `S
**Updated 2026-10-13 (night): demo-hp, demo-felhom and Tester 1 run agent 0.155.0; demo-felhom runs controller 0.310.0, the other two 0.309.0 until the floor is raised. The open-items list is at 127. Reports: `REPORT-poweroff.md`, `REPORT-plugmeter.md`, `REPORT-usenet-2026-10-12.md`, `REPORT-newapps-2026-10-12.md`, `REPORT-disk-evening-2026-10-12.md`, `REPORT-answers-2026-10-12.md`, `REPORT-m3-2026-10-11.md`, `REPORT-redesign-2026-10-10.md`, `REPORT-testers-2026-10-10.md`, `REPORT-register-shrink-2026-10-10.md`, `REPORT-new-apps-2026-10-10.md`, `REPORT-release-2026-10-10.md`, `REPORT-break-the-circle-2026-10-09.md`, `REPORT-dooplex-survival-2026-10-09.md`, `REPORT-day4-2026-10-09.md`.**
## Moved from STATUS on 2026-10-23 (afternoon)
**Updated 2026-10-23 (day).** Released on your go: controller 0.340.0 (the floor), agent 0.163.0 (vouched; root SSH by key only on the three test boxes), installer 1.36.0, hub 0.167.0, and the website with your two sentences. The hub database now leaves every 6 hours and the released images and agent packages every night, both read back from ep0; the break-glass sheet is current — please print it. One false failure mail reached you at 10:28 (my install of the 6-hour copy, fixed in the same hour).
**Decided 2026-10-23 ~12:00 — D39 = B** (the operator started the D39 brief; the ruling is in R-1239 in CLOSED-ITEMS.md and in CONTEXT.md):
| # | Row | Question | Pick (why) | The other option costs | If you do nothing | Blocked |
|---|---|---|---|---|---|---|
| D39 | R-1239 | When an app has an urgent security hole, may its fix reach the boxes at night without the full update test? Today it needs your word and a hand press on each box (Plex, today). | **Yes — a marked „security fix” step** (tested on one test box only; the night update then takes it; works without you awake) | stay as today: your word and a CC session for every hole; households wait for the press | the same as today | — |
## Moved from STATUS on 2026-10-23 (day)
**Updated 2026-10-23 (morning).** Your three picks are built (the tunnel repairs itself with a new key, the hub skips a keyless deleted install, the controller repository stays public), and the night copy can no longer outlive the controller. Four fresh reviews found 34 points, every real one fixed or a row; the test night PASSED (no failed check at night, every night job passed, nothing lost). Nothing was released or deployed; the morning list has the order, and the DooPlex survey asks four questions (D32–D35).
+2 -2
View File
@@ -43,7 +43,7 @@ DooPlex while the boxes are away (`felhom-pve-lan` → `No route to host`, 2026-
| What it is | **QEMU VM 341 `night1004-tester1` on demo-hp** (installed from the ISO on 2026-10-04, `audits/night-2026-10-04/tester1/`); disposable (operator) |
| PVE node name | `felhom` (its certificate: `CN=felhom.enkicsifelhom.hu`) |
| LAN address | **DHCP** — read 2026-10-06 as `192.168.0.154` (MAC `bc:24:11:ac:e3:f9`; find it with `ip neigh` after a ping) |
| Customer / guest | `tester-1`; guest LXC 9201 at `192.168.0.101`, dashboard `felhom.enkicsifelhom.hu` |
| Customer / guest | `tester-1`; guest LXC 9201 by DHCP (`192.168.0.101` until 2026-10-23, then `.148` — read it: `pct exec 9201 -- ip -4 -o addr show eth0`; `box_walk.py` reads it itself), dashboard `felhom.enkicsifelhom.hu` |
| SSH | **`ssh root@192.168.0.154` from DooPlex works BY KEY** (measured 2026-10-09: `BatchMode=yes`, used for the release read-back); the old route `ssh -J demo-hp root@<VM address>` stays the fallback; `box_walk.py` `TARGET=tester-1` holds the route |
| Identity checked | 2026-10-06: the agent's own report `host.node=felhom` = the VM's certificate; the guest answers its domain (200) and not demo-hp's (404) — `audits/readback-2026-10-07/E1-identity-match.txt` |
@@ -218,7 +218,7 @@ Details, and the two hard rules that apply to any host node, in `operations/tail
### demo-hp scratch guest — LXC 9202 `demo-hp-scratch` (R-481, built 2026-09-13)
- **Disposition:** the nightly rotation's throwaway host. Under the **demo-hp customer** (same domain,
same dashboard password), **hub reporting OFF, Cloudflare tunnel OFF, agent local API OFF, off-site
a dashboard password of its OWN — `REHEARSAL_2026_10_20_9202_PW` in the CC secret store, named for the 2026-10-20 rehearsal; the demo-hp `PASSWORD` is refused, measured 2026-10-23), **hub reporting OFF, Cloudflare tunnel OFF, agent local API OFF, off-site
OFF, self-update OFF** (the image is set by hand in `/etc/felhom-controller-image`). **Persists across
nights on purpose.** Apps on it are throwaways; nothing on it is a customer promise. Not in the felhom
pool, so the hub never sees it. Tags `scratch,r481`; the same text sits in the guest at