burndown 2: controller 0.322.0 released; R-1033, R-970, R-1103 closed; R-972 narrowed; R-1104, R-1105 filed
gates / gates (push) Successful in 6m51s
gates / gates (push) Successful in 6m51s
This commit is contained in:
@@ -102,6 +102,11 @@ credentials.
|
||||
- The agent and the controller **poll** the hub; the hub never initiates inbound.
|
||||
- Operator actions are delivered as **signed jobs**: the agent verifies an operator signature
|
||||
before executing, so a compromised hub database alone cannot forge commands.
|
||||
- **[FACT, 2026-10-16, R-970]** The last contradiction is gone: until controller v0.322.0 the box's own hub API key,
|
||||
sent as a Bearer token, opened `/api/config/` (read `controller.yaml` with every secret in it, and REPLACE it,
|
||||
unsigned) and the self-update and geo routes. Nothing called them (the hub never connects to a box). The config
|
||||
routes are removed and no route accepts the hub key; every `/api/` route needs a dashboard session (pinned by
|
||||
`TestR970_HubKeyOpensNoDoor` on the real route table). The box exposes no control endpoint, as stated above.
|
||||
- All operator-initiated actions are recorded in a **customer-visible audit log**.
|
||||
|
||||
---
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 168 KiB |
@@ -0,0 +1,17 @@
|
||||
after login url: /launcher
|
||||
{
|
||||
"path": "/apps/calibre-web/data",
|
||||
"keyField": {
|
||||
"name": "fab_encryption_key",
|
||||
"autocomplete": "new-password",
|
||||
"type": "password",
|
||||
"valueLength": 0
|
||||
},
|
||||
"oldIdPresent": false,
|
||||
"statusRows": 0,
|
||||
"targetLabels": 1,
|
||||
"form": true,
|
||||
"warnCallout": true,
|
||||
"scrollWidth": 1270,
|
||||
"console": []
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
every box's newest controller report (hub DB + WAL, one tar, read 2026-10-16T16:41:33Z):
|
||||
demo-hp 0.322.0-rc.1 https://felhom.enkisfelhom.hu 2026-10-16 16:40:15
|
||||
tester-1 0.321.0 https://felhom.enkicsifelhom.hu 2026-10-16 16:36:41
|
||||
demo-felhom 0.321.0 https://felhom.demo-felhom.eu 2026-10-16 16:36:35
|
||||
Tester-2 0.292.0 https://felhom.sajatfelhom.hu 2026-10-04 18:06:24
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
Tester 2 (sajatfelhom.hu), read only, 2026-10-16T16:42:09Z:
|
||||
http://felhom.sajatfelhom.hu/login -> 530 server=cloudflare
|
||||
https://felhom.sajatfelhom.hu/login -> 530 server=cloudflare
|
||||
@@ -0,0 +1,14 @@
|
||||
key length read: 64
|
||||
GET /api/config with the hub key -> 401
|
||||
POST /api/config with the hub key -> 401
|
||||
GET /api/config/ with the hub key -> 401
|
||||
POST /api/config/ with the hub key -> 401
|
||||
GET /api/config/apply with the hub key -> 401
|
||||
POST /api/config/apply with the hub key -> 401
|
||||
GET /api/config/hash with the hub key -> 401
|
||||
POST /api/config/hash with the hub key -> 401
|
||||
GET /api/selfupdate/check with the hub key -> 401
|
||||
POST /api/selfupdate/check with the hub key -> 401
|
||||
GET /api/geo/settings with the hub key -> 401
|
||||
POST /api/geo/settings with the hub key -> 401
|
||||
control: GET /api/health (no auth) -> 200
|
||||
@@ -0,0 +1,4 @@
|
||||
ctl-0.322.0-rc.2.bin: go1.27.2
|
||||
ctl-0.322.0-rc.2: 1 vulnerabilities reported
|
||||
hub-0.156.0.bin: go1.27.2
|
||||
hub-0.156.0: 1 vulnerabilities reported
|
||||
@@ -0,0 +1,4 @@
|
||||
ctl-0.321.0.bin: go1.24.13
|
||||
ctl-0.321.0: 62 vulnerabilities reported
|
||||
hub-0.155.0.bin: go1.24.13
|
||||
hub-0.155.0: 62 vulnerabilities reported
|
||||
@@ -0,0 +1,40 @@
|
||||
2026/10/16 16:50:06 offsiteapply.go:148: [INFO] [offsite-apply] settle-gate: GO — at/above floor 0.321.0 (we are 0.322.0-rc.2), no managed update running
|
||||
2026/10/16 16:50:43 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.4:57016: [db-dump tier2 offsite update-leg]
|
||||
2026/10/16 16:50:43 night_chain.go:84: [INFO] [night-chain] manual run of tonight's chain: dump → second drive → off-site → update leg
|
||||
2026/10/16 16:50:43 night_chain.go:77: [INFO] [night-chain] db-dump: started
|
||||
2026/10/16 16:52:40 night_chain.go:82: [INFO] [night-chain] db-dump: done in 1m57s
|
||||
2026/10/16 16:52:40 night_chain.go:77: [INFO] [night-chain] tier2: started
|
||||
2026/10/16 16:53:03 night_chain.go:82: [INFO] [night-chain] tier2: done in 22s
|
||||
2026/10/16 16:53:03 night_chain.go:77: [INFO] [night-chain] offsite: started
|
||||
2026/10/16 16:53:03 offbox.go:1021: [INFO] [offbox] backup run started (15 app(s) toggled)
|
||||
2026/10/16 16:54:56 scheduler.go:450: [INFO] [scheduler] Running job: offsite-credential-retry
|
||||
2026/10/16 16:54:56 scheduler.go:467: [INFO] [scheduler] Job offsite-credential-retry completed (took 0s)
|
||||
2026/10/16 16:55:05 offbox.go:1068: [INFO] [offbox] pre-push dump leg completed in 2m2.212s — snapshot pair is coherent
|
||||
2026/10/16 16:55:08 offbox_capture.go:45: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:08 offbox_capture.go:45: [WARN] [offbox] qbittorrent: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:08 offbox_capture.go:45: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:08 offbox_capture.go:45: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:12 offbox.go:1504: [INFO] [offbox] backed up bookstack (/mnt/sys_drive/felhom-data/backups/primary/bookstack, 0 mandatory path(s))
|
||||
2026/10/16 16:55:15 offbox.go:1504: [INFO] [offbox] backed up docmost (/mnt/sys_drive/felhom-data/backups/primary/docmost, 0 mandatory path(s))
|
||||
2026/10/16 16:55:17 offbox.go:1504: [INFO] [offbox] backed up opengist (/mnt/sys_drive/felhom-data/backups/primary/opengist, 0 mandatory path(s))
|
||||
2026/10/16 16:55:20 offbox.go:1504: [INFO] [offbox] backed up paperless-ngx (/mnt/felhom-drives/hdd_1/backups/primary/paperless-ngx, 1 mandatory path(s))
|
||||
2026/10/16 16:55:22 offbox.go:1504: [INFO] [offbox] backed up privatebin (/mnt/sys_drive/felhom-data/backups/primary/privatebin, 0 mandatory path(s))
|
||||
2026/10/16 16:55:22 offbox_capture.go:45: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:24 offbox.go:1501: [WARN] [offbox] backed up prowlarr (/mnt/sys_drive/felhom-data/backups/primary/prowlarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:55:26 offbox.go:1501: [WARN] [offbox] backed up bentopdf (/mnt/sys_drive/felhom-data/backups/primary/bentopdf, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:55:28 offbox.go:1504: [INFO] [offbox] backed up kimai (/mnt/sys_drive/felhom-data/backups/primary/kimai, 0 mandatory path(s))
|
||||
2026/10/16 16:55:30 offbox.go:1504: [INFO] [offbox] backed up plex (/mnt/felhom-drives/hdd_1/backups/primary/plex, 0 mandatory path(s))
|
||||
2026/10/16 16:55:30 offbox.go:1420: [WARN] [offbox] qbittorrent: no recovery unit found on any connected drive — skipping
|
||||
2026/10/16 16:55:30 offbox_capture.go:45: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:33 offbox.go:1501: [WARN] [offbox] backed up radarr (/mnt/felhom-drives/hdd_1/backups/primary/radarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:55:36 offbox.go:1504: [INFO] [offbox] backed up romm (/mnt/felhom-drives/hdd_1/backups/primary/romm, 0 mandatory path(s))
|
||||
2026/10/16 16:55:38 offbox.go:1504: [INFO] [offbox] backed up calibre-web (/mnt/felhom-drives/hdd_1/backups/primary/calibre-web, 1 mandatory path(s))
|
||||
2026/10/16 16:55:42 offbox.go:1504: [INFO] [offbox] backed up home-assistant (/mnt/sys_drive/felhom-data/backups/primary/home-assistant, 0 mandatory path(s))
|
||||
2026/10/16 16:55:42 offbox_capture.go:45: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:55:44 offbox.go:1501: [WARN] [offbox] backed up sonarr (/mnt/felhom-drives/hdd_1/backups/primary/sonarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:55:46 offbox_window.go:231: [INFO] [offbox] retention skipped (after-run): no clean-up window now (not due (last window 2026-10-12T02:16:53Z)) — nothing deleted (decision 68)
|
||||
2026/10/16 16:55:52 offbox.go:1266: [INFO] [offbox] backup OK: 14 app(s) backed up, 1 skipped (no unit), 186 snapshot(s), 2m43s
|
||||
2026/10/16 16:55:52 night_chain.go:82: [INFO] [night-chain] offsite: done in 2m49s
|
||||
2026/10/16 16:55:52 night_chain.go:77: [INFO] [night-chain] update-leg: started
|
||||
2026/10/16 16:55:52 night_chain.go:82: [INFO] [night-chain] update-leg: done in 0s
|
||||
2026/10/16 16:55:52 night_chain.go:93: [INFO] [night-chain] finished in 5m9s
|
||||
@@ -0,0 +1,2 @@
|
||||
{"data":{"legs":["db-dump","tier2","offsite","update-leg"]},"message":"started","ok":true}
|
||||
202
|
||||
@@ -0,0 +1,40 @@
|
||||
2026/10/16 16:40:18 offsiteapply.go:148: [INFO] [offsite-apply] settle-gate: GO — at/above floor 0.321.0 (we are 0.322.0-rc.1), no managed update running
|
||||
2026/10/16 16:41:03 night_chain.go:108: [INFO] [night-chain] manual run started from 172.18.0.4:42566: [db-dump tier2 offsite update-leg]
|
||||
2026/10/16 16:41:03 night_chain.go:84: [INFO] [night-chain] manual run of tonight's chain: dump → second drive → off-site → update leg
|
||||
2026/10/16 16:41:03 night_chain.go:77: [INFO] [night-chain] db-dump: started
|
||||
2026/10/16 16:42:59 night_chain.go:82: [INFO] [night-chain] db-dump: done in 1m56s
|
||||
2026/10/16 16:42:59 night_chain.go:77: [INFO] [night-chain] tier2: started
|
||||
2026/10/16 16:43:21 night_chain.go:82: [INFO] [night-chain] tier2: done in 23s
|
||||
2026/10/16 16:43:21 night_chain.go:77: [INFO] [night-chain] offsite: started
|
||||
2026/10/16 16:43:21 offbox.go:1021: [INFO] [offbox] backup run started (15 app(s) toggled)
|
||||
2026/10/16 16:45:08 scheduler.go:450: [INFO] [scheduler] Running job: offsite-credential-retry
|
||||
2026/10/16 16:45:08 scheduler.go:467: [INFO] [scheduler] Job offsite-credential-retry completed (took 0s)
|
||||
2026/10/16 16:45:22 offbox.go:1068: [INFO] [offbox] pre-push dump leg completed in 2m0.527s — snapshot pair is coherent
|
||||
2026/10/16 16:45:25 offbox_capture.go:45: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:25 offbox_capture.go:45: [WARN] [offbox] qbittorrent: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:25 offbox_capture.go:45: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:25 offbox_capture.go:45: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:29 offbox.go:1504: [INFO] [offbox] backed up paperless-ngx (/mnt/felhom-drives/hdd_1/backups/primary/paperless-ngx, 1 mandatory path(s))
|
||||
2026/10/16 16:45:31 offbox.go:1504: [INFO] [offbox] backed up privatebin (/mnt/sys_drive/felhom-data/backups/primary/privatebin, 0 mandatory path(s))
|
||||
2026/10/16 16:45:31 offbox_capture.go:45: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:33 offbox.go:1501: [WARN] [offbox] backed up sonarr (/mnt/felhom-drives/hdd_1/backups/primary/sonarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:45:36 offbox.go:1504: [INFO] [offbox] backed up calibre-web (/mnt/felhom-drives/hdd_1/backups/primary/calibre-web, 1 mandatory path(s))
|
||||
2026/10/16 16:45:36 offbox.go:1420: [WARN] [offbox] qbittorrent: no recovery unit found on any connected drive — skipping
|
||||
2026/10/16 16:45:38 offbox.go:1504: [INFO] [offbox] backed up romm (/mnt/felhom-drives/hdd_1/backups/primary/romm, 0 mandatory path(s))
|
||||
2026/10/16 16:45:38 offbox_capture.go:45: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:40 offbox.go:1501: [WARN] [offbox] backed up prowlarr (/mnt/sys_drive/felhom-data/backups/primary/prowlarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:45:40 offbox_capture.go:45: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
|
||||
2026/10/16 16:45:42 offbox.go:1501: [WARN] [offbox] backed up radarr (/mnt/felhom-drives/hdd_1/backups/primary/radarr, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:45:44 offbox.go:1504: [INFO] [offbox] backed up opengist (/mnt/sys_drive/felhom-data/backups/primary/opengist, 0 mandatory path(s))
|
||||
2026/10/16 16:45:47 offbox.go:1504: [INFO] [offbox] backed up plex (/mnt/felhom-drives/hdd_1/backups/primary/plex, 0 mandatory path(s))
|
||||
2026/10/16 16:45:49 offbox.go:1501: [WARN] [offbox] backed up bentopdf (/mnt/sys_drive/felhom-data/backups/primary/bentopdf, 0 mandatory path(s)) — but the recovery unit carried NO database dump and NO volume tar, so this snapshot holds none of the app's data; the next run with a dump leg will replace it
|
||||
2026/10/16 16:45:52 offbox.go:1504: [INFO] [offbox] backed up bookstack (/mnt/sys_drive/felhom-data/backups/primary/bookstack, 0 mandatory path(s))
|
||||
2026/10/16 16:45:54 offbox.go:1504: [INFO] [offbox] backed up docmost (/mnt/sys_drive/felhom-data/backups/primary/docmost, 0 mandatory path(s))
|
||||
2026/10/16 16:45:57 offbox.go:1504: [INFO] [offbox] backed up home-assistant (/mnt/sys_drive/felhom-data/backups/primary/home-assistant, 0 mandatory path(s))
|
||||
2026/10/16 16:45:59 offbox.go:1504: [INFO] [offbox] backed up kimai (/mnt/sys_drive/felhom-data/backups/primary/kimai, 0 mandatory path(s))
|
||||
2026/10/16 16:46:01 offbox_window.go:231: [INFO] [offbox] retention skipped (after-run): no clean-up window now (not due (last window 2026-10-12T02:16:53Z)) — nothing deleted (decision 68)
|
||||
2026/10/16 16:46:06 offbox.go:1266: [INFO] [offbox] backup OK: 14 app(s) backed up, 1 skipped (no unit), 172 snapshot(s), 2m40s
|
||||
2026/10/16 16:46:06 night_chain.go:82: [INFO] [night-chain] offsite: done in 2m44s
|
||||
2026/10/16 16:46:06 night_chain.go:77: [INFO] [night-chain] update-leg: started
|
||||
2026/10/16 16:46:06 night_chain.go:82: [INFO] [night-chain] update-leg: done in 0s
|
||||
2026/10/16 16:46:06 night_chain.go:93: [INFO] [night-chain] finished in 5m3s
|
||||
@@ -0,0 +1,5 @@
|
||||
== gitea.dooplex.hu/admin/felhom-controller:0.322.0-rc.3
|
||||
gitea.dooplex.hu/admin/felhom-controller:0.322.0-rc.3 (debia debian crit 4 (fixable 0) high 136 (fixable 0)
|
||||
usr/libexec/docker/cli-plugins/docker-compose gobinary crit 0 (fixable 0) high 4 (fixable 4)
|
||||
usr/local/bin/felhom-controller gobinary crit 0 (fixable 0) high 0 (fixable 0)
|
||||
TOTAL crit 4 (fixable 0) high 140 (fixable 4)
|
||||
@@ -0,0 +1,9 @@
|
||||
== gitea.dooplex.hu/admin/felhom-controller:0.322.0-rc.2
|
||||
gitea.dooplex.hu/admin/felhom-controller:0.322.0-rc.2 (debia debian crit 6 (fixable 2) high 144 (fixable 8)
|
||||
usr/libexec/docker/cli-plugins/docker-compose gobinary crit 0 (fixable 0) high 4 (fixable 4)
|
||||
usr/local/bin/felhom-controller gobinary crit 0 (fixable 0) high 0 (fixable 0)
|
||||
TOTAL crit 6 (fixable 2) high 148 (fixable 12)
|
||||
== gitea.dooplex.hu/admin/felhom-hub:0.156.0
|
||||
gitea.dooplex.hu/admin/felhom-hub:0.156.0 (alpine 3.24.2) alpine crit 0 (fixable 0) high 0 (fixable 0)
|
||||
usr/local/bin/felhom-hub gobinary crit 0 (fixable 0) high 0 (fixable 0)
|
||||
TOTAL crit 0 (fixable 0) high 0 (fixable 0)
|
||||
@@ -0,0 +1,10 @@
|
||||
== gitea.dooplex.hu/admin/felhom-controller:0.321.0
|
||||
gitea.dooplex.hu/admin/felhom-controller:0.321.0 (debian 12. debian crit 16 (fixable 12) high 159 (fixable 23)
|
||||
usr/bin/docker gobinary crit 0 (fixable 0) high 3 (fixable 3)
|
||||
usr/libexec/docker/cli-plugins/docker-compose gobinary crit 0 (fixable 0) high 4 (fixable 4)
|
||||
usr/local/bin/felhom-controller gobinary crit 0 (fixable 0) high 34 (fixable 34)
|
||||
TOTAL crit 16 (fixable 12) high 200 (fixable 64)
|
||||
== gitea.dooplex.hu/admin/felhom-hub:0.155.0
|
||||
gitea.dooplex.hu/admin/felhom-hub:0.155.0 (alpine 3.21.8) alpine crit 0 (fixable 0) high 2 (fixable 2)
|
||||
usr/local/bin/felhom-hub gobinary crit 0 (fixable 0) high 34 (fixable 34)
|
||||
TOTAL crit 0 (fixable 0) high 36 (fixable 36)
|
||||
@@ -0,0 +1,8 @@
|
||||
## no autocomplete=new-password on the export key
|
||||
logins_folders_test.go:423: hu: the export password field lacks autocomplete="new-password" or still has a login-like name
|
||||
logins_folders_test.go:423: en: the export password field lacks autocomplete="new-password" or still has a login-like name
|
||||
--- FAIL: TestAppData_OneBackupSection (0.09s)
|
||||
## a status row back above the form
|
||||
logins_folders_test.go:416: hu: the 2nd copy's status rows repeat the form
|
||||
logins_folders_test.go:419: hu: the target drive is labelled 2 times, want once (the form)
|
||||
logins_folders_test.go:416: en: the 2nd copy's status rows repeat the form
|
||||
@@ -0,0 +1,3 @@
|
||||
## R-694 on the settings tab: the Restored branch removed
|
||||
r694_restored_login_test.go:35: the restored login is offered for reveal
|
||||
--- FAIL: TestR694_TheSettingsTabDoesNotOfferAGeneratedLogin (0.07s)
|
||||
@@ -0,0 +1,10 @@
|
||||
## an /api/geo/ route without the session check (a door)
|
||||
r970_no_hub_key_door_test.go:74: GET /api/geo/countries with the hub key answered 200 — the key opens a door
|
||||
--- FAIL: TestR970_HubKeyOpensNoDoor (0.08s)
|
||||
## the /config GET case back
|
||||
r970_config_routes_test.go:31: GET /api/config answered 200, want the router's 404
|
||||
r970_config_routes_test.go:31: GET /api/config/ answered 200, want the router's 404
|
||||
--- FAIL: TestR970_ConfigRoutesAreGone (0.00s)
|
||||
## the Bearer CSRF exemption back
|
||||
r970_csrf_test.go:40: a POST carrying the hub key passed CSRF (code 200)
|
||||
--- FAIL: TestR970_HubKeyIsNoCSRFExemption (0.00s)
|
||||
@@ -0,0 +1,12 @@
|
||||
== tester-1
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.322.0 Up About a minute (healthy)
|
||||
restic 0.14.0
|
||||
== demo-hp
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.322.0 Up About a minute (healthy)
|
||||
restic 0.14.0
|
||||
== demo-felhom
|
||||
unhealthy=0 restarting=0 exited=0 created=0
|
||||
controller gitea.dooplex.hu/admin/felhom-controller:0.322.0 Up About a minute (healthy)
|
||||
restic 0.14.0
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
felhom.enkisfelhom.hu 200
|
||||
felhom.enkicsifelhom.hu 200
|
||||
felhom.demo-felhom.eu 200
|
||||
@@ -0,0 +1,9 @@
|
||||
integrity ok
|
||||
newest report 2026-10-16 16:54:54 now 2026-10-16 17:06:26
|
||||
min_controller_version ('0.322.0',)
|
||||
min_controller_version_declared_min_agent ('0.322.0=0.158.1',)
|
||||
customer floor columns []
|
||||
customer_configs ['min_controller_version', 'min_controller_declared_min_agent', 'min_controller_set_at']
|
||||
('demo-felhom', '', '', '')
|
||||
('demo-hp', '', '', '')
|
||||
('tester-1', '', '', '')
|
||||
@@ -0,0 +1,3 @@
|
||||
19:04:50
|
||||
HTTP/1.1 303 See Other
|
||||
Location: /configuration?flash=floor_set
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
hub page /configuration shows 0.322.0: 3 times
|
||||
@@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""v0.322.0 burndown-2 browser check: the Data tab's export key field and the 2nd-copy part. 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']))
|
||||
goto(BASE + '/apps/calibre-web/data')
|
||||
time.sleep(2) # give any autofill its moment
|
||||
r = ev("""(function(){ var k=document.getElementById('fabEncryptionKey');
|
||||
return {path: location.pathname, keyField: k ? {name: k.name, autocomplete: k.getAttribute('autocomplete'), type: k.type, valueLength: k.value.length} : null,
|
||||
oldIdPresent: !!document.getElementById('exportPassword'),
|
||||
statusRows: document.querySelectorAll('[data-backup-row="second"],[data-backup-row="target"]').length,
|
||||
targetLabels: document.querySelectorAll('label[for="target"]').length,
|
||||
form: !!document.querySelector('[data-second-backup-form]'),
|
||||
warnCallout: !!document.querySelector('#app-backup-section .app-callout-warn'),
|
||||
scrollWidth: document.documentElement.scrollWidth}; })()""")
|
||||
r['console'] = console[:]
|
||||
shot('calibre-web-data')
|
||||
print(json.dumps(r, indent=1, ensure_ascii=False))
|
||||
finally:
|
||||
proc.terminate()
|
||||
subprocess.run(['rm', '-rf', prof])
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
"""trivy_counts.py <trivy.json>... — per image and per target: CRITICAL/HIGH counts, all and fixable (a fixed version
|
||||
exists). The burndown-2 before/after table (R-972)."""
|
||||
import json, sys
|
||||
from collections import Counter
|
||||
for p in sys.argv[1:]:
|
||||
d = json.load(open(p))
|
||||
print("== %s" % d.get("ArtifactName"))
|
||||
tot_all, tot_fix = Counter(), Counter()
|
||||
for r in d.get("Results", []) or []:
|
||||
a, f = Counter(), Counter()
|
||||
for v in r.get("Vulnerabilities") or []:
|
||||
sev = v.get("Severity")
|
||||
a[sev] += 1
|
||||
if v.get("FixedVersion"):
|
||||
f[sev] += 1
|
||||
tot_all += a; tot_fix += f
|
||||
if a:
|
||||
print(" %-60s %-9s crit %3d (fixable %3d) high %3d (fixable %3d)" % (r.get("Target", "")[:60], r.get("Type", ""),
|
||||
a["CRITICAL"], f["CRITICAL"], a["HIGH"], f["HIGH"]))
|
||||
print(" %-60s %-9s crit %3d (fixable %3d) high %3d (fixable %3d)" % ("TOTAL", "", tot_all["CRITICAL"], tot_fix["CRITICAL"],
|
||||
tot_all["HIGH"], tot_fix["HIGH"]))
|
||||
@@ -26,6 +26,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-10-16 — burndown 2 (controller v0.322.0)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|---|---|---|---|
|
||||
| **R-1033** | **Every box answered its public names over plain http, login pages included.** (P1) | CLOSED 2026-10-16 — fixed in controller v0.312.0 (traefik 301 to https, HSTS); the floor is 0.322.0 and every box that reports runs ≥ 0.321.0. `scripts/https_only_check.py` on all three reporting boxes: **35 public names → 301**, the negative control convicted. **Not checked:** Tester 2 (`sajatfelhom.hu`, off since 2026-10-04, 0.292.0) — its addresses answer Cloudflare 530 (tunnel down), so nothing is served in plain http; it takes the fix with the floor when it returns → R-1104. | `audits/burndown2-2026-10-16/P2/` |
|
||||
| **R-970** | **The box's own hub API key could read and replace the box's settings file, unsigned.** (P2) | CLOSED 2026-10-16 — **controller v0.322.0**: `/api/config`, `/api/config/hash`, `/api/config/apply` removed; no route accepts the hub key (the self-update and geo routes need a session, the CSRF exemption is gone). Nothing called them: no caller in the hub (it never connects to a box; geo-disable goes to Cloudflare), the agent, the scripts or the dashboard; the hub README's „Push/Pull Config" rows described code that no longer exists (removed). Tests on the REAL route table, red-proved; **live** on demo-hp: every former door 401 with the box's own key, `/api/health` 200. `01` §4 records it. | `audits/burndown2-2026-10-16/P3/` |
|
||||
| **R-1103** | **The install form kept branches no page served since R-1095.** (P4) | CLOSED 2026-10-16 — **controller v0.322.0**: the installed-only branches of `app_fields.html` removed; the tests that only rendered that dead page retired, each rule asserted on the settings tab (list in the controller CHANGELOG; R-694 moved, red-proved). | controller CHANGELOG v0.322.0 |
|
||||
|
||||
## 2026-10-16 — apps burndown (controller v0.321.0, catalog `326f846`, agent script `dd8b2ac`, hub build script)
|
||||
|
||||
| Row | What | Closed | Evidence |
|
||||
|
||||
@@ -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 — 23 rows (P2 1, P3 12, P4 10)
|
||||
## Apps & catalog — 22 rows (P2 1, P3 12, P4 9)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -149,7 +149,6 @@ stopping line that lies.
|
||||
| **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.<domain>` with a forged header gets, and what happens to FileBrowser's shares. | 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/<platform>/`; 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-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)
|
||||
@@ -214,18 +213,17 @@ stopping line that lies.
|
||||
| **R-1077** | Storage & devices | P4 | **No USB enclosure that hides or fakes a disk's serial has been measured.** The storage-identity spike (2026-10-15) had none at hand and found none in earlier evidence; the design's `usb_bridge` unknown rule (`architecture/16-storage-physical-identity.md` §3) is therefore an assumption. Cheap single- and dual-bay bridges are known to report one bridge serial for any disk. Evidence: `audits/storage-identity-2026-10-15/README.md` case 4b. | OPEN | — | Buy or borrow one cheap USB enclosure (single and dual bay), run `tools/physdisk_probe.py` against it, and set the rule from what it reports. | Operator (a device) then CC |
|
||||
| **R-1094** | Storage & devices | P4 | **The whole-disk format refusal tells the household to format the free part — which is refused while the other part is in use.** SEEN 2026-10-16 (controller 0.318.0): 409 „Ezen a lemezen egy használatban lévő meghajtó van, ezért a teljes lemez nem formázható. Csak a szabad részét formázhatod." — since agent 0.158.1 a part next to a mounted part can only be USED (the root wrapper trusts no „Felhom's own" claim). Evidence: `audits/storage-identity-buildB-2026-10-15/live/14-samedisk-refusal-offer.txt`. | READY | — | Reword `err.web.meghajto_hasznalatban_formazas` (hu + en): „… nem formázható. A szabad részét használatba veheted." Next controller release; the copy gates run. | CC |
|
||||
|
||||
## Security & access — 21 rows (P1 2, P2 3, P3 10, P4 6)
|
||||
## Security & access — 20 rows (P1 1, P2 2, P3 11, P4 6)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **R-1033** | Security & access | P1 | **Every box answers its public names over plain `http://` — login pages included.** MEASURED 2026-10-14 from DooPlex over the public path (Cloudflare `Server: cloudflare`, `CF-RAY`): `http://felhom.enkicsifelhom.hu/login` → 200, `http://felhom.enkisfelhom.hu/login` (demo-hp) → 200, `http://torrent.enkicsifelhom.hu/` → 200; no redirect to https and no `Strict-Transport-Security`. Sonarr, Radarr and Prowlarr make it worse: their sign-in redirect is ABSOLUTE `http://<name>/login`, so a household that opened the https address lands on the plain page and sends its password unencrypted between the browser and Cloudflare (`audits/media-all-drives-2026-10-14/H-indexer-sync-ncore.txt`). On an untrusted network (café Wi-Fi) that password can be read. The operator may raise this to P1. | **NARROWED** — filed 2026-10-14; **P1 by the operator 2026-10-14 22:13 (`09` §3 decision 244)**. FIXED in controller v0.312.0 (traefik sends every `http://` to `https://`, 301, path and query kept; HSTS one day; decision 246) and PROVEN on Tester 1, demo-hp and demo-felhom (29 public names, `scripts/https_only_check.py`; https answers unchanged), delivered by hand. Open: the fleet delivery — vouch 0.312.0 and the floor (morning); Tester 2 (`sajatfelhom.hu`, 0.292.0, off) gets it with the floor | the operator's vouch + floor | Vouch controller 0.312.0, raise the floor; then run `scripts/https_only_check.py` on every box and close | CC |
|
||||
| **R-985** | Security & access | P3 | **Our zero-knowledge wording is stronger than the product: on a live box the operator can reach the off-site backup key (through the vaulted root credential and the operator tunnel) without the customer's recovery code.** Design-shaped; decision for the operator in `STATUS.md`. Fix options: change the wording, or remove operator root reach to the key. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | WAITING-ON-OPERATOR — design-proposal | operator decision | Decide the wording or the change | operator |
|
||||
| **R-983** | Security & access | P3 | **Seven small dashboard weaknesses: after sign-in the browser can be sent to another site; the server-reboot route acts on a GET, so a link can reboot the guest (**FIXED 2026-10-13 in controller v0.310.0, R-998 session: POST only, `TestServerReboot_PostOnly` — six remain**); no anti-framing headers; parallel sign-in attempts get past the limiter; the settings page allows 8-character passwords; reset-code requests have no limit on the box; logout can be triggered from another site.** Fix each with a test, in one controller release. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | One hardening release | CC |
|
||||
| **R-977** | Security & access | P3 | **FileBrowser's default password can come back if its data volume is ever re-created, because the box checks it only once; and the tunnel can start before the box is claimed.** Static; needs a live check on 9202. Fix: check at every start, and do not start the tunnel before the claim. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | Measure on 9202; then fix | CC |
|
||||
| **R-976** | Security & access | P3 | **A family member's session can be copied out of a family-gated app, and a dashboard password reset does not end family or gate sessions; so a stolen session keeps working for up to 30 days.** Fix: give each app its own derived token instead of the session itself, and end every family and gate session on a reset. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | Fix with tests in a controller release | CC |
|
||||
| **R-970** | Security & access | P2 | **Whoever holds a box's own hub API key — the hub, or anyone with a copy of the hub database — can read the box's settings file with every secret in it and replace that file through the internet; the box does not check that such a change is signed.** This contradicts `01` §4 (the box exposes no control endpoint) and §10 (a hub compromise alone cannot forge commands). Nothing in the product calls this route any more. Fix: remove the route, or require an operator-signed op for it. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN — confirmed by a second reader | — | Remove the route with a test that it is gone; release the controller | CC |
|
||||
| **R-954** | Security & access | P2 | **We cannot tell whether anyone used the old hub operator key (R-950) or the open hub read routes (R-951) before 2026-10-12; if someone did, they could have read every box's break-glass root credential and every customer's box report.** The exposure began in February 2026; the logs that could show it cover only 2026-10-12 from about 09:54 and one day in June, so about 238 days are not visible. Fix (operator decision): change the break-glass root password of every box (tester-1 holds a real person's data), or accept the risk; either way, keep ingress access logs long enough to answer this next time. | WAITING-ON-OPERATOR — filed 2026-10-12 by the security review; the log search was not completed in-session | operator decision | Decide: rotate every box's break-glass root password now, or accept | operator |
|
||||
| **R-1042** | Security & access | P3 | **The boxes' Cloudflare keys cannot change zone settings, so Cloudflare's own „Always Use HTTPS” cannot be switched on.** Measured 2026-10-14 on all three zones (`audits/https-only-2026-10-14/``1A-1-zones-and-keys.txt`): each `cf_api_token` is valid and sees only its own zone, but GET/PATCH `settings/always_use_https`, `ssl`, `security_header` answer 403; the redirect and transform rule phases too. The https-only fix therefore lives on the box (decision 246). A second layer at Cloudflare needs „Zone Settings: Edit” (and, for headers, „Transform Rules”) added to each customer's key — the hub's zone-reach check stays. | **WAITING-ON-OPERATOR** — filed 2026-10-14 | a key permission only the operator can grant | Optional: widen each customer key, then switch „Always Use HTTPS” on per zone | operator |
|
||||
| **R-1104** | Security & access | P3 | **Check https-only on Tester 2 when it reports again.** R-1033 (plain http on every public name) is closed on the three boxes that report (35 names → 301, 2026-10-16). Tester 2 (`sajatfelhom.hu`) has been off since 2026-10-04 on controller 0.292.0; its addresses answer Cloudflare 530 (tunnel down), so nothing is served in plain http while it is off. When it comes back it takes the floor (≥ 0.312.0 carries the redirect). | OPEN — filed 2026-10-16 | Tester 2 reporting again | Run `scripts/https_only_check.py` with Tester 2 in it; close | CC |
|
||||
| **R-955** | Security & access | P4 | **The cross-box check of the hub API is done in source and tests but not fully proven: if a later check masks a missing id check, a box's key could read or change another box's hub record without any test noticing.** Read 2026-10-12: every box-facing `/api/v1` route takes the box from its own key or refuses a different id; 14 routes already had a refusal test, 5 got one (`crossbox_refusal_test.go`). Left: red-prove the 4 new tests that were not red-proved (as the restore-directive one was), and the live read-only check with one demo box's key against the other demo box's id. | OPEN — filed 2026-10-12 by the security review | — | Red-prove the 4 tests; run the live GET check on the two demo boxes | CC |
|
||||
| **R-924** | Security & access | P2 | **The operator signing keys exist only on DooPlex, with no passphrase and in no backup — a loss of DooPlex means no box ever accepts a signed agent update, config bundle or OS step again without an on-site re-enrollment.** MEASURED 2026-10-09: `/mnt/5_hdd/felhom.eu/felhom-op-operational`, `felhom-rec-recovery`, `felhom_op_ed25519` (0600 `kisfenyo`); `ssh-keygen -y -P ''` opens all three (no passphrase); `/mnt/5_hdd/felhom.eu` is in no backup set, the off-site job does not carry them. `04` §1 says the operational key is "passphrase-protected" and the recovery key "kept offline" — today neither holds (custody ruling 1 of 2026-09-16 put both on DooPlex for this phase; `04` §3.1). Both keys share one fate, so the cold key cannot do its one job (re-pin after losing the operational key). `runbooks/total-loss-of-dooplex.md` S6. | **NARROWED 2026-10-09 — operator ruling: option A** (print both AND copy them). The three key files and their public halves ride the nightly encrypted DooPlex→ep0 copy (`scripts/dooplex-offsite/`, a missing key refuses the push); the Sunday restore test proves each private key still derives its own public half (never printed). **LEFT:** the operator prints S6a/S6b (`runbooks/break-glass-sheet.md`); the keys still have no passphrase (custody ruling 1 of 2026-09-16 stands until the first sale). | operator: print | Operator: print the two keys; then close | operator |
|
||||
| **R-132** | Security & access | P3 | **`curl -w '%{redirect_url}'` reconstructs the request URL WITH its basic-auth credential** — so a `-u ":$HUB_PW"` call that never put the password in a URL still printed it **Merged 2026-10-05 from R-350 (duplicate):** (1) 2026-08-20 occurrence: POST /configuration/artifacts answers 303; leak lives only in the CC transcript under ~/.claude/projects/ on DooPlex, not in git/evidence (checked then). (2) `-v` and `--libcurl` also re-render the credential, not only %{redirect_url}; confirm redirects with %{http_code} + follow-up GET. (3) Rotation path: hub /configuration form (current_password/new_password/confirm_password); DB override wins over ConfigMap (break-glass); CC can rotate file-to-file without printing (operator-present-one-time-secrets) if asked. | **WAITING-ON-OPERATOR** — **ACTION: rotate `HUB_PW`** **Folded R-580 2026-10-03** (the same `curl -w %{redirect_url}` credential echo, seen again 2026-09-18). **Note 2026-10-12 (security review):** `manifests/hub.yaml` also carries the hub login's bcrypt hash in this public repository (the break-glass field); when `HUB_PW` is rotated, decide whether that field should stay empty. | — | Happened on 2026-07-31 while red-proofing the R-120 gate: the hub operator password was written to the session transcript by the write-out format, not by the request. `-u` is safe; the *reporting* was not. Rule: read the redirect from `-D -` and grep `^Location:`, never `%{redirect_url}`, on any authenticated call. Rotate the hub password (`/configuration` → Login password; ConfigMap `auth.password_hash` is the reset path) and update `~/.config/credentials` | Viktor |
|
||||
@@ -240,15 +238,16 @@ stopping line that lies.
|
||||
| **R-1050** | Security & access | P4 | **Raise HSTS to one year on or after 2026-11-15, if no https trouble was reported.** `09` §3 decision 249 (D2 = A, operator 2026-10-15): HSTS stays `max-age=86400` now; after a month with no https trouble it becomes one year (`31536000`); never preload, never `includeSubDomains`. The value is `HSTSMaxAgeSeconds` in `felhom-controller/controller/internal/infra/infra.go`, pinned by `TestRenderForwardedHeaders_HTTPSOnly`. „Trouble” = any report of a name that will not open, or a box answering http after the floor. | **WATCHING** — filed 2026-10-15 | the date | On or after 2026-11-15: read the register and the operator's mail for https trouble since 2026-10-15; none → a controller release with one year (test updated), bake → vouch → floor | CC |
|
||||
| **R-1054** | Security & access | P3 | **The hub's operator/global API key is still one bearer that can do everything on `/api/v1`, including reading every box's break-glass root password (`GET /admin/hosts/{id}/recovery-credential`).** Hub v0.152.0 gave the web surface scoped tokens (`05` §17) but tokens are refused on `/api/v1` by design. Who uses the global key today is not listed in one place. Fix: list its consumers; move each operator use to a scoped token on the web side (or a token class on the API), and keep the global key only where a box path needs it — or retire it. | OPEN — filed 2026-10-15 by the token build | — | List the global key's consumers (pod env, scripts, runbooks) | CC |
|
||||
|
||||
## Box system & updates — 12 rows (P2 1, P3 7, P4 4)
|
||||
## Box system & updates — 13 rows (P2 1, P3 8, P4 4)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **R-1105** | Box system & updates | P3 | **The controller runtime is still Debian 12, because Debian 13 moves the off-site engine from restic 0.14.0 to 0.18.0.** The off-site code's measured behaviour is restic 0.14.0's: a missing source path is only a warning (`offbox_capture.go`), the `--json` progress shape (`offbox_progress.go`), the forget day-buckets (`offbox_window.go`), restore without a lock (`offbox_proof.go`). Debian 12's restic binary is built with go1.19.8. Measured 2026-10-16 on demo-hp: one off-site backup with 0.18.0 (backup OK, 172 snapshots), then 0.14.0 on the same store (backup OK, 186) — so the store stays readable both ways; the rest is NOT measured. | OPEN — filed 2026-10-16 | — | Re-prove each 0.14.0 assumption on restic 0.18 (a missing path, progress, a forget window, a restore), then move the runtime to Debian 13 | CC |
|
||||
| **R-984** | Box system & updates | P4 | **Host and agent hardening, low risk each: the backup-target route has no role check; the unsigned guest spec has no size bounds; signed ops have no maximum validity; the guarded mkfs does not refuse LVs, zvols or RAID devices; the local API is reachable from the LAN by a static route; a sudoers inspect pattern is wider than needed; install passes secrets on the command line; guest settings files are world-readable; the host offers rpcbind and DNS on the LAN.** Fix each in its own small change. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | Batch into hardening work | CC |
|
||||
| **R-982** | Box system & updates | P3 | **The Proxmox host accepts root password login over SSH from the LAN (measured on demo-hp), so anyone on the household network can try the root password.** Design-shaped: the break-glass password exists for the console. Fix: key-only SSH for root; the password stays for the console. Evidence: `audits/security-review-2026-10-12/evidence/demo-hp-host-reads.txt`. | OPEN — design-proposal | — | Decide; then set it in the installer and the config bundle | CC |
|
||||
| **R-979** | Box system & updates | P3 | **Controller updates are trusted by registry tag alone (no digest, no signature), and the Update button offers any newer tag, even an unvouched one; the built-in traefik, cloudflared and FileBrowser images are also pinned by tag only.** Whoever can push to the registry decides what runs. Fix: pin by digest from the hub's vouched list; offer only vouched versions. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN — design-proposal | — | Design note, then build | CC |
|
||||
| **R-974** | Box system & updates | P3 | **A compromised host agent can likely become root on the host through the root wrapper that applies PBS settings: the wrapper accepts a folder path inside the agent's own writable tree and then acts on it as root.** Static (the symlink behaviour was reasoned, not run). Same class as R-861. Fix: a fixed root-owned folder, and refuse symlinks before any root action. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | Fix the wrapper with a test on a throwaway box | CC |
|
||||
| **R-972** | Box system & updates | P2 | **The shipped controller and hub programs are built with Go 1.24.13 and golang.org/x/crypto v0.31.0, and the agent with Go 1.26.0, so dozens of fixed Go security bugs (including html/template escaping fixes) are still in what runs on boxes and the hub.** Go 1.24 has had no security fixes since Go 1.26 was released (Go supports the two newest major releases: https://go.dev/doc/devel/release#policy); the scanners name fixes in Go 1.26.9 and 1.27.2. Also: the controller image's Debian 12 base has 12 fixable critical packages, and the Samba image has 48 fixable high ones. Fix: build all three on a supported, patched Go; update x/crypto; rebuild the base images. Scanner output: `audits/security-review-2026-10-12/scanners/`. | OPEN — measured on the shipped binaries | — | Bump the toolchain and bases in their own release task | CC |
|
||||
| **R-972** | Box system & updates | P2 | **The shipped controller and hub programs are built with Go 1.24.13 and golang.org/x/crypto v0.31.0, and the agent with Go 1.26.0, so dozens of fixed Go security bugs (including html/template escaping fixes) are still in what runs on boxes and the hub.** Go 1.24 has had no security fixes since Go 1.26 was released (Go supports the two newest major releases: https://go.dev/doc/devel/release#policy); the scanners name fixes in Go 1.26.9 and 1.27.2. Also: the controller image's Debian 12 base has 12 fixable critical packages, and the Samba image has 48 fixable high ones. Fix: build all three on a supported, patched Go; update x/crypto; rebuild the base images. Scanner output: `audits/security-review-2026-10-12/scanners/`. | **NARROWED** 2026-10-16 (burndown 2): **controller v0.322.0 released** on Go 1.27.2 + x/crypto v0.58.0, runtime debian:12.13-slim + `apt-get upgrade` (Debian 13 deliberately not: restic 0.18, R-1105) — scanner: critical 16 → 4, fixable criticals and highs in the OS 0; govulncheck 62 → 1 (module-level, `openpgp` not linked). **Hub v0.156.0 built** (Go 1.27.2, alpine 3.24.2: 0 critical, 0 high) — deploy waits for the operator's yes. **Agent: Go 1.27.2 committed (`37b99d7`), tested, NOT released** — the next vouched agent release carries it. Open: the Samba image (48 fixable high, not rebuilt); docker-compose plugin's own Go (4 high, upstream). Evidence `audits/burndown2-2026-10-16/P4/` | the operator's yes for the hub deploy; the next vouched agent release | Deploy hub v0.156.0 on the yes; rebuild the Samba image on its current base; close when the agent release carries Go 1.27.2 | CC |
|
||||
| **R-862** | Box system & updates | P3 | **Tester 2 cannot take the config bundle until one by-hand bootstrap is done: its `felhom-os-apply` (agent 0.142.0) predates the bundle mode, and no signed job can write a root file on it.** FOUND 2026-10-04 (R-840 build, Part C): the route reaches every box installed from installer 1.31.0 on, and the demo boxes (bootstrapped by CC); Tester 2 has every root file of agent 0.142.0 (installer 1.30.0) and lacks only the R-858 wrapper fix, which matters only for a Docker step it gets solely from a signed job. CC has no route to Tester 2 (its door admits only the operator's WireGuard peer; `felhom-op` cannot become root). The steps: `runbooks/config-bundle.md` "Tester 2". Then CC signs the bundle and reads it back. | **WAITING-ON-OPERATOR** — the operator said (2026-10-04 ~19:05) he will try through his tunnel **2026-10-07 07:58 (`09` §3 decision 170): the operator believes Tester 2 never set up the off-site backup (no escrow); the laptop may stay off for days; the tester plans to add an HDD.** **2026-10-07 (Part H, read only):** in Tester 2's last 10 notifications (2026-10-05 09:16 → 2026-10-07 06:58) the household got 0 mails, the operator 10 (host_down ×9, expected_dbdump_missed ×1); control: the household channel shows on the other three customers. So the household is not mailed daily while the box is off; no row (`audits/day-2026-10-07/H/`). | the operator's WireGuard tunnel | the operator runs the three bootstrap commands; CC sends the bundle | operator |
|
||||
| **R-78** | Box system & updates | P3 | **`local_api` authority ruling — auto-reconcile vs detect-only** **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-25, size M, roadmap state `idea (deferred OUT of R-77 on purpose)`.** Moved verbatim; nothing added or reinterpreted. The roadmap keeps its copy as history, marked moved. **An OWED OPERATOR DECISION, not a defect — moved because an owed ruling hidden among feature ideas is the shape this session exists to remove.** | **OPEN — migrated from ROADMAP 2026-08-22, rank unchanged** | — | R-77 ships detection because the fix is genuinely undecided, and **both directions can lose customer-visible function**. **Direction 1 (today):** `controller.yaml` wins and drift is silent → the 2026-07-25 island migration blinded the whole fleet's control plane for 17.5 h (drive gate, guest-reboot recovery, quiesce/backup all degrade). R-77 makes that loud but does not stop it recurring. **Direction 2 (`bootstrap.json` wins, auto-reconcile on boot):** a guest whose `controller.yaml` is CORRECT and whose `bootstrap.json` is stale — a half-completed re-provision, a hand-repaired guest, a setup-wizard box — gets a **working channel clobbered on the next restart**, fleet-wide and silently, during a routine deploy. That is not obviously better than the bug. Needs a spike: which writer is authoritative per field (endpoint vs fingerprint vs token — `mergeLocalAPI` replaces the whole block, so they cannot be reconciled independently today), whether the agent side should stamp a generation/mtime so 'newer wins' is even expressible, and whether reconcile should require an operator ack. Until then the drift alert plus a manual edit is the supported path. | CC |
|
||||
| **R-468** | Box system & updates | P3 | **[P3-LOW] THE GOLDEN WAIVER — goldens on a cadence, not per release (operator ruling 2026-09-13).** 25 goldens in 26 days in August, almost one per release, because `golden_currency_gate.py` trips on every release by design and the only honest ways past it were a bake or a declared `--no-verify` (thirteen by 2026-09-01, R-404/R-417). **The ruling: bake WEEKLY, and always before any drill or fresh install.** Every release still raises the FLOOR, so both demo boxes keep getting each release in ~20 s; only the golden — which protects a fresh install and nothing else — moves to a cadence. **The mechanism (built 2026-09-13):** `documentation/tests/golden-waiver. **⚠ CORRECTED THE SAME DAY (R-472): between bakes the floor does NOT carry a release — the hub holds any floor above the vouched golden (publish-train rule 1), so releases between bakes reach the demo boxes only by hand-deploy.**yml`, four lines (`issued`, `expires`, `reason`, `register_row: R-468`), read by the gate. While valid, a golden BEHIND the record makes the gate print a loud ADVISORY and exit 0; when it expires the gate is red again until someone bakes or renews. **The 14-day cap is enforced by the gate, not the runbook** — a longer, undated, unparseable, reason-less or row-less waiver is INCONCLUSIVE (exit 2), never 0 and never silently ignored. **It never covers a golden that is UNRECORDED (R-385)** — that is not a cadence choice. **A dated waiver cannot be forgotten; it just expires** — the difference from R-242's original rule, which recurred the day after it was written. Tests: `scripts/test_golden_currency_gate.py` cases 5–15 (E/F/G/H, a 15-day, absent, unparseable, bad-row and empty-reason waiver each 2; the R-421 decoy — a file saying only `expires` — 2). **This is a PRE-CUSTOMER arrangement: the first external install retires it** (delete the file in that commit). Cadence written into `RUNBOOK-manual-build.md` §4.2 and the `felhom.eu` end-of-session checklist. **Does NOT touch R-242's open half (nothing gates the VOUCH).** **2026-10-15 (`09` §3 decision 250, operator):** the cadence is now the RULE, not a waiver — a controller release needs no golden (the floor carries it), the golden is baked every Sunday night when the floor is above it (`runbooks/nightly-rotation.md`), and `golden_currency_gate.py` convicts only a golden MORE THAN 7 DAYS old while a release is ahead (CASE 18–21, red-proved). The waiver file stays as a dated exception for a missed Sunday. | **WATCHING** — the weekly bake is a night-routine step; the first external install still retires the waiver arrangement | — | — | CC |
|
||||
|
||||
Reference in New Issue
Block a user