diff --git a/CONTEXT.md b/CONTEXT.md index 526baaad..4584ebe8 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -16,6 +16,15 @@ > and holds nothing of its own; this file does hold its own content, namely the standing rulings below. +> **2026-10-01 — the three rulings built; decision 57 decided by CC unattended (operator may reverse).** 54/55: the first +> full monthly re-test — nextcloud `3b59dfb` and sonarr `1a37032` re-tested on both venues and written (~17 min per app); +> `retest-floating.py` fixed for a fresh bench (R-749). 56: controller v0.285.0 (keeps running + previous controller image, +> previous from the swap record); R-751 nil-stack crash fixed; floor 0.285.0; demo-hp 84 → 2 and N100 76 → 2 controller +> images; golden 0.285.0 baked + vouched (gate OK). **57 (R-747):** mealie `SECURITY_USER_LOCKOUT_TIME=1` (catalog +> `a4597cd`) — measured 120 min lock on 9202. R-746 digest resolver `804884a`; R-744 outline fixture `9fc7052`. +> Opened R-749..R-752 (R-752: calibre-web-automated, wger, Grafana, BookStack lockable by strangers — read, not measured). +> Report: `REPORT-rulings-2026-10-01.md`. + > **Rulings 2026-10-01 (operator) — `09` §3 decisions 54–56, recorded before the work.** **54 (R-743 1A):** the monthly > re-test is run by a CC session the operator starts with the standing brief `claude/MONTHLY-security-retest.md` (project); > STATUS carries "Monthly security re-test: last run , next due ". **55 (R-743 2A):** it covers every app with a diff --git a/REPORT-rulings-2026-10-01.md b/REPORT-rulings-2026-10-01.md new file mode 100644 index 00000000..0c8f182c --- /dev/null +++ b/REPORT-rulings-2026-10-01.md @@ -0,0 +1,90 @@ +# REPORT — the operator's three rulings of 2026-10-01 built (day session) + +Evidence: `documentation/audits/rulings-2026-10-01/` (A–D, T, tools) · `documentation/audits/retest-2026-10/` (the monthly +run) · golden: `documentation/tests/golden-0.285.0-2026-10-01/`. +Architecture read: `09` §3 decisions 30, 45, 52, 53 and §6.5; `03-host-agent.md` (the controller swap) with agent +`internal/localapi/controllerswap.go`; `07` (R-698); `runbooks/monthly-floating-retest.md`; `audits/night-rulings-2026-09-30/`. +Baselines (live Gitea ~07:10 CEST): controller `a70c398` (0.284.2), agent `d766666` (0.138.0), felhom.eu `3159892`, catalog +`efd492d`. Register 383 rows by `register_shape_gate`'s method; highest id R-748; last decision 53. + +## The Part table + +| Part | done / not done / changed | why | +|---|---|---| +| **Rulings 54–56** | **done** — recorded first in `09` §3 and CONTEXT (`2076bf9`) | before any work | +| **A — the full re-test** | **done** — nextcloud `3b59dfb` and sonarr `1a37032` re-tested on both venues and written, pushed through the pre-push gates | first start refused in one minute: the script checked the bench for its own files before copying them (R-749, fixed `9e53205`) | +| A1 runbook | **done** — every app by default, `--engines-only` the switch, the standing brief, cost | — | +| A4 linuxserver cost | **done** — see below | — | +| A5 STATUS standing line | **done** — "last run 2026-10-01, next due ~2026-11-01" | — | +| **B — two controller versions** | **done — controller v0.285.0**, floor 0.285.0 (MinAgent 0.131.0 declared) | measured first: the agent rolls back to the RUNNING image | +| B3 live | **done** on 9202 (version-order fallback) and both demo boxes (the swap record) | 9202's self-update is off (no hub), so the record path was shown on the demo boxes | +| — R-751 | **added** — a nil-stack panic in the update clean-up, found by the full suite, fixed in the same release | a panic in a goroutine ends the controller | +| **C — mealie** | **done — decided by CC unattended (decision 57):** `SECURITY_USER_LOCKOUT_TIME=1`, catalog `a4597cd`; 9202 proof 120 min | no fix stops an hourly renewal without changing the login name (option d, left open) | +| C other apps | **done (read, not measured)** — four more lockable: R-752 | source reading by a sub-agent at each pinned tag | +| **D1 — R-746** | **done** `804884a`, red-proofed (unit + live registry) | — | +| **D2 — R-744** | **done** `9fc7052`, proven on 9202 at 1.10.1 | outline has no newer release, so no step | +| **E — release, floor, golden** | **done** — demo boxes on 0.285.0 within ~6 s; golden 0.285.0 baked, round-trip identical, vouched; the gate prints **OK** | — | + +## Claims in the brief that turned out wrong (or right) + +1. **"`previousImage` … handed to the agent's `SwapController`"** — **wrong.** `SwapController(ctx, target)` carries the + target only; `previousImage` goes into `update-state.json`. The agent reads `/etc/felhom-controller-image` when the swap + begins and rolls back to THAT — the running image. On all three boxes it equals `:`, so the + practical outcome matches; the mechanism does not. +2. **"The sweep skips every controller image today"** — **right**, twice over: the sweep's repositories never include the + controller's, and it skips any repo containing `felhom-controller`. +3. **"nextcloud and sonarr still differ"** — **right**, and only those two (bookstack, radarr, code-server did not differ today). +4. **"mealie's lock is per account"** — **right** (`login_attemps` / `locked_at` on the user). Also: the lock outlives its + hours until mealie's HOURLY job resets the counter, so `1` means 1–2 h (measured 120 min). +5. **"The golden's image is not needed after first boot"** — right **after the first swap**; until then it IS the running + image (kept as such). A whole-guest restore brings its own Docker store (`mp0 backup=1`). +6. **"Is an old controller tag still in the registry?"** — **no, below 0.213.0** (2026-08-12): `0.201.0` answers 404. Nobody + recorded what removed them (R-750). This session deleted no registry tag. +7. "Live on 9202 … after the release" for the record path — 9202 has self-update off; the record path ran on the demo boxes. + +## Part A — the run + +| app | result | bench | box | +|---|---|---|---| +| nextcloud `34.0.4-apache` | **DONE** — written `3b59dfb` | 05:19–05:32 UTC (proven, peak 23.7 %) | ~5 min: old digest installed, seeded, the guarded Update ran the re-test step, new digest running, read back, badge "Naprakész" | +| sonarr `4.0.20` (linuxserver) | **DONE** — written `1a37032` | 05:38–05:50 (proven, peak 13.9 %) | ~3 min, same steps | + +**Monthly cost.** ~17 min per app (bench ~13 incl. the 10-minute watch, box ~4) plus ~15 min to set up and tear down. The +four linuxserver apps with ladders (bookstack, radarr, sonarr, code-server) are rebuilt weekly, but the monthly run tests +only the day's digest — **at most 4 re-tests a month from them (~70 min of bench, ~16 min of 9202), not 16.** Acceptable. + +## Part B — images before → after + +| box | controller images | Docker images (`system df`) | `/var/lib/docker` used | +|---|---|---|---| +| 9202 | 5 → 2 | 3.17 → 3.10 GB (layers are shared) | 3.5 → 3.4 GB | +| demo-hp 9201 | 84 → 2 (82 deleted) | 15.2 → 11.65 GB | 18 → 15 GB | +| N100 9201 | 76 → 2 (74 deleted) | 6.07 → 1.11 GB | 6.0 → 1.2 GB | + +Each kept 0.285.0 (running) and 0.284.2 (previous). Red-proofs (`B/B1-red-proofs.txt`): previous dropped → 0.284.2 +deleted; record ignored → 0.283.1 deleted; no swap check → deleted while swapping; no in-use check → a used image deleted; +no success check → a failed swap's image named previous; no nil-stack check → panic. The first in-use red attempt +removed a line and did not build; redone with the condition disabled (recorded). + +## Part C — mealie + +Settings (v3.28.0 source): `SECURITY_MAX_LOGIN_ATTEMPTS` 5, `SECURITY_USER_LOCKOUT_TIME` 24 (hours), per account, lifted +by an hourly job; `POST /api/admin/users/unlock` exists but the only admin is the locked account. Fix and 9202 proof: see +decision 57 (`C/C1-mealie-lockout-1h.txt`: right password 200 → 5 wrong 401 → 423 → right password 423 for 120 min → 200; +a wrong one 401 after). Other apps (R-752): calibre-web-automated (per username, 3/min and 40/day), wger (per IP = traefik's, +30 min, everyone), Grafana (per account, 5 min), BookStack (e-mail|IP, 60 s); gokapi and claper cannot. + +## Rows + +**383 → 387.** Opened R-749 (re-test start), R-750 (old registry tags gone), R-751 (update clean-up crash), R-752 (four +lockable apps). Closed R-743, R-744, R-745, R-746, R-749, R-751. Narrowed R-747. + +## Teardown + +- **Machine:** 9202 back on the live catalog (`repo_url` read back), the same six containers as at the start, controller + 0.285.0 (the release); the apps this session installed (nextcloud, sonarr, outline, mealie) removed through the product. + Bench 9401 destroyed with its template. Drill VM: CT 9100 destroyed, token/runner/log shredded, qemu exited, reverted to + `virgin`. Drill catalog reset to live (`a4597cd`), image lines identical. +- **Host:** demo-hp `pct list` = 9201, 9202 (as at the start); the N100 untouched except the floor's controller update. +- **Hub:** two form saves — the floor (0.285.0, MinAgent 0.131.0) and the vouch (golden 0.285.0); one floor attempt without + the credential answered 302 `/login` and stored nothing. diff --git a/REPORT.md b/REPORT.md index 6cf9435f..0c8f182c 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,9 +1,90 @@ -# REPORT — 2026-09-27 evening: a restore and a drive move keep the app's records +# REPORT — the operator's three rulings of 2026-10-01 built (day session) -Evidence: `documentation/audits/records-carried-2026-09-27/` (README = the session record). +Evidence: `documentation/audits/rulings-2026-10-01/` (A–D, T, tools) · `documentation/audits/retest-2026-10/` (the monthly +run) · golden: `documentation/tests/golden-0.285.0-2026-10-01/`. +Architecture read: `09` §3 decisions 30, 45, 52, 53 and §6.5; `03-host-agent.md` (the controller swap) with agent +`internal/localapi/controllerswap.go`; `07` (R-698); `runbooks/monthly-floating-retest.md`; `audits/night-rulings-2026-09-30/`. +Baselines (live Gitea ~07:10 CEST): controller `a70c398` (0.284.2), agent `d766666` (0.138.0), felhom.eu `3159892`, catalog +`efd492d`. Register 383 rows by `register_shape_gate`'s method; highest id R-748; last decision 53. -- `07` §6.6: new paragraph "What a restore keeps from the app it replaces" (and: a drive move is not a restore). -- Register: 336 → 337 rows. Opened R-700 (WATCHING, live proof open), R-701 (demo-hp restore test refused for space); closed R-697 (to - `CLOSED-ITEMS.md`); R-691 annotated: (2) not built, and why. -- Hub: no code change; global floor 0.276.0 (MinAgent 0.131.0) via `POST /configuration/global-floor`. -- STATUS and CONTEXT updated. Capability map: not changed. +## The Part table + +| Part | done / not done / changed | why | +|---|---|---| +| **Rulings 54–56** | **done** — recorded first in `09` §3 and CONTEXT (`2076bf9`) | before any work | +| **A — the full re-test** | **done** — nextcloud `3b59dfb` and sonarr `1a37032` re-tested on both venues and written, pushed through the pre-push gates | first start refused in one minute: the script checked the bench for its own files before copying them (R-749, fixed `9e53205`) | +| A1 runbook | **done** — every app by default, `--engines-only` the switch, the standing brief, cost | — | +| A4 linuxserver cost | **done** — see below | — | +| A5 STATUS standing line | **done** — "last run 2026-10-01, next due ~2026-11-01" | — | +| **B — two controller versions** | **done — controller v0.285.0**, floor 0.285.0 (MinAgent 0.131.0 declared) | measured first: the agent rolls back to the RUNNING image | +| B3 live | **done** on 9202 (version-order fallback) and both demo boxes (the swap record) | 9202's self-update is off (no hub), so the record path was shown on the demo boxes | +| — R-751 | **added** — a nil-stack panic in the update clean-up, found by the full suite, fixed in the same release | a panic in a goroutine ends the controller | +| **C — mealie** | **done — decided by CC unattended (decision 57):** `SECURITY_USER_LOCKOUT_TIME=1`, catalog `a4597cd`; 9202 proof 120 min | no fix stops an hourly renewal without changing the login name (option d, left open) | +| C other apps | **done (read, not measured)** — four more lockable: R-752 | source reading by a sub-agent at each pinned tag | +| **D1 — R-746** | **done** `804884a`, red-proofed (unit + live registry) | — | +| **D2 — R-744** | **done** `9fc7052`, proven on 9202 at 1.10.1 | outline has no newer release, so no step | +| **E — release, floor, golden** | **done** — demo boxes on 0.285.0 within ~6 s; golden 0.285.0 baked, round-trip identical, vouched; the gate prints **OK** | — | + +## Claims in the brief that turned out wrong (or right) + +1. **"`previousImage` … handed to the agent's `SwapController`"** — **wrong.** `SwapController(ctx, target)` carries the + target only; `previousImage` goes into `update-state.json`. The agent reads `/etc/felhom-controller-image` when the swap + begins and rolls back to THAT — the running image. On all three boxes it equals `:`, so the + practical outcome matches; the mechanism does not. +2. **"The sweep skips every controller image today"** — **right**, twice over: the sweep's repositories never include the + controller's, and it skips any repo containing `felhom-controller`. +3. **"nextcloud and sonarr still differ"** — **right**, and only those two (bookstack, radarr, code-server did not differ today). +4. **"mealie's lock is per account"** — **right** (`login_attemps` / `locked_at` on the user). Also: the lock outlives its + hours until mealie's HOURLY job resets the counter, so `1` means 1–2 h (measured 120 min). +5. **"The golden's image is not needed after first boot"** — right **after the first swap**; until then it IS the running + image (kept as such). A whole-guest restore brings its own Docker store (`mp0 backup=1`). +6. **"Is an old controller tag still in the registry?"** — **no, below 0.213.0** (2026-08-12): `0.201.0` answers 404. Nobody + recorded what removed them (R-750). This session deleted no registry tag. +7. "Live on 9202 … after the release" for the record path — 9202 has self-update off; the record path ran on the demo boxes. + +## Part A — the run + +| app | result | bench | box | +|---|---|---|---| +| nextcloud `34.0.4-apache` | **DONE** — written `3b59dfb` | 05:19–05:32 UTC (proven, peak 23.7 %) | ~5 min: old digest installed, seeded, the guarded Update ran the re-test step, new digest running, read back, badge "Naprakész" | +| sonarr `4.0.20` (linuxserver) | **DONE** — written `1a37032` | 05:38–05:50 (proven, peak 13.9 %) | ~3 min, same steps | + +**Monthly cost.** ~17 min per app (bench ~13 incl. the 10-minute watch, box ~4) plus ~15 min to set up and tear down. The +four linuxserver apps with ladders (bookstack, radarr, sonarr, code-server) are rebuilt weekly, but the monthly run tests +only the day's digest — **at most 4 re-tests a month from them (~70 min of bench, ~16 min of 9202), not 16.** Acceptable. + +## Part B — images before → after + +| box | controller images | Docker images (`system df`) | `/var/lib/docker` used | +|---|---|---|---| +| 9202 | 5 → 2 | 3.17 → 3.10 GB (layers are shared) | 3.5 → 3.4 GB | +| demo-hp 9201 | 84 → 2 (82 deleted) | 15.2 → 11.65 GB | 18 → 15 GB | +| N100 9201 | 76 → 2 (74 deleted) | 6.07 → 1.11 GB | 6.0 → 1.2 GB | + +Each kept 0.285.0 (running) and 0.284.2 (previous). Red-proofs (`B/B1-red-proofs.txt`): previous dropped → 0.284.2 +deleted; record ignored → 0.283.1 deleted; no swap check → deleted while swapping; no in-use check → a used image deleted; +no success check → a failed swap's image named previous; no nil-stack check → panic. The first in-use red attempt +removed a line and did not build; redone with the condition disabled (recorded). + +## Part C — mealie + +Settings (v3.28.0 source): `SECURITY_MAX_LOGIN_ATTEMPTS` 5, `SECURITY_USER_LOCKOUT_TIME` 24 (hours), per account, lifted +by an hourly job; `POST /api/admin/users/unlock` exists but the only admin is the locked account. Fix and 9202 proof: see +decision 57 (`C/C1-mealie-lockout-1h.txt`: right password 200 → 5 wrong 401 → 423 → right password 423 for 120 min → 200; +a wrong one 401 after). Other apps (R-752): calibre-web-automated (per username, 3/min and 40/day), wger (per IP = traefik's, +30 min, everyone), Grafana (per account, 5 min), BookStack (e-mail|IP, 60 s); gokapi and claper cannot. + +## Rows + +**383 → 387.** Opened R-749 (re-test start), R-750 (old registry tags gone), R-751 (update clean-up crash), R-752 (four +lockable apps). Closed R-743, R-744, R-745, R-746, R-749, R-751. Narrowed R-747. + +## Teardown + +- **Machine:** 9202 back on the live catalog (`repo_url` read back), the same six containers as at the start, controller + 0.285.0 (the release); the apps this session installed (nextcloud, sonarr, outline, mealie) removed through the product. + Bench 9401 destroyed with its template. Drill VM: CT 9100 destroyed, token/runner/log shredded, qemu exited, reverted to + `virgin`. Drill catalog reset to live (`a4597cd`), image lines identical. +- **Host:** demo-hp `pct list` = 9201, 9202 (as at the start); the N100 untouched except the floor's controller update. +- **Hub:** two form saves — the floor (0.285.0, MinAgent 0.131.0) and the vouch (golden 0.285.0); one floor attempt without + the credential answered 302 `/login` and stored nothing. diff --git a/STATUS.md b/STATUS.md index 678ebd1d..cf6de322 100644 --- a/STATUS.md +++ b/STATUS.md @@ -2,54 +2,56 @@ **Ready for the first real tester (Tester-2): yes. You confirmed the tunnel route and the connect mails (2026-09-30).** -**Updated 2026-09-30 (late evening). Both demo boxes run controller 0.284.2 and host agent 0.138.0. Hub 0.126.0. New installs get golden 0.284.2 with agent 0.138.0.** +**Updated 2026-10-01. Both demo boxes run controller 0.285.0 and host agent 0.138.0. Hub 0.126.0. New installs get golden 0.285.0 with agent 0.138.0.** **Tester-2 — read only, from the hub.** The customer record exists. Tester-2's box has not registered yet. -## Your two decisions of tonight — built +## A decision I took myself (you may reverse it) -- **Security fixes under the same name (your A).** The catalog can now re-test a database image's new fix and record - it as a tested step. A box then takes it at night by itself. Tested from start to end on the scratch box: the night - run took the fix, the app kept its data, and the page went back to "up to date". **Today there was nothing to re-test** - for the database and redis images. -- **Old program files on the disk (your A).** A box now keeps each app's current program and the one before it, and - deletes the older ones. It never deletes one that another app still uses. On the first start it cleaned up once: - **the HP demo box freed 11 GB**, the scratch box 21 GB. All apps kept running. +- **mealie: a stranger can now lock the household out for 1 to 2 hours, not a whole day.** mealie locks the account after + five wrong passwords, and its login name is public. I set the lock to mealie's shortest time (1 hour; its hourly + clean-up makes it up to 2). Tested on the scratch box: locked for 2 hours, then the right password worked. The guard + against password guessing stays. **What it does not fix:** a stranger can lock it again every hour. The other way would + be a secret login name, but that changes what the household types — so I did not do it. + +## Your three decisions of today — built + +- **The monthly security re-test covers every tested app (your 2A).** First full run today: **nextcloud and sonarr** had + a new build under the same name. Both passed on the test bench and on the scratch box, and are now in the catalog. Boxes + take them at night. It took about 17 minutes per app. +- **You start the monthly run (your 1A).** Give the standing brief `MONTHLY-security-retest.md` to CC once a month. The + runbook now says so. +- **A box keeps only two controller versions (your 3A).** The new controller 0.285.0 deletes the older ones. The HP demo + box went from 84 versions to 2 (Docker images 15.2 → 11.7 GB). The N100 went from 76 to 2 (6.1 → 1.1 GB). All apps kept + running. I checked first: if an update fails, the box goes back to the version it was running, and that one is always kept. ## What else I did, and it worked -- **The short login risk is closed.** After an install, an app with a known default password is now closed to - strangers until the box has changed that password. Tested on two apps: no stranger got in. -- **wger's phone app can log in** now (its missing key is made at the first start). -- **New controller 0.284.2** is on both demo boxes, and **a new golden 0.284.2** is baked and vouched. The golden check - is green (not just waived). +- **A crash fixed.** The image clean-up after an app update could crash the controller if the app was removed right + after. The test suite found it. Fixed in 0.285.0. +- **Two test-tool fixes:** the digest check now refuses an image build that is gone, and the outline test works with + outline's newest version. +- **New controller 0.285.0** is on both demo boxes, and **a new golden 0.285.0** is baked and vouched. The golden check is + green. ## What broke, and what I did -- **The clean-up first missed most images.** Two mistakes, both found on the scratch box before any demo box got the - release: it did not run on the Remove button, and it could not see images stored without a name. Both fixed and - tested. That is why the version is 0.284.2 (0.284.0 and 0.284.1 never went out). -- **wanderer** can now run on the test bench, but its update is not tested yet: I could not create test data in it. - Also found: its search engine needs one extra setting before any update, or it refuses to start. -- **Two exact versions were rebuilt by their makers under the same name** (nextcloud and sonarr). The monthly re-test - finds them. Tonight I only did the database images, as you ruled. -- **mealie locks the account after five wrong passwords** — also when a stranger types them. Written down. -- **Old controller versions** still pile up on each box (about 50). Your rule covered apps only. Written down. +- **The monthly re-test could not start on a fresh test bench.** It checked for its own files before it copied them. Fixed. +- **Four more apps can be locked by strangers like mealie:** calibre-web (up to a day), wger (30 minutes, and everyone at + once), Grafana (5 minutes), BookStack (1 minute). I read this in their code; not tested on a box yet. Written down. +- **Very old controller versions are gone from our own registry** (everything before mid-August). Nothing needs them + today. I do not know what removed them. Written down. -**Rows.** Tonight: 5 closed, 3 narrowed, 6 opened. The list went from 377 to 383 rows (counted with every row, including three whose number has a letter — the old check skipped those). +**Rows.** Today: 6 closed, 1 narrowed, 4 opened. The list went from 383 to 387 rows. ## What needs you -1. **Who presses the monthly re-test?** It is one command, but it needs the test bench and the scratch box set up - first, so it cannot run by itself (the steps are written down). Options: CC does it in a monthly session, or you - start it. If nobody presses it, same-name security fixes wait until the next time we move that app. -2. **Should the monthly re-test cover every app, or only databases?** Exact versions get rebuilt too (nextcloud, - sonarr). If you do nothing, only databases are re-tested. -3. **Old controller versions:** keep only the current and the previous one? If you do nothing, about 150 MB per release - keeps piling up. -4. **No golden bake is due** — tonight's bake carries the newest controller. +1. **Nothing urgent.** If you want the household to be safe from the hourly re-lock in mealie, say so: then I give it a + secret login name, and the app page shows it. If you do nothing, a stranger who keeps trying can keep mealie locked. +2. **Do you know what removed the old controller versions from the registry?** If you do nothing, I look at the Gitea + settings next time (read only). ## Standing steps -- **Monthly:** the same-name security re-test (runbook `monthly-floating-retest.md`). -- **Weekly:** the golden bake (around 7 October). +- **Monthly security re-test: last run 2026-10-01, next due ~2026-11-01.** (You start it with the standing brief.) +- **Weekly:** the golden bake (around 8 October). diff --git a/documentation/architecture/09-update-architecture.md b/documentation/architecture/09-update-architecture.md index 9b55997a..5914c75b 100644 --- a/documentation/architecture/09-update-architecture.md +++ b/documentation/architecture/09-update-architecture.md @@ -621,6 +621,21 @@ R-636's louder repeated alarm. **Why:** about 50 old controller versions sat on each demo box (R-745); a release is ~400 MB unpacked and several ship a day. Registry tags are never deleted by this. +### 2026-10-01 — decided by CC unattended, operator may reverse + +57. **mealie's account lock lasts 1 hour (in practice 1–2 h), not 24** — *decided by CC unattended 2026-10-01 (R-747) — + operator may reverse.* **Question:** how long may a stranger's five wrong passwords lock the household out of mealie? + **Options:** (a) keep mealie's default 24 h — the strongest guard against guessing, and a stranger who knows the public + login (`admin` / `changeme@example.com`) locks the household out for a day; (b) `SECURITY_USER_LOCKOUT_TIME=1` — the + unit's minimum; mealie's hourly job lifts it, so 1–2 h; still 5 tries per lock against guessing; (c) no lock (a huge + attempt limit) — strangers cannot lock anyone out, and nothing slows guessing but the hash; (d) rename the login to + something unguessable after install — the guard stays and strangers cannot aim it, but it changes what the household + types and what the app page shows. **Why (b):** the brief prefers a short lockout over none; a household would not + notice the weaker guard (5 tries per 1–2 h against a generated 30-character first password); (d) changes a promise on + the page and stays open as an option. **Measured on 9202:** 5 wrong → the right password 423 for 120 min, then 200; a + wrong one 401 after. **Limit, stated:** the lock is per account and the name is public, so a stranger can renew it + every hour. Catalog `a4597cd`. + ### 2026-09-30 (day) — operator notes, recorded before the work - **The day brief runs by day.** Every backup and automatic-update test is started by hand — the night chain's debug diff --git a/documentation/audits/rulings-2026-10-01/C/C1-mealie-lockout-1h.txt b/documentation/audits/rulings-2026-10-01/C/C1-mealie-lockout-1h.txt index 3fa28621..24d0e2f3 100644 --- a/documentation/audits/rulings-2026-10-01/C/C1-mealie-lockout-1h.txt +++ b/documentation/audits/rulings-2026-10-01/C/C1-mealie-lockout-1h.txt @@ -28,3 +28,64 @@ 06:34:53 +18 min right password: 423 06:36:53 +20 min right password: 423 06:38:53 +22 min right password: 423 +06:40:53 +24 min right password: 423 +06:42:53 +26 min right password: 423 +06:44:53 +28 min right password: 423 +06:46:53 +30 min right password: 423 +06:48:53 +32 min right password: 423 +06:50:53 +34 min right password: 423 +06:52:53 +36 min right password: 423 +06:54:53 +38 min right password: 423 +06:56:53 +40 min right password: 423 +06:58:53 +42 min right password: 423 +07:00:53 +44 min right password: 423 +07:02:53 +46 min right password: 423 +07:04:53 +48 min right password: 423 +07:06:53 +50 min right password: 423 +07:08:53 +52 min right password: 423 +07:10:53 +54 min right password: 423 +07:12:53 +56 min right password: 423 +07:14:53 +58 min right password: 423 +07:16:53 +60 min right password: 423 +07:18:53 +62 min right password: 423 +07:20:53 +64 min right password: 423 +07:22:53 +66 min right password: 423 +07:24:53 +68 min right password: 423 +07:26:54 +70 min right password: 423 +07:28:54 +72 min right password: 423 +07:30:54 +74 min right password: 423 +07:32:54 +76 min right password: 423 +07:34:54 +78 min right password: 423 +07:36:54 +80 min right password: 423 +07:38:54 +82 min right password: 423 +07:40:54 +84 min right password: 423 +07:42:54 +86 min right password: 423 +07:44:54 +88 min right password: 423 +07:46:54 +90 min right password: 423 +07:48:54 +92 min right password: 423 +07:50:54 +94 min right password: 423 +07:52:54 +96 min right password: 423 +07:54:54 +98 min right password: 423 +07:56:54 +100 min right password: 423 +07:58:54 +102 min right password: 423 +08:00:54 +104 min right password: 423 +08:02:54 +106 min right password: 423 +08:04:54 +108 min right password: 423 +08:06:54 +110 min right password: 423 +08:08:54 +112 min right password: 423 +08:10:54 +114 min right password: 423 +08:12:54 +116 min right password: 423 +08:14:54 +118 min right password: 423 +08:16:55 +120 min right password: 200 +08:16:55 RESULT: locked for 120 min (last answer 200) +08:16:55 right password again: 200 | a wrong one after: 401 +08:16:58 INFO 2026-10-01T10:12:54 - [192.168.0.180:0] 423 Locked "POST /api/auth/token HTTP/1.1" +ERROR 2026-10-01T10:14:54 - User is locked out from 192.168.0.180 +ERROR 2026-10-01T10:14:54 - User is locked out from 192.168.0.180 +INFO 2026-10-01T10:14:54 - [192.168.0.180:0] 423 Locked "POST /api/auth/token HTTP/1.1" +INFO 2026-10-01T10:16:22 - locked users reset + +10:17:00 [X] stop -> 200 {'ok': True, 'message': 'Stack mealie stop completed'} +10:17:32 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'mealie', 'volumes_removed': ['mealie_mealie_data'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_note': 'Az alkalm +10:17:40 [X] after remove: deployed=False leftovers='/opt/docker/stacks/mealie' +08:17:40 removed: False diff --git a/documentation/audits/rulings-2026-10-01/README.md b/documentation/audits/rulings-2026-10-01/README.md new file mode 100644 index 00000000..c3ef263e --- /dev/null +++ b/documentation/audits/rulings-2026-10-01/README.md @@ -0,0 +1,13 @@ +# The operator's three rulings of 2026-10-01 built (day session) + +Report and Part table: `felhom.eu/REPORT-rulings-2026-10-01.md`. Monthly run evidence: `../retest-2026-10/`. Golden: +`documentation/tests/golden-0.285.0-2026-10-01/`. + +| folder | what | +|---|---| +| `A/` | the bench created (60 GB, swap 0), the drill reset, 9202 pointed at the drill, 9202's containers before | +| `B/` | controller image retention: `B0` the boxes before, `B1` red-proofs, `B2` 9202 deploy + pass (`B2-9202-selfupdate.txt` is empty: that attempt found self-update off on 9202 and was stopped), `B3`/`B4`/`B5` demo boxes before, the floor, after | +| `C/` | mealie: `C0` the drill-only commit, `C1` the 1-hour lock measured as a stranger (120 min) | +| `D/` | `D1` R-746 digest resolver (unit red + live registry), `D2` R-744 outline fixture on 9202 | +| `T/` | teardown: bench destroyed, 9202 back on live, drill reset | +| `tools/` | `walk.py`, `repoint.py` (this folder's paths), `b_9202.py`, `b_selfupdate.py`, `c_mealie.py`, `d_outline.py` | diff --git a/documentation/audits/rulings-2026-10-01/T/T2-9202-repoint-live.txt b/documentation/audits/rulings-2026-10-01/T/T2-9202-repoint-live.txt new file mode 100644 index 00000000..680c08e0 --- /dev/null +++ b/documentation/audits/rulings-2026-10-01/T/T2-9202-repoint-live.txt @@ -0,0 +1,15 @@ +git: + branch: main + repo_url: https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git + sync_interval: 15m + token: + username: "" +hub: +0 + +felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.285.0 +filebrowser gtstef/filebrowser:1.3.3-stable +paperless-postgres postgres:18-alpine +paperless-redis redis:7-alpine +paperless-webserver ghcr.io/paperless-ngx/paperless-ngx:2.20.15 +traefik traefik:v3.6.7 diff --git a/documentation/audits/rulings-2026-10-01/T/T3-drill-reset.txt b/documentation/audits/rulings-2026-10-01/T/T3-drill-reset.txt new file mode 100644 index 00000000..3fc19518 --- /dev/null +++ b/documentation/audits/rulings-2026-10-01/T/T3-drill-reset.txt @@ -0,0 +1,5 @@ +# drill reset 2026-10-01T08:18:54Z +drill before: df5a2a2; live: a4597cd +df5a2a2 DRILL mealie: SECURITY_USER_LOCKOUT_TIME=1 (R-747 proof on 9202) +drill after: a4597cd +image lines IDENTICAL diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 91fd6da5..d9094805 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -858,7 +858,7 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server` | **R-744** | **[P3-LOW] outline's fixture cannot seed outline 1.10.1: no `csrfToken` cookie after `installation.create`.** MEASURED 2026-09-30 on both venues (bench and 9202, the end-to-end re-test's first attempt): `POST /api/installation.create` answered 302 with the session, and `GET /home` set no `csrfToken` cookie (1.9.1 did; the 1.9.1 → 1.10.1 step read back that afternoon because its read-back uses the API key made at 1.9.1). So the NEXT outline step, and any re-test of outline, stops at C1 with „no csrfToken cookie from GET /home". **Needs:** the fixture reads outline 1.10.1's CSRF the way its page does (measure first). `audits/night-rulings-2026-09-30/A/e2e/*outline-attempt*` **-- 2026-10-01:** Outline 1.10 names the cookie `__Host-csrfToken` on a secure request (`server/utils/csrf.ts`); the fixture accepts both names (catalog `9fc7052`). Proven on 9202 at the live pin 1.10.1: seed, read back, unknown id and wrong key refused. Outline has no newer release than 1.10.1, so no step. `audits/rulings-2026-10-01/D/D2-outline-fixture-9202.txt` | **CLOSED 2026-10-01 — fixture fixed, proven on 9202** | | **R-745** | **[P3-LOW] Old CONTROLLER images are never deleted: ~50 versions on each demo box.** MEASURED 2026-09-30 after v0.284.2's one-time sweep: every image no container uses on demo-hp and on the N100 is a `felhom-controller` tag (0.201.0 … 0.283.1); the N100 still reads 3.6 GB reclaimable. Decision 53 covers APP images, and the sweep leaves the controller's own images alone on purpose (the self-update may need the previous one). A release is ~150 MB and there are several a day. **Needs:** the same rule for the controller — keep the running and the previous tag — as a decision (the self-update's rollback reads which image?). `audits/night-rulings-2026-09-30/E/` **-- 2026-10-01:** Decision 56 built: controller v0.285.0 keeps the running image + the previous (the swap record; version order as fallback), deletes older/untagged ones. **Measured first:** the agent rolls back to the RUNNING image (what `/etc/felhom-controller-image` named), never to a previous one the controller hands it. After the floor: demo-hp 84 → 2 controller images (Docker images 15.2 → 11.65 GB), the N100 76 → 2 (6.07 → 1.11 GB), 9202 5 → 2. `audits/rulings-2026-10-01/B/` | **CLOSED 2026-10-01 — controller v0.285.0, floor 0.285.0** | | **R-746** | **[P3-LOW] `image_digest.resolve` ignores a `@digest` in its argument — it answers the TAG's current digest.** MEASURED 2026-09-30: `resolve('redis:7-alpine@sha256:000…0')` returned `sha256:858f…` (the tag's), while a manifest request for that digest answers 404. Every gate today passes it a plain tag, so no gate is wrong; a caller that passes `ref@digest` to ask "is THIS digest still served" gets a false yes. **Needs:** refuse a digest-carrying ref, or ask for the manifest by digest (with a test). `scripts/image_digest.py` **-- 2026-10-01:** `resolve()` asks the registry for the manifest BY DIGEST when the ref carries one; a malformed digest is refused without a request (catalog `804884a`, `scripts/test_image_digest.py`; red: the old resolver fails 3 of 4). Live: `redis:7-alpine@sha256:000…0` → HTTP 404 (was the tag's digest). `audits/rulings-2026-10-01/D/D1-r746-digest.txt` | **CLOSED 2026-10-01 — catalog 804884a** | -| **R-747** | **[P3-LOW] A stranger can lock the household out of mealie with five wrong logins.** MEASURED 2026-09-30 on 9202 (the R-741 proof): after the install hold opened, a stranger's default-login tries were refused (401) and after five of them mealie answered 423 (locked) to every login — the generated, correct password included. mealie's own brute-force guard, on an app published on the internet; the setup gate and the install hold do not cover an app after its first setup. Not measured: how long the lock lasts. **Needs:** measure the lock's length; decide whether the page tells the household what to do. `audits/night-rulings-2026-09-30/C/C3-mealie-poll.txt` | **READY — rank P3-LOW; owner: CC** | +| **R-747** | **[P3-LOW] A stranger can lock the household out of mealie with five wrong logins.** MEASURED 2026-09-30 on 9202 (the R-741 proof): after the install hold opened, a stranger's default-login tries were refused (401) and after five of them mealie answered 423 (locked) to every login — the generated, correct password included. mealie's own brute-force guard, on an app published on the internet; the setup gate and the install hold do not cover an app after its first setup. Not measured: how long the lock lasts. **Needs:** measure the lock's length; decide whether the page tells the household what to do. `audits/night-rulings-2026-09-30/C/C3-mealie-poll.txt` **-- 2026-10-01:** Measured at v3.28.0 (source + 9202): 5 wrong logins lock the ACCOUNT (not the IP) for `SECURITY_USER_LOCKOUT_TIME` hours (default 24); the lock is lifted by an hourly job; an admin can unlock others via `POST /api/admin/users/unlock`, but the household's only admin is the locked account. Both login names are public (`admin`, `changeme@example.com`). **Fixed** (`09` §3 decision 57, decided by CC unattended — operator may reverse): `SECURITY_USER_LOCKOUT_TIME=1`, catalog `a4597cd`; on 9202 the right password answered 423 for 120 min, then 200; a wrong one still 401 (`audits/rulings-2026-10-01/C/`). **Left:** a stranger can renew the lock every hour (per account, public name — option (d) in decision 57 would end that); the page does not tell the household why it is locked; an INSTALLED mealie takes the new setting only when its compose is rendered again (not measured which act does that). | **NARROWED — the lock is 1–2 h; renewal and the page remain; owner: CC** | | **R-748** | **[P3-LOW] The register-shape gate skipped every row whose id has a letter suffix — so R-88a, R-88b and R-209a were never shape-checked, and its count read 3 short.** FOUND 2026-09-30 (late) while counting the register: `register_shape_gate.py` matched `R-\d+` only; the brief's „the reviewer's regex undercounted by 3” is the same three rows. Fixed the same session: `R-\d+[a-z]?`; decoy `suffix-row-eaten-state` (a suffixed row with its state cell eaten) seen passing with the old pattern and convicted with the new. The register is **382** rows by either count now. | **CLOSED 2026-09-30 — `scripts/register_shape_gate.py`** | | **R-749** | **[P3-LOW] `retest-floating.py` could never start on a fresh bench: it checked for `/opt/upg/upgrade-test.py` on the bench BEFORE the step that copies it there.** FOUND 2026-10-01 at the first full monthly run (decision 55): bench 9401 freshly created by the runbook, the run answered „CANNOT START — missing: the bench LXC 9401 on demo-hp with /opt/upg” in one minute. The runbook says the command syncs the bench itself — it does, but only after the check. On 2026-09-30 the bench had been synced by hand earlier, so nobody saw it. **Needs:** the check asks for what the bench must bring (docker, python3), the sync then provides `/opt/upg`. `audits/rulings-2026-10-01/A/` **-- 2026-10-01:** Fixed the same session (catalog `9e53205`): the check asks for docker + python3; after the sync `/opt/upg/upgrade-test.py` is required. The re-run started at once and finished both apps. | **CLOSED 2026-10-01 — catalog 9e53205** | | **R-750** | **[P3-LOW] The registry no longer holds controller releases older than 0.213.0 (2026-08-12) — something removed them, and nothing records what.** MEASURED 2026-10-01 (anonymous registry API, `audits/rulings-2026-10-01/B/`): `felhom-controller` has 91 tags, the oldest release 0.213.0; `0.201.0` answers 404; Gitea's package list starts 2026-08-12. No runbook, row or memory names a clean-up. Today nothing needs those versions: a box runs a newer one, a whole-guest restore brings the guest's own Docker store back (mp0 `backup=1`), and decision 56 deletes only on the box. **But** a box or a backup that names a removed version cannot pull it again (R-698's shape, for the controller). **Needs:** find what removed them (a Gitea clean-up rule?), and record the rule — or say it was a one-time act. Read-only on DooPlex. | **OPEN — rank P3-LOW; owner: operator (Gitea settings), CC measures** |