diff --git a/REPORT-burndown-apps-2026-10-16.md b/REPORT-burndown-apps-2026-10-16.md new file mode 100644 index 00000000..c2bcf449 --- /dev/null +++ b/REPORT-burndown-apps-2026-10-16.md @@ -0,0 +1,82 @@ +# REPORT — apps burndown: RomM saves, the false „shared password" line, FileBrowser, the Data tab, the install address, the build guards, the Pi key (2026-10-16) + +Written in ASD-STE100 Simplified Technical English. Operator brief „apps burndown", daytime, operator present. +Design read first: `architecture/02-controller-module-map.md` „The app page" (now with the v0.321.0 notes), +`07-backup-architecture.md` (what an app backup holds — a fact added), `09-update-architecture.md` §0. +Evidence: `documentation/audits/burndown-apps-2026-10-16/`. + +**The operator's word before the start:** no RomM saves need to be kept or copied on any box. So Part 1 has no one-time +copy, and no stop for the N100. + +## 1. RomM saves (R-1097) — done + +- **Measured first (demo-hp 9201):** RomM writes saves to `/romm/assets`; the image keeps `/romm` as an anonymous + volume; the compose mounted nothing there. Every RomM backup on the drive held only `romm_config`, `romm_db_data`, + `romm_redis_data` — no saves. In the code, the app backup takes only the named volumes the compose declares. + `P1/p1-measure.txt`. +- **Fix (catalog `326f846`):** a named volume `romm_assets:/romm/assets`, in the compose and both older step files. A + named volume is the catalog's normal place for app data (07 §6.2), every app backup takes it, and the restore brings it + back. New catalog gate `image-volumes` (a measured list of image data paths that must be mounted). +- **Survey:** no other running app on demo-hp keeps data in an anonymous volume (PrivateBin's are temp folders). + `P1/anon-volumes-demo-hp.txt`. +- **Proof, through the product's endpoints** (`tools/romm_saves.py`, `P1/romm-saves.txt`): sync → restart (the new + volume mounted) → a 32 KiB save file written into `/romm/assets` with `docker exec` → the by-day backup run (night + chain, 5 min 32 s; its update leg did nothing) wrote `romm_romm_assets.tar` → RomM removed, keeping backups (the volume + went) → restored from the backups page (77 s) → **the save back, byte-identical (sha256)**. The dummy file removed after. +- **Boxes:** the hub shows RomM only on demo-hp (the N100 had it once, on 2026-10-13). `P1/hub-romm-boxes.txt`. + +## 2. Parts 2–7 + +| Part | Result | +|---|---| +| 2 — the false „shared password" line (R-1102, D10 = A) | **Done.** `default_creds` removed (hu + en) from the eight; copy freeze re-captured with the reason; a catalog test, red-proved. Live on demo-hp: the line is on none of their app pages or install pages (`live/d10-check.txt`). The user name „admin" stays in the first steps of grafana, gokapi, nzbget, transmission. | +| 3.1 — FileBrowser's stale password (R-1101) | **Done** (controller 0.321.0). The „change it" line is gone for FileBrowser; the page checks the stored password with FileBrowser first and, refused, says so and offers the new-password press. Only FileBrowser's own 401/403 counts. | +| 3.2 — FileBrowser's own banners | **Done.** `server.disableUpdateCheck` (`backend/common/settings/structs.go:51`, read at `backend/cmd/root.go:66`) and `frontend.disablePWAInstall` (`structs.go:294`, sent at `backend/http/static.go:300`) at the pinned tag, set in the rendered config. In the browser, logged in: no „update is available", no „Install app", `disablePWAInstall: true`, `updateAvailable: ""`. Control: the 2026-10-16 screenshot `logins-folders-2026-10-16/browser/cold-romm-2-after-login.png` shows the banner before. | +| 4 — the Data tab's backup part | **Done.** One section, rows, plain words; the value column in the UI font; the export eye is the text „Megjelenítés"/„Elrejtés" with `aria-label`. Changed keys: `tier2_config.a_2_mentes_egy_masik`, `.csak_db_konfiguracio`, `.jelenleg_csak_a_belso_ssd`, `.nincs_elerheto_off_drive_cel`, `.nincs_masik_adatmeghajto_automatikus_cel`, `.rendszerlemez.vedett`, `note.tier2.no_space_ssd`, `note.tier2.reason_no_second`. No eye icon exists in the dashboard's icon set, and adding one would change every page's fixture, so the button is the text the other reveal buttons use. | +| 5 — the install address (R-1095) | **Done** (302 to the settings tab, GET only). **The template clean-up is NOT done:** the installed-only branches of `app_fields.html` are pinned by the R-254/R-709 guard tests, which assert a reveal control on that path; removing them means retiring those tests — filed R-1103. | +| 6 — the build guards (R-1098) | **Done.** `build-hub.sh` 1.25.0 refuses an existing tag and an unanswered registry (6 cases, red-proved). The agent's `release-agent.sh` had the check but let an unreachable registry through; now only a clean 404 proceeds (5 cases, red-proved). Script changes only: **no hub image, no hub deploy, no stop needed.** | +| 7 — the Raspberry Pi key (R-1067) | **Not done, as the brief says:** Tester-2 still reports controller **0.292.0** (newest report 2026-10-04). Its name is in the row. `P7-pi-fleet.txt`. | + +## 3. Browser check (headless Chrome through `https://felhom.enkisfelhom.hu`) — `browser/` + +`tools/cdp_burndown.py`. Calibre-Web's Data tab: one backup section, rows nightly/second/target, no off-drive/PBS, value +font Plus Jakarta Sans, the export toggle with its label. FileBrowser's page: the reveal, no „change it" line. +`/apps/calibre-web/install` landed on `/apps/calibre-web/settings`. FileBrowser itself: no banners. No console error; no +page wider than the window. + +## 4. Versions and commits + +- **felhom-controller** `c02c9d8` → `7e791d9` → docs. `0.321.0-rc.1` (demo-hp), release **0.321.0**, MinAgent 0.158.1. + Tester 1, demo-hp and the N100 took it through per-box floors; nothing unhealthy, restarting, exited or created; the + three dashboards 200; https-only 35 names OK; each box's FileBrowser config carries both switches. **Global floor + 0.321.0 / MinAgent 0.158.1**, the per-box floors cleared (`release/`). +- **app-catalog-felhom.eu** `5630979` → `326f846` (RomM volume, gate `image-volumes`, D10) → docs. +- **felhom-agent** `3835224` → `dd8b2ac` (script and test only; no release). +- **felhom.eu** `11fa13ee` → this commit (`build-hub.sh` 1.25.0, its test, the register, design notes, STATUS, evidence). +- **Fixtures replaced (13):** `app_data_deployed`, `app_data_export`, `app_data_files_media`, `app_data_not_hdd`, + `_not_hdd_absent`, `_not_hdd_protected`, `_not_hdd_same_disk`, `_notarget_disabled`, `_target_auto`, `_target_ssd`, + `app_settings_deployed`, `app_settings_operator_creds`, `app_settings_infra_filebrowser`. **New (1):** + `app_settings_infra_filebrowser_stale`. Every other fixture, the install page's included, byte-identical. +- **Red-proofs:** `redproof/` (controller 6 + 1 redo, catalog 2 gates + 1 test, hub build, agent). + +## 5. Rows + +**Before 179, after 175. Closed 5:** R-1097, R-1102, R-1101, R-1095, R-1098. **Opened 1:** R-1103. R-1067 stays +BLOCKED, now naming Tester-2. + +**System poster:** no listed fact changed — none of the three was needed. + +## 6. Observations + +1. The install form keeps branches no page serves any more. FILED: R-1103 +2. Reading the hub's database by copying its three files one by one gave a STALE floor (0.320.0, 30 s after 0.321.0 was set); one tar of the three files and the hub's own page both read 0.321.0. NOT-A-FINDING: an evidence technique, not a product defect; the memory recipe is corrected to one tar. +3. The by-day backup run (night chain) also runs the update leg and the off-site push. NOT-A-FINDING: it is the sanctioned trigger, documented as „the night chain, by day"; here the update leg did nothing (kimai held). +4. No live control exists for the „known, shared password" line any more: the eight apps were the only ones that showed it. NOT-A-FINDING: `known_login_test.go` is the mechanism control and passes. + +## 7. Teardown (three layers) + +- **Machine:** demo-hp 9201 — RomM removed and restored (it runs, healthy, with its new saves volume); the dummy save + file removed. FileBrowser recreated once on each box by the config change (the password survives: its database is in + its volume). Nothing else changed. The N100: only the normal release update. +- **Host:** nothing. +- **Hub:** the three per-box floors set and cleared; the global floor raised to 0.321.0 (the release). diff --git a/STATUS.md b/STATUS.md index 20ddd857..510106f8 100644 --- a/STATUS.md +++ b/STATUS.md @@ -6,6 +6,7 @@ `documentation/backlog/STATUS-HISTORY.md`. Reports: `REPORT-night-2026-10-14.md`, `REPORT-immich-rollout-2026-10-14.md`, `REPORT-seo-felho-tarhely-2026-10-14.md`, `REPORT-immich-repair-2026-10-14.md`, `REPORT-power-cut-fixes-2026-10-14.md`, `REPORT-night-power-2026-10-13.md`, `REPORT-poweroff.md`. ## What works +- **RomM game saves are in the backup now** (2026-10-16, catalog): a save survived a backup, a removal and a restore on demo-hp, byte for byte. Eight apps no longer claim „a known, shared password" (your D10 = A). If someone changes the file manager's password in the file manager, the box notices and offers a new one (controller 0.321.0). - **Every installed app says where its login is, and has a „Fájlok ↗" button to its folder** (controller 0.320.0, 2026-10-16). The file manager is shown as part of the box again, with its password; on a box that does not know that password, one press sets a new one (done on demo-hp and, at your yes, on the N100). - **Every app but three has a user guide and an FAQ, in Hungarian and English** (catalog, 2026-10-15 night): the „Útmutató” tab shows it on boxes with controller 0.315.0 or newer. Every button name in a guide was checked in the app's own files at the version we ship; a guide that could not be checked stays hidden (plant-it, home-assistant), and Emby has none (R-1092). - **The N100 is the lived-in box** (since 2026-10-15): used like a household's box, changed only through the product. qBittorrent, Prowlarr, Sonarr and Radarr are installed and linked by the box; films never go off-site (checked before any download). Its diary: `documentation/lived-in/n100.md`. @@ -49,9 +50,9 @@ | D7 | R-990, R-989 | Rule yes / no / later on the forum ideas (`audits/forum-research-2026-10-12/FELHOM.md` §5) and the selfh.st ideas (`audits/selfhst-survey-2026-10-12/FELHOM.md` §6) | — | — | nothing changes; the research ages | — | | D8 | R-206 | Install DooPlex's Docker settings from git (restarts Docker), and prune the build cache weekly: yes or no? | **Yes, in a quiet window** | — | a rebuild of DooPlex still has the settings in git | — | | D9 | R-1062 | The N100's USB drive is also its whole-box backup drive. Split it in two anyway? | **No — keep one drive**; the lived-in box keeps its nightly whole-box backup | Split: the box loses its local whole-box backup (or add a second drive first) | it stays one drive; multi-drive tests stay on Tester 1 | — | -| D10 | R-1102 | Eight apps tell the household „starts with a known, shared password" about a login the box generated or the household typed. Drop that line on the eight (the login panel now says where the login is)? | **Drop it** — one catalog commit | Keep it with a new „hint" form: a controller change | the eight apps keep the misleading sentence at install and on the app page | — | ## Waiting on you — do +- **Nézd meg a demo-hp-n a Calibre-Web „Adatok és mentés" fülét és a Fájlkezelőt** (`felhom.enkisfelhom.hu`, controller 0.321.0, minden dobozon kiadva). A mentés egy részben, egyszerű szavakkal; a Fájlkezelő nem kér frissítést és nem ajánl „Install app"-ot. - **Look at `roms/roms/` on the N100's drive in the file manager (R-1099).** RomM now scans `roms//`. A game uploaded through RomM before today sits one folder deeper: if there are platform folders in `roms/roms/`, move them up into `roms/` and press „Scan” in RomM. If not: those games show under a platform called „roms”. - **Nézd meg a Calibre-Web oldalát a demo-hp-n** (`felhom.enkisfelhom.hu`, controller 0.319.0, minden dobozon kiadva). A fejléc, a fülsor és minden rész most külön panel (az „A” stílus). A belépési adatok egy panelben vannak, egymás mellett. A Calibre-Web jelszaváról a doboz nem tud, mert az alkalmazás előbb települt, mint hogy a sablonban jelszó lett volna — ezt most egy mondat mondja ki. Ha nem tetszik, írd meg, mit változtassak. Ha nem nézed meg: semmi nem romlik el. - **Discard two unclaimed boxes and delete Tester4** (hub, browser only): the build B scratch VMs registered as unclaimed boxes — registration ids 40 (`b43afa10…`) and 41 (`9c78ea30…`); both VMs are destroyed. Tester4 (the throwaway customer) is switched off the household way. If not: two dead entries stay in the Hosts list and Tester4 stays in Customers. diff --git a/documentation/architecture/02-controller-module-map.md b/documentation/architecture/02-controller-module-map.md index 0c0e8ac9..91f8ca50 100644 --- a/documentation/architecture/02-controller-module-map.md +++ b/documentation/architecture/02-controller-module-map.md @@ -542,6 +542,20 @@ database with no lock timeout), verifies new=200 and admin/admin refused, stores start. Spike: `audits/logins-folders-2026-10-16/P1b`. FileBrowser still asks for its own login the first time; one sign-in for both is R-1100. +**[DESIGN] A stored FileBrowser password is checked before it is offered (v0.321.0, R-1101).** In `generated`, the +page asks FileBrowser whether the stored value still logs in; only FileBrowser's own 401/403 counts as refused (a +FileBrowser that does not answer is not a refusal — an unproven refusal must not tell the household its password +changed). Refused → „A Fájlkezelőben megváltoztattad a jelszót, ezért a doboz már nem ismeri." and „Új jelszó +beállítása". The page no longer tells the household to change FileBrowser's password, and FileBrowser's own update +notice and „Install app" prompt are switched off in its config (the box updates it; it is opened from the dashboard). + +**[DESIGN] The Data tab's backup part is ONE section (v0.321.0).** „Biztonsági mentés": an „Éjszakai mentés" row, the +2nd copy's state as rows, what the 2nd copy is, its form. Plain words — no „off-drive", no „PBS", no +„DB/konfiguráció" (pinned by `TestAppData_OneBackupSection`). + +**[DESIGN] An installed app's install address goes to its settings tab (v0.321.0, R-1095)** — 302, GET only, like the +`/stacks/{name}/deploy` bridge. + **[DESIGN] „Fájlok ↗" — one of the app's folders, in FileBrowser.** The header button opens the folder with the library role, else the first declared folder, else (a media app) the first present drive's `media` folder; built only with `fileBrowserLink` (PathEscape). The data tab lists each drive's media folders the app uses (Sonarr `tv`, Radarr diff --git a/documentation/architecture/07-backup-architecture.md b/documentation/architecture/07-backup-architecture.md index de212402..311c681c 100644 --- a/documentation/architecture/07-backup-architecture.md +++ b/documentation/architecture/07-backup-architecture.md @@ -553,6 +553,13 @@ a normal night: nothing. A box off for weeks: one catch-up when it returns; the running all along. An upgrade from an older release: the ledger is seeded, the first missed night after it is the first one made up. +**[FACT, 2026-10-16, R-1097] An image's own anonymous VOLUME is in no backup.** The app backup takes the named volumes the +compose DECLARES (`backup.ResolveDockerVolumeNames`) and the drive folders it binds; a path the image keeps in its +`VOLUME` that the compose does not mount lands in an anonymous volume and is taken by nothing. Measured with RomM's saves +(`/romm/assets`) on demo-hp; fixed with a named volume, and catalog gate `image-volumes` keeps a measured registry of such +paths (`audits/burndown-apps-2026-10-16/P1/`). A survey of every running container on demo-hp found no other data path +in an anonymous volume (PrivateBin's are temp folders). + ### 6.2 Coverage per app class — and an unresolved count **[FACT]** Of 53 catalog templates, **52 keep data in Docker named volumes**; exactly **13** carry a diff --git a/documentation/audits/burndown-apps-2026-10-16/P1/anon-volumes-demo-hp.txt b/documentation/audits/burndown-apps-2026-10-16/P1/anon-volumes-demo-hp.txt new file mode 100644 index 00000000..8e173b69 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P1/anon-volumes-demo-hp.txt @@ -0,0 +1,2 @@ +romm rommapp/romm:5.3.1 ['/redis-data', '/romm'] +privatebin privatebin/pdo:2.0.6 ['/run', '/tmp', '/var/lib/nginx/tmp'] diff --git a/documentation/audits/burndown-apps-2026-10-16/P1/hub-romm-boxes.txt b/documentation/audits/burndown-apps-2026-10-16/P1/hub-romm-boxes.txt new file mode 100644 index 00000000..16e0c588 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P1/hub-romm-boxes.txt @@ -0,0 +1,7 @@ +customers reporting RomM telemetry, with their newest report (hub DB + WAL, read 2026-10-16): + demo-felhom rows 1 newest 2026-10-13 09:37:46 + demo-hp rows 4450 newest 2026-10-16 16:32:25 +customers reporting ANY app in the last 2 hours: + demo-felhom + demo-hp + tester-1 diff --git a/documentation/audits/burndown-apps-2026-10-16/P1/night-chain-log.txt b/documentation/audits/burndown-apps-2026-10-16/P1/night-chain-log.txt new file mode 100644 index 00000000..56249685 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P1/night-chain-log.txt @@ -0,0 +1,18 @@ +2026/10/16 13:47:23 main.go:814: [INFO] Self-update enabled (check every 6h, auto-update: false, auto-update time: 04:30) +2026/10/16 14:51:36 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.4:40328: [db-dump tier2 offsite update-leg] +2026/10/16 14:51:36 night_chain.go:84: [INFO] [night-chain] manual run of tonight's chain: dump → second drive → off-site → update leg +2026/10/16 14:51:36 night_chain.go:77: [INFO] [night-chain] db-dump: started +2026/10/16 14:53:42 night_chain.go:82: [INFO] [night-chain] db-dump: done in 2m6s +2026/10/16 14:53:42 night_chain.go:77: [INFO] [night-chain] tier2: started +2026/10/16 14:54:10 night_chain.go:82: [INFO] [night-chain] tier2: done in 28s +2026/10/16 14:54:10 night_chain.go:77: [INFO] [night-chain] offsite: started +2026/10/16 14:57:08 night_chain.go:82: [INFO] [night-chain] offsite: done in 2m58s +2026/10/16 14:57:08 night_chain.go:77: [INFO] [night-chain] update-leg: started +2026/10/16 14:57:08 unattended.go:270: [INFO] [update-leg] started (manual-chain): window 02:30, no step starts at or after 20:12 +2026/10/16 14:57:08 unattended.go:316: [INFO] [update-leg] kimai: skipped — held +2026/10/16 14:57:08 unattended.go:248: [INFO] [update-leg] update leg (manual-chain): done=0 undone=0 held=0 failed=0 skipped=1 in 0s [skipped: kimai=held] +2026/10/16 14:57:08 night_chain.go:82: [INFO] [night-chain] update-leg: done in 0s +2026/10/16 14:57:08 night_chain.go:93: [INFO] [night-chain] finished in 5m32s +romm Up About a minute (healthy) +romm-redis Up About a minute (healthy) +romm-db Up About a minute (healthy) diff --git a/documentation/audits/burndown-apps-2026-10-16/P1/p1-measure.txt b/documentation/audits/burndown-apps-2026-10-16/P1/p1-measure.txt new file mode 100644 index 00000000..2aa9d495 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P1/p1-measure.txt @@ -0,0 +1,40 @@ +== mounts of the running romm container +volume -> /redis-data +volume -> /romm +volume romm_romm_config -> /romm/config +bind -> /romm/resources +bind -> /romm/library/bios +bind -> /romm/library/roms + +== compose volumes +167:volumes: +168- romm_config: +169- romm_db_data: +170- romm_redis_data: +171- +172-networks: +173- traefik-public: +18:# Cover art etc → ${HDD_PATH}/appdata/romm/resources (app-internal, NOT browsable) +29: image: rommapp/romm:5.3.1@sha256:0d66b4ea152a237c7f24b95e87459f46d3c596a7c0b35a82f26bc971487278b3 +40: if [ ! -f /romm/config/config.yml ]; then +42: cat > /romm/config/config.yml << 'CONF' +80: # on demo-hp 9201 (R-1089, felhom.eu audits/romm-ghost-2026-10-16/A1). Now roms// IS +83: - ${USERDATA_PATH}/roms:/romm/library/roms +84: - ${USERDATA_PATH}/roms-bios:/romm/library/bios +85: - ${HDD_PATH}/appdata/romm/resources:/romm/resources +86: - romm_config:/romm/config +== what earlier app backups of romm hold +/mnt/felhom-drives/hdd_1/kept/romm/2026-10-16_113539/unit/volume-dumps +total 203084 +drwxr-xr-x 2 root root 4096 Oct 4 05:35 . +drwxr-xr-x 5 root root 4096 Oct 4 11:18 .. +-rw-r--r-- 1 root root 2560 Oct 4 05:35 romm_romm_config.tar +-rw-r--r-- 1 root root 163786752 Oct 4 05:35 romm_romm_db_data.tar +-rw-r--r-- 1 root root 44148736 Oct 4 05:35 romm_romm_redis_data.tar +== anonymous volume contents +assets +config +library +resources +/var/lib/docker/volumes/567f3e5915421b542fc2e67d3195d03b32ef07ef79f2851c6198653cf29e00f6/_data/assets +28K /var/lib/docker/volumes/567f3e5915421b542fc2e67d3195d03b32ef07ef79f2851c6198653cf29e00f6/_data diff --git a/documentation/audits/burndown-apps-2026-10-16/P1/romm-saves.txt b/documentation/audits/burndown-apps-2026-10-16/P1/romm-saves.txt new file mode 100644 index 00000000..beee7f63 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P1/romm-saves.txt @@ -0,0 +1,49 @@ +== 1. sync the catalog +sync -> 200 {'ok': True, 'data': {'ok': True, 'updated': ['code-server', 'gokapi', 'grafana', 'kimai', 'nextcloud', 'nzbget', 'prowlarr', 'romm', 'transmission'], 'message': 'Sablonok frissítve — frissítve: code- +applied compose names romm_assets: 3 +== 2. restart RomM (Újraindítás) +restart -> 200 {'ok': True, 'data': {'state': 'starting'}, 'message': 'Stack romm restart requested — state now: starting'} +state: running +mounts after restart: +volume -> /redis-data +volume -> /romm +volume romm_romm_assets -> /romm/assets +volume romm_romm_config -> /romm/config +bind -> /romm/resources +bind -> /romm/library/bios +bind -> /romm/library/roms +== 3. a save where RomM writes saves (dummy bytes, as the app's own user) +drwxr-xr-x 3 root root 4096 Oct 16 16:51 .. +-rw-r--r-- 1 root root 32768 Oct 16 16:51 r1097-proof.sav +4a55d00c3ee6bccd55649e48fd19d84703f755f84474ac0b6a41299b114ee6a5 /romm/assets/romm/users/1/saves/gba/r1097-proof/r1097-proof.sav +== 4. the by-day backup (night chain) +night-chain -> 202 {'data': {'legs': ['db-dump', 'tier2', 'offsite', 'update-leg']}, 'message': 'started', 'ok': True} +night chain last line: 2026/10/16 14:57:08 night_chain.go:93: [INFO] [night-chain] finished in 5m32s after 342s +romm volume dumps now: /mnt/felhom-drives/hdd_1/backups/primary/romm/volume-dumps/: +total 176584 +drwxr-xr-x 2 root root 4096 Oct 16 14:56 . +drwxr-xr-x 5 root root 4096 Oct 16 14:56 .. +-rw-r--r-- 1 root root 37888 Oct 16 14:56 romm_romm_assets.tar +-rw-r--r-- 1 root root 2560 Oct 16 14:56 romm_romm_config.tar +-rw-r--r-- 1 root root 162300416 Oct 16 14:56 romm_romm_db_data.tar +-rw-r--r-- 1 root root 18463744 Oct 16 14:56 romm_romm_redis_data.tar +/mnt/felhom-drives/hdd_1/backups/primary/romm/volume-dumps/romm_romm_assets.tar + +== 5. remove RomM, KEEPING its backups +remove -> 200 {'ok': True, 'data': {'removed': 'romm', 'volumes_removed': ['romm_romm_assets', 'romm_romm_config', 'romm_romm_db_data', 'romm_romm_redis_data'], 'hdd_paths_removed': [], 'hdd_paths_preserved': ['/mn +volumes left: romm_romm_redis_data.pre-aof-fix-20260924T135937Z +== 6. restore from the backups page +restore: {'ok': True, 'snapshot_id': 'helyi', 'http': 'HTTP/2 302', 'seconds': 77.0} +state: running +mounts after restore: +volume -> /redis-data +volume -> /romm +volume romm_romm_assets -> /romm/assets +volume romm_romm_config -> /romm/config +bind -> /romm/resources +bind -> /romm/library/bios +bind -> /romm/library/roms +drwxr-xr-x 3 root root 4096 Oct 16 16:58 .. +-rw-r--r-- 1 root root 32768 Oct 16 16:51 r1097-proof.sav +4a55d00c3ee6bccd55649e48fd19d84703f755f84474ac0b6a41299b114ee6a5 /romm/assets/romm/users/1/saves/gba/r1097-proof/r1097-proof.sav +== VERDICT: the save came back byte-identical: True diff --git a/documentation/audits/burndown-apps-2026-10-16/P7-pi-fleet.txt b/documentation/audits/burndown-apps-2026-10-16/P7-pi-fleet.txt new file mode 100644 index 00000000..c6135fab --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/P7-pi-fleet.txt @@ -0,0 +1,6 @@ +tables with customer: ['customer_notifications', 'customer_configs', 'customer_claims', 'customer_resets'] +every customer's NEWEST report (hub DB + WAL): + demo-hp 0.320.0 2026-10-16 14:58:09 + tester-1 0.320.0 2026-10-16 14:49:33 + demo-felhom 0.320.0 2026-10-16 14:49:28 + Tester-2 0.292.0 2026-10-04 18:06:24 diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/.profile/Default/Code Cache/js/index-dir/the-real-index b/documentation/audits/burndown-apps-2026-10-16/browser/.profile/Default/Code Cache/js/index-dir/the-real-index new file mode 100644 index 00000000..53d56b28 Binary files /dev/null and b/documentation/audits/burndown-apps-2026-10-16/browser/.profile/Default/Code Cache/js/index-dir/the-real-index differ diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-data.png b/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-data.png new file mode 100644 index 00000000..bbb6f262 Binary files /dev/null and b/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-data.png differ diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-install.png b/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-install.png new file mode 100644 index 00000000..4b7e1b96 Binary files /dev/null and b/documentation/audits/burndown-apps-2026-10-16/browser/apps-calibre-web-install.png differ diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/apps-filebrowser.png b/documentation/audits/burndown-apps-2026-10-16/browser/apps-filebrowser.png new file mode 100644 index 00000000..9d94840d Binary files /dev/null and b/documentation/audits/burndown-apps-2026-10-16/browser/apps-filebrowser.png differ diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/filebrowser-sidebar.png b/documentation/audits/burndown-apps-2026-10-16/browser/filebrowser-sidebar.png new file mode 100644 index 00000000..b5e0f522 Binary files /dev/null and b/documentation/audits/burndown-apps-2026-10-16/browser/filebrowser-sidebar.png differ diff --git a/documentation/audits/burndown-apps-2026-10-16/browser/report.json b/documentation/audits/burndown-apps-2026-10-16/browser/report.json new file mode 100644 index 00000000..8705b6a7 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/browser/report.json @@ -0,0 +1,60 @@ +rm: cannot remove '/mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/burndown-apps-2026-10-16/browser/.profile/Default': Directory not empty +after login url: /launcher +{ + "/apps/calibre-web/data": { + "path": "/apps/calibre-web/data", + "backupSections": 1, + "rows": [ + "nightly", + "second", + "target" + ], + "offDrivePBS": false, + "valueFont": "\"Plus Jakarta Sans\"", + "exportToggle": [ + "Megjelenítés", + "Jelszó mutatása" + ], + "fbReveal": false, + "fbStale": false, + "changeItLine": false, + "scrollWidth": 1270, + "console": [] + }, + "/apps/filebrowser": { + "path": "/apps/filebrowser", + "backupSections": 0, + "rows": [], + "offDrivePBS": false, + "valueFont": null, + "exportToggle": null, + "fbReveal": true, + "fbStale": false, + "changeItLine": false, + "scrollWidth": 1280, + "console": [] + }, + "/apps/calibre-web/install": { + "path": "/apps/calibre-web/settings", + "backupSections": 0, + "rows": [], + "offDrivePBS": false, + "valueFont": null, + "exportToggle": null, + "fbReveal": false, + "fbStale": false, + "changeItLine": false, + "scrollWidth": 1270, + "console": [] + }, + "filebrowser": { + "path": "/files/Beolvas%C3%A1s", + "updateBanner": false, + "installApp": false, + "releaseBanners": 0, + "globals": { + "disablePWAInstall": true, + "updateAvailable": "" + } + } +} diff --git a/documentation/audits/burndown-apps-2026-10-16/live/d10-check.txt b/documentation/audits/burndown-apps-2026-10-16/live/d10-check.txt new file mode 100644 index 00000000..e2ac55a7 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/live/d10-check.txt @@ -0,0 +1,22 @@ +control: bookstack (keeps default_creds): + 302 known-login markers: 0 +grafana 200 known-login=0 answer= +grafana/install 200 known-login=0 answer= +gokapi 200 known-login=0 answer= +gokapi/install 200 known-login=0 answer= +kimai 200 known-login=0 answer=data-login-answer="fields" +kimai/install 302 known-login=0 answer= +code-server 200 known-login=0 answer= +code-server/install 200 known-login=0 answer= +nzbget 200 known-login=0 answer= +nzbget/install 200 known-login=0 answer= +nextcloud 200 known-login=0 answer= +nextcloud/install 200 known-login=0 answer= +transmission 200 known-login=0 answer= +transmission/install 200 known-login=0 answer= +prowlarr 200 known-login=0 answer= +prowlarr/install 200 known-login=0 answer= +control bookstack: 200 known-login=0 alapertelmezett=0 +control wger: 200 known-login=0 alapertelmezett=0 +control mealie: 200 known-login=0 alapertelmezett=0 +control claper: 200 known-login=0 alapertelmezett=0 diff --git a/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-agent.txt b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-agent.txt new file mode 100644 index 00000000..cf1d20cd --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-agent.txt @@ -0,0 +1,3 @@ + FAIL unreachable (000) -> refused (exit 1) + FAIL server error (500) -> refused (exit 1) +2 of 5 cases FAILED diff --git a/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-catalog.txt b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-catalog.txt new file mode 100644 index 00000000..cb8156a9 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-catalog.txt @@ -0,0 +1,4 @@ + XX DECOY: the PARENT /romm mounted rc=0 (expected 1) + XX DECOY: a CHILD /romm/assets/x mounted rc=0 (expected 1) +AssertionError: Lists differ: [' default_creds: "admin / x"\n'] != [] +FAILED (failures=1) diff --git a/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-controller.txt b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-controller.txt new file mode 100644 index 00000000..6340aa4f --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-controller.txt @@ -0,0 +1,26 @@ +## refused = any non-200 (FileBrowser down reads as refused) +=== RUN TestFileBrowserStoredPassword_RefusedOnlyOnA401 + filebrowser_reset_test.go:182: FileBrowser down reads as refused — an unproven refusal must not tell the household its password changed +--- FAIL: TestFileBrowserStoredPassword_RefusedOnlyOnA401 (0.00s) +## no stored-password check on the page +=== RUN TestFileBrowserLogin_StoredPasswordRefusedOffersReset + logins_folders_test.go:365: stale: the page still offers the reveal of a password FileBrowser refuses +--- FAIL: TestFileBrowserLogin_StoredPasswordRefusedOffersReset (0.08s) +## the change-it line back on FileBrowser +=== RUN TestFileBrowserLogin_StoredPasswordRefusedOffersReset + logins_folders_test.go:380: FileBrowser still says to change the password after the first login (R-1101) +--- FAIL: TestFileBrowserLogin_StoredPasswordRefusedOffersReset (0.08s) +## no redirect for an installed app's /install +=== RUN TestInstallAddress_InstalledRedirectsToSettings + logins_folders_test.go:432: installed: 200 → "", want 302 → /apps/calibre-web/settings +--- FAIL: TestInstallAddress_InstalledRedirectsToSettings (0.08s) +## off-drive back in a Data tab string (hu) +=== RUN TestAppData_OneBackupSection +## a second backup section +=== RUN TestAppData_OneBackupSection + logins_folders_test.go:408: hu: 2 backup sections, want 1 + logins_folders_test.go:408: en: 2 backup sections, want 1 +--- FAIL: TestAppData_OneBackupSection (0.10s) +## (redo) off-drive/PBS back in the always-shown 2nd-copy text (hu) + logins_folders_test.go:415: hu: off-drive/PBS on the Data tab ("off-drive") +--- FAIL: TestAppData_OneBackupSection (0.09s) diff --git a/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-hubbuild.txt b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-hubbuild.txt new file mode 100644 index 00000000..b1d0877e --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/redproof/redproof-hubbuild.txt @@ -0,0 +1,6 @@ + FAIL --push, tag present -> refuses: exit 0, want 1 + FAIL --push, tag absent -> proceeds: no 'is not present in the registry' + FAIL --multiarch, tag present -> refuses: exit 0, want 1 + FAIL --push, registry unreachable -> refuses: exit 0, want 1 + FAIL --push, auth failure -> refuses: exit 0, want 1 +5 of 6 cases FAILED diff --git a/documentation/audits/burndown-apps-2026-10-16/release/containers.txt b/documentation/audits/burndown-apps-2026-10-16/release/containers.txt new file mode 100644 index 00000000..39d258f6 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/containers.txt @@ -0,0 +1,15 @@ +== tester-1 +unhealthy=0 restarting=0 exited=0 created=0 +controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 18 seconds (healthy) +filebrowser Up 16 seconds (healthy) +2 +== demo-hp +unhealthy=0 restarting=0 exited=0 created=0 +controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 21 seconds (healthy) +filebrowser Up 3 minutes (healthy) +2 +== demo-felhom +unhealthy=0 restarting=0 exited=0 created=0 +controller gitea.dooplex.hu/admin/felhom-controller:0.321.0 Up 25 seconds (healthy) +filebrowser Up 23 seconds (healthy) +2 diff --git a/documentation/audits/burndown-apps-2026-10-16/release/customer-floors-cleared.txt b/documentation/audits/burndown-apps-2026-10-16/release/customer-floors-cleared.txt new file mode 100644 index 00000000..8600ce1c --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/customer-floors-cleared.txt @@ -0,0 +1,3 @@ +tester-1 Location: /customers/tester-1?flash=floor_set +demo-hp Location: /customers/demo-hp?flash=floor_set +demo-felhom Location: /customers/demo-felhom?flash=floor_set diff --git a/documentation/audits/burndown-apps-2026-10-16/release/customer-floors.txt b/documentation/audits/burndown-apps-2026-10-16/release/customer-floors.txt new file mode 100644 index 00000000..eea08604 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/customer-floors.txt @@ -0,0 +1,3 @@ +demo-hp Location: /customers/demo-hp?flash=floor_set +tester-1 Location: /customers/tester-1?flash=floor_set +demo-felhom Location: /customers/demo-felhom?flash=floor_set diff --git a/documentation/audits/burndown-apps-2026-10-16/release/dashboards.txt b/documentation/audits/burndown-apps-2026-10-16/release/dashboards.txt new file mode 100644 index 00000000..7635a913 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/dashboards.txt @@ -0,0 +1,3 @@ +felhom.enkisfelhom.hu 200 +felhom.enkicsifelhom.hu 200 +felhom.demo-felhom.eu 200 diff --git a/documentation/audits/burndown-apps-2026-10-16/release/global-floor.txt b/documentation/audits/burndown-apps-2026-10-16/release/global-floor.txt new file mode 100644 index 00000000..f3704e94 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/global-floor.txt @@ -0,0 +1,3 @@ +17:22:20 +HTTP/1.1 303 See Other +Location: /configuration?flash=floor_set diff --git a/documentation/audits/burndown-apps-2026-10-16/release/https-only.txt b/documentation/audits/burndown-apps-2026-10-16/release/https-only.txt new file mode 100644 index 00000000..ad440aff --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/https-only.txt @@ -0,0 +1,40 @@ +control NEGATIVE (a stub answering 200 over plain http): convicted, as it must (200 -) +== tester-1 (enkicsifelhom.hu): 13 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://media.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://media.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://plex.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://plex.enkicsifelhom.hu/r1033-check?q=1 + OK http://prowlarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://prowlarr.enkicsifelhom.hu/r1033-check?q=1 + OK http://radarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://radarr.enkicsifelhom.hu/r1033-check?q=1 + OK http://sonarr.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://sonarr.enkicsifelhom.hu/r1033-check?q=1 + OK http://torrent.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://torrent.enkicsifelhom.hu/r1033-check?q=1 + OK http://transmission.enkicsifelhom.hu/r1033-check?q=1 -> 301 https://transmission.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 diff --git a/documentation/audits/burndown-apps-2026-10-16/release/hubdb-floors-after.txt b/documentation/audits/burndown-apps-2026-10-16/release/hubdb-floors-after.txt new file mode 100644 index 00000000..70914086 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/release/hubdb-floors-after.txt @@ -0,0 +1,7 @@ +FIRST READ (hub.db, -wal, -shm copied ONE BY ONE with kubectl exec cat) — STALE: +('min_controller_version', '0.320.0', '2026-10-16 13:48:34') +('min_controller_version_declared_min_agent', '0.320.0=0.158.1', '2026-10-16 13:48:34') +SECOND READ (one tar of the three files) — 17:23: +('min_controller_version', '0.321.0', '2026-10-16 15:22:20') +('min_controller_version_declared_min_agent', '0.321.0=0.158.1', '2026-10-16 15:22:20') +THIRD CHANNEL — GET /configuration with CC's token: the page shows 0.321.0 diff --git a/documentation/audits/burndown-apps-2026-10-16/tools/cdp_burndown.py b/documentation/audits/burndown-apps-2026-10-16/tools/cdp_burndown.py new file mode 100644 index 00000000..36838a9b --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/tools/cdp_burndown.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +"""v0.321.0 apps-burndown browser check (Data tab, FileBrowser page and FileBrowser itself, an install address). Minimal CDP client: log in to the controller through its PUBLIC address in headless Chrome, open each +app-page tab, assert computed styles, collect console errors, save screenshots. The password is read +from a 0600 file and only ever sent inside a CDP message; it is never printed.""" +import base64, json, os, socket, struct, subprocess, sys, time, urllib.request + +CHROME = os.path.expanduser('~/.cache/ms-playwright/chromium_headless_shell-1243/chrome-headless-shell-linux64/chrome-headless-shell') +BASE = sys.argv[1] # https://felhom.enkisfelhom.hu +PWFILE = sys.argv[2] # the controller password (0600 file) +FBPWFILE = sys.argv[3] # FileBrowser's admin password (0600 file) — for the cold „Fajlok" click +OUT = sys.argv[4] +PORT = 9333 +os.makedirs(OUT, exist_ok=True) +prof = os.path.join(OUT, '.profile') +proc = subprocess.Popen([CHROME, f'--remote-debugging-port={PORT}', '--no-sandbox', f'--user-data-dir={prof}', + '--window-size=1280,900', 'about:blank'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) +try: + for _ in range(50): + try: + tabs = json.load(urllib.request.urlopen(f'http://127.0.0.1:{PORT}/json')) + break + except Exception: + time.sleep(0.2) + ws_url = [t for t in tabs if t['type'] == 'page'][0]['webSocketDebuggerUrl'] + host, path = ws_url[len('ws://'):].split('/', 1) + h, p = host.split(':') + sock = socket.create_connection((h, int(p))) + key = base64.b64encode(os.urandom(16)).decode() + sock.send(f'GET /{path} HTTP/1.1\r\nHost: {host}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n\r\n'.encode()) + buf = b'' + while b'\r\n\r\n' not in buf: + buf += sock.recv(4096) + st = {'pending': buf.split(b'\r\n\r\n', 1)[1]} + + def send(obj): + data = json.dumps(obj).encode() + hdr = bytearray([0x81]) + n = len(data) + if n < 126: hdr.append(0x80 | n) + elif n < 65536: hdr.append(0x80 | 126); hdr += struct.pack('>H', n) + else: hdr.append(0x80 | 127); hdr += struct.pack('>Q', n) + mask = os.urandom(4); hdr += mask + sock.sendall(bytes(hdr) + bytes(b ^ mask[i % 4] for i, b in enumerate(data))) + + def recv_exact(n): + while len(st['pending']) < n: + st['pending'] += sock.recv(1 << 20) + out, st['pending'] = st['pending'][:n], st['pending'][n:] + return out + + def recv(): + msg = b'' + while True: + b0, b1 = recv_exact(2) + n = b1 & 0x7f + if n == 126: n = struct.unpack('>H', recv_exact(2))[0] + elif n == 127: n = struct.unpack('>Q', recv_exact(8))[0] + msg += recv_exact(n) + if b0 & 0x80: return json.loads(msg) + + console = [] + mid = [0] + def call(method, **params): + mid[0] += 1; i = mid[0] + send({'id': i, 'method': method, 'params': params}) + while True: + m = recv() + if m.get('method') == 'Runtime.exceptionThrown': + console.append(('exception', m['params']['exceptionDetails'].get('text', ''), m['params']['exceptionDetails'].get('exception', {}).get('description', '')[:200])) + elif m.get('method') == 'Runtime.consoleAPICalled' and m['params']['type'] in ('error', 'warning'): + console.append((m['params']['type'], ' '.join(str(a.get('value', a.get('description', ''))) for a in m['params']['args'])[:200])) + elif m.get('method') == 'Log.entryAdded' and m['params']['entry']['level'] in ('error', 'warning'): + e = m['params']['entry']; console.append(('log-' + e['level'], e.get('text', '')[:200], e.get('url', ''))) + if m.get('id') == i: + if 'error' in m: raise RuntimeError(f'{method}: {m["error"]}') + return m.get('result', {}) + + def ev(expr): + r = call('Runtime.evaluate', expression=expr, returnByValue=True, awaitPromise=True) + return r.get('result', {}).get('value') + + def goto(url): + call('Page.navigate', url=url) + for _ in range(100): + time.sleep(0.2) + if ev('document.readyState') == 'complete': + break + time.sleep(1.5) # images' onerror, layout + + for d in ('Page', 'Runtime', 'Log', 'Network'): + call(d + '.enable') + pw = open(PWFILE).read().strip() + goto(BASE + '/login') + ev("(function(){var f=document.querySelector('input[type=password]'); f.value=%s; f.form.submit(); return 1})()" % json.dumps(pw)) + del pw + time.sleep(3) + print('after login url:', ev('location.pathname')) + console.clear() # the login page is not under test + + def shot(name): + s = call('Page.captureScreenshot', format='png', captureBeyondViewport=True) + open(os.path.join(OUT, name + '.png'), 'wb').write(base64.b64decode(s['data'])) + + report = {} + for p in ['/apps/calibre-web/data', '/apps/filebrowser', '/apps/calibre-web/install']: + n0 = len(console) + goto(BASE + p) + report[p] = ev("""(function(){ return {path: location.pathname, + backupSections: document.querySelectorAll('[data-backup-section]').length, + rows: [].map.call(document.querySelectorAll('[data-backup-row]'), function(e){return e.getAttribute('data-backup-row')}), + offDrivePBS: /off-drive|\\bPBS\\b/i.test(document.body.innerText), + valueFont: (function(){var v=document.querySelector('[data-backup-row] .app-field-value'); return v?getComputedStyle(v).fontFamily.split(',')[0]:null})(), + exportToggle: (function(){var b=document.getElementById('exportPwToggle'); return b?[b.textContent.trim(), b.getAttribute('aria-label')]:null})(), + fbReveal: !!document.querySelector('#initcred-reveal'), fbStale: !!document.querySelector('[data-fb-stale]'), + changeItLine: document.body.innerText.indexOf('hagyd figyelmen') >= 0, + scrollWidth: document.documentElement.scrollWidth};})()""") + report[p]['console'] = console[n0:] + shot(p.strip('/').replace('/', '-')) + # FileBrowser itself, logged in with the box's own password: no update banner, no install-app prompt. + fbpw = open(FBPWFILE).read().strip() + call('Network.clearBrowserCookies') + goto(BASE.replace('://felhom.', '://files.') + '/') + ev("""(function(pw){var u=document.querySelector('input[type=text],input[name=username],input#username'); + var p=document.querySelector('input[type=password]'); if(!u||!p) return 0; + function set(el,v){var d=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value'); d.set.call(el,v); el.dispatchEvent(new Event('input',{bubbles:true}));} + set(u,'admin'); set(p,pw); var b=document.querySelector('form button[type=submit], form input[type=submit], button.button--blue'); + if(b){b.click()} else {p.form && p.form.requestSubmit && p.form.requestSubmit()} return 1})(%s)""" % json.dumps(fbpw)) + del fbpw + time.sleep(5) + report['filebrowser'] = ev("""(function(){ var t=document.body.innerText; + return {path: location.pathname, updateBanner: /update is available/i.test(t), installApp: /install app/i.test(t), + releaseBanners: document.querySelectorAll('.release-banner').length, + globals: (function(){ try { var g=window.globalVars||{}; return {disablePWAInstall: g.disablePWAInstall, updateAvailable: g.updateAvailable}; } catch(e) { return null; } })()};})()""") + shot('filebrowser-sidebar') + print(json.dumps(report, indent=1, ensure_ascii=False)) +finally: + proc.terminate() + subprocess.run(['rm', '-rf', prof]) diff --git a/documentation/audits/burndown-apps-2026-10-16/tools/romm_saves.py b/documentation/audits/burndown-apps-2026-10-16/tools/romm_saves.py new file mode 100644 index 00000000..c300db90 --- /dev/null +++ b/documentation/audits/burndown-apps-2026-10-16/tools/romm_saves.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +"""romm_saves.py — R-1097 proof on demo-hp 9201 through the product's endpoints (box_walk helpers, TARGET=9201). +Sync the catalog, restart RomM (the applied compose gains romm_assets), write a save where RomM writes saves, run the +by-day night chain (the sanctioned backup trigger), remove RomM KEEPING its backups, restore from the backups page, +and read the save back. Env: SC (0600 scratch with .ctlpw), EV. The save is a dummy file; no secret is printed.""" +import os, sys, time, hashlib +sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") +import box_walk as w +APP = "romm" +SAVE_DIR = "/romm/assets/romm/users/1/saves/gba/r1097-proof" +SAVE = SAVE_DIR + "/r1097-proof.sav" +log = open(os.path.join(w.EV, "romm-saves.txt"), "a", buffering=1) +def say(*a): + w.say(*a); log.write(" ".join(map(str, a)) + "\n") +def mounts(): + return w.guest("docker inspect romm --format '{{range .Mounts}}{{.Type}} {{.Name}} -> {{.Destination}}{{println}}{{end}}' " + "| sed -E 's/volume [0-9a-f]{64}/volume /'").strip() +def save_state(): + return w.guest(f"docker exec romm sh -c 'ls -la {SAVE_DIR} 2>&1; sha256sum {SAVE} 2>&1' | tail -3").strip() + +w.login() +say("== 1. sync the catalog") +code, d = w.ctl("POST", "/api/sync"); say("sync ->", code, str(d)[:200]) +say("applied compose names romm_assets:", w.guest("grep -c romm_assets /opt/docker/stacks/romm/docker-compose.yml").strip()) +say("== 2. restart RomM (Újraindítás)") +code, d = w.ctl("POST", f"/api/stacks/{APP}/restart"); say("restart ->", code, str(d)[:160]) +for _ in range(40): + time.sleep(5) + if w.stack(APP).get("state") == "running": + break +say("state:", w.stack(APP).get("state")); say("mounts after restart:\n" + mounts()) +say("== 3. a save where RomM writes saves (dummy bytes, written with docker exec into the running container)") +w.guest(f"docker exec romm sh -c 'mkdir -p {SAVE_DIR} && head -c 32768 /dev/urandom > {SAVE}'") +before = save_state(); say(before) +say("== 4. the by-day backup (night chain)") +t0 = time.time(); code, d = w.ctl("POST", "/api/debug/backup/night-chain"); say("night-chain ->", code, str(d)[:160]) +for _ in range(180): + time.sleep(10) + tail = w.guest("docker logs --since 60m felhom-controller 2>&1 | grep 'night-chain' | tail -1").strip() + if "finished in" in tail or "REFUSED" in tail: + break +say("night chain last line:", tail[-220:], f"after {round(time.time()-t0)}s") +say("romm volume dumps now:", w.guest("ls -la /mnt/felhom-drives/hdd_1/backups/*/romm/volume-dumps/ /mnt/felhom-drives/hdd_1/*/romm/recovery-unit/volume-dumps/ 2>/dev/null; find /mnt/felhom-drives/hdd_1 -name 'romm_romm_assets.tar' -newer /proc/1 2>/dev/null | head -5")) +say("== 5. remove RomM, KEEPING its backups") +w.ctl("POST", f"/api/stacks/{APP}/stop"); time.sleep(10) +code, d = w.ctl("POST", f"/api/stacks/{APP}/remove", {"remove_hdd_data": False, "remove_backups": False}) +say("remove ->", code, str(d)[:200]) +time.sleep(5) +say("volumes left:", w.guest("docker volume ls --format '{{.Name}}' | grep -i romm").strip() or "(none)") +say("== 6. restore from the backups page") +r = w.restore(APP); say("restore:", {k: r.get(k) for k in ("ok", "snapshot_id", "http", "seconds")}) +for _ in range(60): + time.sleep(5) + if w.stack(APP).get("state") == "running": + break +say("state:", w.stack(APP).get("state")); say("mounts after restore:\n" + mounts()) +after = save_state(); say(after) +same = before.split()[-2:] == after.split()[-2:] +say("== VERDICT: the save came back byte-identical:", same) diff --git a/documentation/backlog/CLOSED-ITEMS.md b/documentation/backlog/CLOSED-ITEMS.md index fa0e2e18..c4a9227d 100644 --- a/documentation/backlog/CLOSED-ITEMS.md +++ b/documentation/backlog/CLOSED-ITEMS.md @@ -26,6 +26,16 @@ --- +## 2026-10-16 — apps burndown (controller v0.321.0, catalog `326f846`, agent script `dd8b2ac`, hub build script) + +| Row | What | Closed | Evidence | +|---|---|---|---| +| **R-1097** | **RomM keeps the household's saves in an anonymous Docker volume, outside the backup.** (P3) | CLOSED 2026-10-16 — **measured first** on demo-hp 9201: every RomM backup held romm_config, romm_db_data, romm_redis_data and no saves; the app backup takes only compose-declared named volumes (`backup.ResolveDockerVolumeNames`). Catalog `326f846`: `romm_assets:/romm/assets` in the compose and both `steps/` files; gate `image-volumes` (measured registry, decoys, red-proved). **Proven:** a save written into `/romm/assets` → the by-day night chain wrote `romm_romm_assets.tar` → RomM removed (keeping backups, the volume went) → restored from the backups page → the save back byte-identical (sha256). No one-time copy: operator 2026-10-16, no RomM saves worth keeping; the hub shows RomM only on demo-hp. | `audits/burndown-apps-2026-10-16/P1/` | +| **R-1102** | **Eight apps said „starts with a known, shared password" about a generated or typed login.** (P3) | CLOSED 2026-10-16 — operator decision **D10 = A**: `app_info.default_creds` removed (hu + en) from grafana, gokapi, kimai, code-server, nzbget, nextcloud, transmission, prowlarr; copy freeze re-captured with the reason; `D10NoFalseKnownLogin` red-proved. **Live** on demo-hp: none of the eight shows the line on its app page or install page; the login panel gives its answer. **Reasoning kept:** the user name „admin" stays in the first steps of grafana, gokapi, nzbget, transmission; no live control exists any more (these eight were the only apps that showed the line) — `known_login_test.go` is the mechanism control. | `audits/burndown-apps-2026-10-16/live/d10-check.txt` | +| **R-1101** | **FileBrowser's stored password went stale when changed in FileBrowser, and the page said to change it.** (P4) | CLOSED 2026-10-16 — **controller v0.321.0**: the „change it" line is gone for FileBrowser; in `generated` the page asks FileBrowser first (only its own 401/403 counts; not answering is not a refusal) and, refused, says so and offers „Új jelszó beállítása", which accepts that case. Red-proved both ways. Also: FileBrowser's own update banner and „Install app" prompt switched off in the rendered config (`disableUpdateCheck`, `disablePWAInstall`), checked in the browser. | `audits/burndown-apps-2026-10-16/browser/report.json` | +| **R-1095** | **`/apps/{slug}/install` still opened for an installed app.** (P4) | CLOSED 2026-10-16 — **controller v0.321.0**: GET answers 302 to `/apps/{slug}/settings` (POST untouched); red-proved; **live** in the browser. The installed-only template branches stay — R-1103. | `audits/burndown-apps-2026-10-16/browser/report.json` | +| **R-1098** | **The hub build did not refuse a version already in the registry.** (P3) | CLOSED 2026-10-16 — `scripts/build-hub.sh` v1.25.0 refuses before the build when the tag exists, and when the registry cannot say (the controller `build.sh` rule); `scripts/test_build_hub_push_once.sh`, 6 cases, red-proved (5 fail with the check removed). The agent's `release-agent.sh` had the check but let an unreachable registry through: now only a clean 404 proceeds (`test_release_push_once.sh`, red-proved). No hub image, no deploy. | `audits/burndown-apps-2026-10-16/redproof/` | + ## 2026-10-16 — RomM, Ghost, withdrawn controller builds (catalog `fb18471`, hub v0.155.0) | Row | What | Closed | Evidence | diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 52eb509a..9be260d3 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -124,7 +124,7 @@ stopping line that lies. | **R-494** | Install & onboarding | P4 | **NARROWED 2026-09-14 by operator ruling → [P3-LOW] the hub COULD create the tunnel at customer creation, for a domain already on Cloudflare. Not blocking: every customer has their own domain and the operator creates the tunnel per day-0 A.1 (`architecture/01-topology-and-trust.md`).** *Original finding, kept:* **[P1-HIGH] A new customer's dashboard has NO reachable address unless the operator hand-makes a Cloudflare tunnel — the link in the setup-code mail is dead.** MEASURED 2026-09-14 on a fresh install from the public ISO (drill intervention **I1**): the claim mail points at `https://felhom.drill0242.felhom.eu`; that name has **no A and no AAAA** record (`dig @1.1.1.1`, control `felhom.enkisfelhom.hu` resolves); the hub has **no tunnel- or DNS-creation code** (`hub/internal/cloudflare/` holds only geo-rule removal; `cf_tunnel_token` is a pasted, optional form field, `configs.go:1478`) — day-0 runbook A.1 makes it a manual Cloudflare-dashboard step that nothing on the customer-create page asks for; the box's own split-horizon resolver on the appliance LAN IP answered `google.com` but not the dashboard name at 13:27:39Z; the agent applied the record at **13:27:44Z** (`lanresolver: applied split-horizon record … ip=192.168.0.158`, 3 m 46 s after the controller started), so the box CAN answer the name — **but only to a device that uses the box as its DNS server, and no document, screen or mail tells a household to do that**; the router and the installer-offered DNS answer nothing. The page was reachable only at the guest's LAN address with the name forced (`curl --resolve …:443:192.168.0.158`). **A volunteer could not have done that.** **What it needs:** an operator ruling — the hub creates the tunnel and DNS at customer creation, or the product gives a household a LAN address that works with no DNS change. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: operator-side automation; the operator creates the tunnel by hand per the day-0 runbook.** | — | — | CC | | **R-504** | Install & onboarding | P4 | **[P3-LOW] `iso.felhom.eu` cannot show an index page on its own — its root returns 404, and the download page lives on the website instead.** MEASURED 2026-09-14: `https://iso.felhom.eu/` and `/index.html` → 404; only named objects answer. The host is an R2 bucket behind a custom domain; whether R2 would serve an uploaded `index.html` at `/` was **not measured** (uploading anything to the public bucket is a publication). The ISO v1.27.0 task puts the Hungarian download page at `felhom.eu/letoltes` (published with the ISO, after the operator's yes). **Remaining:** a redirect from `iso.felhom.eu/` to that page needs a Cloudflare rule the session has no credential for. | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (Cloudflare rule)** **Re-ranked 2026-10-03: P3→P4: households are sent to the website's download page; the bare address is cosmetic.** | — | — | operator | -## Apps & catalog — 25 rows (P2 1, P3 14, P4 10) +## Apps & catalog — 23 rows (P2 1, P3 12, P4 10) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| @@ -144,15 +144,12 @@ stopping line that lies. | **R-707** | Apps & catalog | P4 | **[P2] 37 apps still start with a login a stranger can take (`09` §3 decision 45).** Audit of all 53 apps: `app-catalog-felhom.eu/FIRST-ADMIN.md` (class, fix route, status, measured or read). Open: **3 hard-coded defaults** — calibre-web (`admin / admin123`, measured working on demo-hp and 9202; its own `cps.py -s` route needs a generated password WITH a special character — our generator is letters+digits, a controller change), mealie (`changeme@example.com / MyPassword`), wger (`admin / adminadmin`); **34 open first-run screens** (the first visitor creates the admin: actualbudget, adventurelog, audiobookshelf, calcom, docmost, emby, ghost, gitea, gramps-web, home-assistant, homebox, immich, jellyfin, komga, n8n, navidrome, opengist, outline, papra, plant-it, radarr, rallly, recipe-importer, romm, seerr, sonarr, sparkyfitness, tandoor, termix, uptime-kuma, vikunja, wanderer, wishlist, zipline). **Stale notes:** romm's `default_creds` `admin / admin` answers 401 on demo-hp (like a wrong password) — the page now warns with a login that does not exist; zipline's looks stale too. **Measured on demo-hp 2026-09-28 (read-only):** bookstack's default still logs in on the INSTALLED app (the fix is for new installs; the page now warns). Each fix: route (a) env or (b) the app's own CLI/API via `after_install:`, proven on 9202 with the default failing and the generated password working; route (c) a page sentence. Several sessions (operator, 2026-09-28). **2026-09-29 (controller v0.280.0, catalog `d0e7e2e`):** every class-3 app fixed — mealie, wger, calibre-web by `after_install` (calibre-web with the new `password:24:special`), proven on 9202 fresh installs (`audits/login-gate-2026-09-29/D/`); the setup gate (decision 46, spike PASSED) built and live on immich, n8n, audiobookshelf (probes measured) and uptime-kuma (button) (`…/C/`); romm's and zipline's stale notes removed. **Left: 30 class-4 apps** — gate each (probe measured on 9202 where one exists — 11 upstream candidates listed in `…/B/B-VERDICT.md` §3; the button otherwise). **2026-09-29 afternoon (controller v0.281.0, catalog `6faf432`):** 28 more class-4 apps gated — 32 of 34 — each proven on 9202 (`audits/gate-rollout-2026-09-29/`B): stranger → gate page / 401, household reached the first-setup screen, the gate opened (9 by a measured probe, the rest by the press), the app answered after. seerr, outline, rallly: gated, their opening needs a media server / e-mail (not proven). **Left:** wanderer (R-714); plant-it is not installable. | **NARROWED** (2026-10-03 triage: the row's verdict was finished, but it names open work no other row carries — seerr, outline and rallly are gated, but the gate OPENING is not proven (needs a media server / e-mail)) — **CLOSED — 2026-09-29 (the rest → R-714)** | — | — | CC | | **R-905** | Apps & catalog | P4 | **wger's collected style files (283 MB) go into every wger backup, though the app rebuilds them at every start.** Since R-762's fix (`DJANGO_DEBUG=False` + `collectstatic`, catalog `cf1ed43`) the static files sit in a named volume that the backup legs copy like data (measured 283 MB, `audits/design-build-2026-10-06/`); `collectstatic` regenerates them, so they are rebuildable bytes in every Tier-1 unit, Tier-2 copy and off-site snapshot. wger is `lifecycle: hidden` and no box runs it (hub read 2026-10-08). Options to weigh: a non-backed-up volume class for regenerable data, or an anonymous volume. Found by the R-762 helpers 2026-10-08. | **OPEN — needs a design before wger is shown** | — | Design the „regenerable volume" exclusion (catalog + controller backup legs) | CC | | **R-995** | Apps & catalog | P4 | **Removing an app through the product can leave unnamed Docker volumes and an empty import folder behind.** MEASURED 2026-10-13 on demo-felhom after 61 removals (`documentation/audits/sizing-2026-10-13/METHOD.md` „Teardown”, `evidence/leftover-volumes-after-bench.txt`): 8 unnamed volumes (4–72 KB; immich 1, romm 2, privatebin 3, onlyoffice 2 — volumes the images declare themselves) and `userdata/import/{paperless,calibre,grimmory}` (empty, created by the deploy). The removal answered 200 and its own leftover check was clean, because it looks only at labelled volumes. No data risk; disk and tidiness only. Removed by name on the bench. | **READY** — filed 2026-10-13 | — | Make the removal account for the containers' unnamed volumes (and an empty import folder it created), with a test that deploys an image that declares a VOLUME | CC | -| **R-1067** | Apps & catalog | P4 | **The catalog still carries `pi_compatible:` on every template, and nothing reads it any more.** controller v0.315.0 removed the „Pi kompatibilis" badge and the „csak x86" tag from every page (Raspberry Pi is no longer supported, so one was noise and the other was a warning on almost every app). The `ResourceHints.PiCompatible` struct field and the catalog key were deliberately LEFT: a box still on 0.314.0 reads the key, and removing it would make that box show „csak x86" on every app. **Gated on the floor:** once the fleet floor is at or above 0.315.0, drop `pi_compatible:` from the templates and the field from `stacks.ResourceHints` in the same pair of commits. Harmless until then — an unread key costs nothing. | **BLOCKED** — filed 2026-10-15 | the fleet floor reaching 0.315.0 | Drop the catalog key and the struct field; grep the hub and the website for Pi wording at the same time (none found 2026-10-15) | CC | +| **R-1067** | Apps & catalog | P4 | **The catalog still carries `pi_compatible:` on every template, and nothing reads it any more.** controller v0.315.0 removed the „Pi kompatibilis" badge and the „csak x86" tag from every page (Raspberry Pi is no longer supported, so one was noise and the other was a warning on almost every app). The `ResourceHints.PiCompatible` struct field and the catalog key were deliberately LEFT: a box still on 0.314.0 reads the key, and removing it would make that box show „csak x86" on every app. **Gated on the floor:** once the fleet floor is at or above 0.315.0, drop `pi_compatible:` from the templates and the field from `stacks.ResourceHints` in the same pair of commits. Harmless until then — an unread key costs nothing. | **BLOCKED** — filed 2026-10-15. Checked 2026-10-16 (apps burndown): the floor is 0.321.0 and demo-hp, Tester 1 and the N100 run 0.321.0, but **Tester-2 still reports 0.292.0** (its newest report 2026-10-04; hub DB with WAL) — so nothing removed. | every box on ≥ 0.315.0 — today Tester-2 | Drop the catalog key and the struct field; grep the hub and the website for Pi wording at the same time (none found 2026-10-15) | CC | | **R-1043** | Apps & catalog | P4 | **Sonarr, Radarr and Prowlarr build their login redirect as an absolute `http://` URL.** They trust forwarded headers only from their „Trusted networks” (the household set `192.168.0.0/24`; traefik is `172.18.x`), so they ignore `X-Forwarded-Proto: https` and answer `Location: http:///login` to an https visitor. Since controller v0.312.0 that hop is sent back to https by traefik (a GET, no password), so it is cosmetic; a template could seed traefik's network as trusted. (`audits/https-only-2026-10-14/``1A-3-4.txt`) | **OPEN** — filed 2026-10-14 | — | Seed the docker network as a trusted proxy in the *arr templates (with the R-1030 seed), measure the redirect | CC | -| **R-1095** | Apps & catalog | P4 | **`/apps/{slug}/install` still opens for an app that is INSTALLED, and shows the install form read-only.** Found 2026-10-16 (app page tuning, controller v0.319.0): the settings tab stopped rendering the install form, but `appInstallPageHandler` renders `deploy` for any app, so a typed or stale `/install` address shows „ — Telepítés" with disabled fields and no button. Its secret guards hold (R-254/R-709 tests; parity cases `deploy_installed*`). | OPEN | the task forbade route changes | a 302 from `/apps/{slug}/install` to `/apps/{slug}/settings` for an installed app (the `redirectStackDeployGET` shape); then the deployed-only branches of `app_fields.html` can go | CC | | **R-1096** | Apps & catalog | P3 | **After a disk-full, an app's damaged Redis file keeps the app down for good, and nothing says what to do.** FOUND 2026-10-16 on demo-hp 9201: the standing RomM (installed 2026-08-21) was stopped; the dashboard's Start re-created it, `romm-redis` crash-looped (unhealthy), the box stopped it again (`crash_loop`, `09` §3 decision 28) and mailed `app_start_failed`. Its Redis volume dates from the 2026-09-24 thin-pool fill (R-672): Docmost's damaged append-only file was repaired by hand that day with `redis-check-aof --fix` (`audits/r672-2026-09-24/B7-redis-aof-fix.txt`); RomM's was not (the copy `romm_romm_redis_data.pre-aof-fix-20260924T135937Z` is still on the box). The Redis log was not read: the box removed the containers first. That RomM was then removed by operator ruling (`audits/romm-ghost-2026-10-16/A1/`). | OPEN — filed 2026-10-16 | — | List the catalog apps whose Redis runs with `--appendonly yes` (romm does); for each, decide whether its Redis needs persistence at all; reproduce on 9202 by damaging an append-only file, and make the box repair a copy or say what to do | CC | | **R-1100** | Apps & catalog | P3 | **One sign-in for the dashboard and FileBrowser (proxy auth) — spike first.** Since controller 0.320.0 every app with a folder has „Fájlok ↗", and the box can show FileBrowser's password — but FileBrowser still asks for its OWN login the first time, so the household signs in twice. FileBrowser Quantum has a proxy-auth method (`auth.methods.proxyAuth`, header-trusted) that could take the dashboard session through traefik; nobody has measured it. Design: `architecture/02-controller-module-map.md` „Logins and folders". | OPEN — filed 2026-10-16 (task logins-folders). | — | Spike on demo-hp: proxy auth at the pinned tag (source + measured), a forwardAuth in traefik that answers only for a dashboard session, what a stranger reaching `files.` with a forged header gets, and what happens to FileBrowser's shares. | CC | -| **R-1102** | Apps & catalog | P3 | **Eight apps say „starts with a known, shared password" about a login that is generated, set at install, or made on the first visit.** `app_info.default_creds` with no `after_install` keeps the known-login sentence in effect (`web/known_login.go`), so the install dialog and the app page say „Ez az alkalmazás egy ismert, közös jelszóval indul: . Telepítés után azonnal változtasd meg." for grafana, gokapi, kimai, code-server, nzbget, nextcloud, transmission (a password the box generated or the household typed, shown in the login panel) and prowlarr („Az első belépéskor te hozod létre"). Found 2026-10-16 while adding the login panel's answers. | OPEN | — | Catalog: drop `default_creds` on these eight (hu + `i18n.en`, `check-copy-i18n.py --capture-freeze`) now that the login panel says where the login is — or keep the hint and give the controller a „hint, not a known password" form. An operator word on which. | CC | -| **R-1097** | Apps & catalog | P3 | **RomM keeps the household's saves, save states and screenshots in an anonymous Docker volume, outside the backup.** FOUND 2026-10-16 from RomM 5.3.1's source and the running container: `ASSETS_BASE_PATH = /romm/assets` (`backend/config/__init__.py:39`); the image declares `VOLUME /romm`; the compose mounts nothing at `/romm/assets`, and `docker inspect` on 9201 showed an anonymous volume at `/romm`. Compose keeps an anonymous volume across a recreate, so an update does not lose it; a remove + reinstall or a restore does, and the app backup (named volumes + drive folders) probably does not hold it — NOT measured. Moving it to a named volume or the drive would hide what a box already has, so a fix needs a one-time copy. | OPEN — filed 2026-10-16 | — | Measure on 9202: a save in a browser game, back up, restore, read the save; then a named `romm_assets` volume with a one-time copy from the anonymous one | CC | | **R-1099** | Apps & catalog | P4 | **The N100 may hold RomM games in the old upload place, now shown under a fake „roms” platform.** Until 2026-10-16 RomM's own upload wrote to the drive's `roms/roms//`; after the mapping fix (catalog `fb18471`, R-1089) those files are at RomM's `library/roms/roms/…`. Measured on 9201: such a game showed as missing in its platform and again under a platform „roms” (`audits/romm-ghost-2026-10-16/B/b-after.txt`). Files put in with the file manager are not affected — they are now found. The N100 runs RomM (hub `/apps/romm`); CC does not log in to the lived-in box. | OPEN — filed 2026-10-16 | Operator: look at `roms/roms/` on the N100's drive in the file manager | If it holds platform folders: move them up one level into `roms/`, then run „Scan” in RomM; if it is empty, close | Operator | -| **R-1101** | Apps & catalog | P4 | **FileBrowser's stored password goes stale when the household changes it in FileBrowser — and the page tells them to.** In the `generated` state the panel shows „Az első bejelentkezés után azonnal változtasd meg!" under FileBrowser's login (R-513 copy). Changing it in FileBrowser leaves the box's copy wrong: „Megjelenítés" then shows a password that is refused, and the state stays `generated`, so „Új jelszó beállítása" is not offered. Found 2026-10-16. | OPEN | — | Either drop the „change it" line for FileBrowser, or have the reveal (or a tick) check the stored value against FileBrowser's login and fall back to `operator` when it is refused. | CC | +| **R-1103** | Apps & catalog | P4 | **`app_fields.html` still carries the install form's INSTALLED-only branches, which nothing serves since v0.321.0.** R-1095 made `/apps/{slug}/install` of an installed app answer 302 to its settings tab, so `deploy.html` now renders only for a not-installed app. The `.AlreadyDeployed` branches (disabled inputs, the eye reveals, `DeployedFieldValues`) stayed because the R-254/R-709 guard tests (`deploy_secret_exposure_test.go`, `r709_password_field_test.go`, `r694_restored_login_test.go`) render that path by hand and assert a reveal control on it; removing the branches means retiring those tests for an unreachable path. Found 2026-10-16. | OPEN | — | Remove the branches and the parity cases `deploy_installed*`; move each guard's assertion to the settings tab it now protects (most already exist there: `TestAppPage_NoStoredValueShowsSentence`, `TestR709_*`), red-proved | CC | ## App updates — 4 rows (P3 4) @@ -315,7 +312,7 @@ stopping line that lies. | **R-990** | Business & legal | P4 | **Operator: read `documentation/audits/forum-research-2026-10-12/FELHOM.md` §5 and rule on each recommendation.** READ-ONLY research 2026-10-12: three years (#7943–#11092, 2,908 posts, 195 people, 82 beginner threads) of the public PROHARDVER! topic „Home server / házi szerver építése”, tagged and counted by person (`FINDINGS.md`, method and spot-check in `METHOD.md`; no username or post text in git). Ten ranked recommendations, none acted on; the top three: a „which machine works” page (8 of 33 beginners who name their hardware own only a Synology, a router disk or a Raspberry Pi — the installer cannot run there; the same gap is R-989's first recommendation from a different source, and the shipped-page half is R-991), „you do not choose an operating system — we do” (the biggest beginner question, 20 of 82 threads), and „bring your old office PC” as the cheap entry. Catalog gap (≥ 3 people): a DLNA server (7), an ad/DNS blocker (5), a camera recorder (5). | **WAITING-ON-OPERATOR** — filed 2026-10-12 | the operator's reading | Operator: rule on each of the ten (yes / no / later). If nothing is done: nothing changes; the research ages | operator | | **R-1035** | Business & legal | P4 | **Did the „felhő tárhely” page earn anything? Two dated checks.** **Baseline (reported by the operator from Search Console, 2026-10-14, last 3 months):** 1 click, 48 impressions, CTR 2.1 %, average position **73.6**; „felhő tárhely” 19 impressions, „felhőtárhely” 5, „felhő alapú tárhely” 3. CC measured separately that the phrase „felhő tárhely” appeared **0 times** on the home, FAQ and backup pages — Google was showing the home page for those queries by accident. `/sajat-felho-tarhely` + `/en/own-cloud-storage` published **2026-10-14**; full baseline, price sources and the power provenance in `audits/seo-2026-10-14/BASELINE.md`. **What to measure, both times:** impressions and average position for „felhő tárhely” and „saját felhő tárhely”, plus the new page's own impressions, clicks and position. **A null result at +4 weeks means nothing on its own** — a new page on a site at position 73.6 takes weeks to months to move, and „felhő tárhely” alone is held by Microsoft, Google and the big list sites; the page aims at the two longer phrases. That is why there are two checks and not one, and why neither should be read as a verdict on the copy. **There is no Search Console connector in this project, so CC cannot read these numbers** — the operator reads them, CC records and compares. **Blocked until the operator asks for indexing** (Search Console → URL-ellenőrzés → the URL → „Indexelés kérése”): until that happens, or Google finds the page itself, both checks read zero for a reason that is not the page. **This is a slow channel and does not replace talking to people.** | **WATCHING** | operator: request indexing, then supply the Search Console read | 2026-11-11: first read (+4 weeks); 2026-12-09: second (+8 weeks), compared with the first | operator + CC | -## Process & tooling — 30 rows (P3 12, P4 18) +## Process & tooling — 29 rows (P3 11, P4 18) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| @@ -348,7 +345,6 @@ stopping line that lies. | **R-1025** | Process & tooling | P4 | **A power drill on demo-hp cuts Tester 1 too, and nothing warns the session using it.** 2026-10-14 17:25 CEST another session cut demo-hp's plug (`plugcut.py --plug hp`, the Immich drill) while this session ran downloads on Tester 1 (VM 341 runs on demo-hp); Tester 1 rebooted and came back by itself. The drill's fence checks Tester 1 for backups and package jobs only. | **OPEN** — filed 2026-10-14 | — | Add „a test runs on Tester 1” to the fence (a marker file the testing session writes), or name the rule in `runbooks/target-selection.md` | CC | | **R-1039** | Process & tooling | P4 | **qBittorrent stalls on the LAST piece of an archive.org torrent; Transmission does not.** Seen three times on 2026-10-14 (Tester 1 S2 and S3, demo-hp 9201 live read-back): the film reaches 99.994 %, the piece shared with archive.org's side files (`__ia_thumb.jpg`, `_meta.sqlite`, `_meta.xml`) never arrives, qBittorrent's log shows „500 Internal Server Error” from the archive.org web seed, while Transmission finishes the same torrent and curl range requests on the same files answer 206. A household that uses archive.org torrents with qBittorrent hits it too; our tests finish only with a local peer. | **OPEN** — filed 2026-10-14 | — | Note it in qBittorrent's onboarding record; for test films use Transmission, or a public-domain torrent with real peers | CC | | **R-1078** | Process & tooling | P4 | **A controller comment still describes `mp1 /mnt/sys_drive` as a separate volume.** `C/cmd/controller/main.go:2546` („the SYSTEM-DATA volume (`mp1`, `/mnt/sys_drive`)…”) — `mp1` was removed by R-165; `sys_drive` is a folder of `mp0` (measured on all three boxes, `audits/storage-identity-2026-10-15/README.md`). The same stale cell in `07-backup-architecture.md` §6.1 was corrected in this session. Not fixed here: this run allowed no product-repo change. | READY | — | Correct the comment in the next controller change. | CC | -| **R-1098** | Process & tooling | P3 | **The hub build does not refuse a version that already exists in the registry.** `scripts/build-hub.sh` v1.24.0 refuses a dirty tree or one behind `origin/main`, but not an existing tag; the controller's `build.sh` refuses a second push of a tag (`09-update-architecture.md` §0). Found 2026-10-16 building hub 0.155.0; checked by hand that time (`docker manifest inspect`: 0.155.0 absent, control 0.154.0 present; `audits/romm-ghost-2026-10-16/B/hub-build-0.155.0.txt`). | OPEN — filed 2026-10-16 | — | Give `build-hub.sh` the controller's refusal (registry check; an unreachable registry refuses), with test cases seen failing; check the agent build the same way | CC |