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:
@@ -0,0 +1,105 @@
|
||||
# REPORT — burndown 2: Data tab leftovers, plain http closed, the hub-key door removed, Go 1.27.2, the dead install-form branches (2026-10-16)
|
||||
|
||||
Written in ASD-STE100 Simplified Technical English. Operator brief „burndown 2", operator present.
|
||||
Design read first: `architecture/01-topology-and-trust.md` §4 and §10 (now records R-970), `04-control-plane-authorization.md`,
|
||||
`09-update-architecture.md` §0 and the release rule. Evidence: `documentation/audits/burndown2-2026-10-16/`.
|
||||
|
||||
## 0. A decision I took (inside the brief; reversible)
|
||||
|
||||
**The controller runtime stays on Debian 12 (12.13, all fixes) — not Debian 13.** The brief said „the current Debian
|
||||
stable". Debian 13 changes restic, the off-site backup engine, from 0.14.0 to 0.18.0. The off-site code relies on
|
||||
measured 0.14.0 behaviour (a missing source path is only a warning, the progress output, the clean-up windows,
|
||||
restore without a lock). Changing it silently changes how customer backups behave. The Go toolchain and every fixable
|
||||
OS package are updated; the Debian 13 move is its own row with the re-proof (R-1105).
|
||||
|
||||
## 1. Each part
|
||||
|
||||
| Part | Result |
|
||||
|---|---|
|
||||
| 1 — Data tab leftovers | **Done** (controller 0.322.0). The export password field: `autocomplete="new-password"`, name `fab_encryption_key`. The 2nd copy's status rows are gone; the form is the one place (its no-target reason moved into it); the warning stays. Browser (headless Chrome through the public address): the field renders with the attribute and stays empty; no status rows; one target label. **Headless Chrome has no saved passwords, so the autofill itself is not proven — the attribute is.** `P1/` |
|
||||
| 2 — plain http (R-1033, P1) | **Closed.** The hub's newest reports: demo-hp, Tester 1, demo-felhom on ≥ 0.321.0. `https_only_check.py`: 35 public names → 301 on all three. **Tester 2** (`sajatfelhom.hu`, 0.292.0, off since 2026-10-04) is not checked: both http and https answer Cloudflare 530 (tunnel down). R-1104 checks it when it returns. `P2/` |
|
||||
| 3 — the box settings route (R-970, P2) | **Closed.** See §3. |
|
||||
| 4 — Go and base images (R-972, P2) | **Controller released, hub built and waiting for your yes, agent committed (no release).** See §4. |
|
||||
| 5 — dead install-form branches (R-1103, P4) | **Closed.** The installed-only branches of `app_fields.html` removed. Retired tests and where their rule is now: `TestDeployPage_DeployedApp_DoesNotCarryTheSecret` → `TestAppPage_LoginPairPanel` + `TestR255_NoPlantedSecretReachesAnyPage`; `TestR709_AnInstalledAppsPasswordIsNotInThePage` → the same two (its install-form half stays as `TestR709_TheInstallFormKeepsItsGenerator`); `TestR694_TheDeployPageDoesNotOfferAGeneratedLogin` → `TestR694_TheSettingsTabDoesNotOfferAGeneratedLogin` (new, red-proved); the parity cases `deploy_installed` and `deploy_installed_restored`. |
|
||||
|
||||
## 2. https check per box (`P2/https-only.txt`)
|
||||
|
||||
tester-1 13 names, demo-hp 14, demo-felhom 8 — each `http://` → 301 to the same `https://` address; the negative
|
||||
control convicted. Tester 2: not checked (530, off).
|
||||
|
||||
## 3. The removed route (R-970)
|
||||
|
||||
- **What:** the routes that read and replaced the box's settings file, and the hub-key access to the self-update and
|
||||
geo routes (the dashboard keeps using those with a session). The matching CSRF exemption for the hub key is gone.
|
||||
- **Nothing called them:** no caller in the hub (it never connects into a box; its geo switch goes to Cloudflare
|
||||
directly), the agent, the controller's own pages, or any script in the five repos. The hub README listed „Push/Pull
|
||||
Config", „Show Diff" and „Trigger Update" — no code exists for them; the four rows are removed.
|
||||
`02-controller-module-map.md` marked config-apply as needing rework, not as a planned use.
|
||||
- **Proof:** the route table is now one function (`buildMux`), and `TestR970_HubKeyOpensNoDoor` drives that real table
|
||||
with real servers: every method on every former door, with the box's key and no session, is refused, and a planted
|
||||
settings file is neither served nor changed. Plus `TestR970_ConfigRoutesAreGone` and
|
||||
`TestR970_HubKeyIsNoCSRFExemption`. Each red-proved (`redproof/`). **Live on demo-hp:** every former door answers 401
|
||||
with the box's own key (read inside the box, never printed); `/api/health` 200 (`P3/live-door-check.txt`).
|
||||
|
||||
## 4. Go and base images (R-972)
|
||||
|
||||
| | before | after |
|
||||
|---|---|---|
|
||||
| controller binary | Go 1.24.13, x/crypto v0.31.0 | **Go 1.27.2, x/crypto v0.58.0** |
|
||||
| controller runtime | `debian:bookworm-slim` | **`debian:12.13-slim@sha256:67b3…` + `apt-get upgrade`** (restic stays 0.14.0) |
|
||||
| controller builder | `golang:1.24-bookworm` | `golang:1.27.2-trixie@sha256:4766…` |
|
||||
| hub | Go 1.24.13, x/crypto v0.31.0, `alpine:3.21` | **Go 1.27.2, x/crypto v0.58.0, `alpine:3.24.2@sha256:294b…`** (builder `golang:1.27.2-alpine3.24@sha256:f92b…`) |
|
||||
| agent | Go 1.26.0 (binary 0.158.1), x/crypto v0.52.0 | `go.mod` Go 1.27.2, x/crypto v0.58.0 — committed, **not released** |
|
||||
|
||||
Scanners (the review's own tools: trivy 0.74.0, govulncheck v1.1.4):
|
||||
|
||||
| image | critical (fixable) | high (fixable) | govulncheck on the binary |
|
||||
|---|---|---|---|
|
||||
| controller 0.321.0 (before) | 16 (12) | 200 (64) | 62 |
|
||||
| controller 0.322.0-rc.3 = the release's commit | **4 (0)** | **140 (4)** | **1** |
|
||||
| hub 0.155.0 (before) | 0 (0) | 36 (36) | 62 |
|
||||
| hub 0.156.0 | **0 (0)** | **0 (0)** | **1** |
|
||||
|
||||
The 4 remaining fixable highs are in Docker's own `docker-compose` plugin (its Go), not ours. The one govulncheck note
|
||||
is `x/crypto/openpgp`, a package neither program links (`go mod why`), reported at module level. Full suites green on
|
||||
Go 1.27.2 in all three repos (the i18n parity suite included).
|
||||
|
||||
**Restic, measured on demo-hp:** an off-site backup with restic 0.18.0 (rc.1, Debian 13) — „backup OK", 172 snapshots;
|
||||
then with 0.14.0 (rc.2) on the same store — „backup OK", 186 snapshots. The store stays readable both ways; the other
|
||||
0.14.0 assumptions are not measured on 0.18 → R-1105.
|
||||
|
||||
## 5. Versions, commits, fixtures, rows
|
||||
|
||||
- **felhom-controller** `8fe46a1` → `f037be7` (R-970, Data tab, R-1103) → `fc77fcb` (Go 1.27.2) → `52619e8`
|
||||
(Debian 12.13) → `e9d0d72` (CHANGELOG, `apt-get upgrade`) → docs. Test images rc.1 (Debian 13), rc.2, rc.3 (demo-hp
|
||||
only); release **0.322.0**, MinAgent 0.158.1.
|
||||
- **felhom.eu** `836d64ef` → `9d6f3b46` (hub 0.156.0) → this commit. **Hub 0.156.0 is pushed to the registry, NOT
|
||||
deployed** — waiting for the operator's yes.
|
||||
- **felhom-agent** `dd8b2ac` → `37b99d7` (Go 1.27.2, not released).
|
||||
- **Fixtures re-captured (8):** `app_data_deployed`, `app_data_export`, `app_data_notarget_disabled`,
|
||||
`app_data_target_auto`, `app_data_target_ssd`, `deploy_new`, `deploy_new_blocked`, `deploy_new_memory_blocked`
|
||||
(the last three: whitespace only, plus one test-data option production never renders). **Removed (2):**
|
||||
`deploy_installed`, `deploy_installed_restored`. Every other fixture byte-identical.
|
||||
- **Rows: before 175, after 174. Closed 3** (R-1033, R-970, R-1103). **Opened 2** (R-1104, R-1105). R-972 NARROWED.
|
||||
|
||||
**The release (`release/`):** Tester 1, demo-hp and demo-felhom took 0.322.0 through per-box floors (all three on it at
|
||||
19:01). Nothing unhealthy, restarting, exited or created; the three dashboards 200; https-only 35 names OK; restic 0.14.0
|
||||
in each controller. **Global floor 0.322.0 / MinAgent 0.158.1** at 19:04:50 (`303` → `flash=floor_set`); the three
|
||||
per-box floors cleared. Read back on two channels: the hub's own `/configuration` page, and one `tar` of `hub.db` +
|
||||
`-wal` + `-shm` (integrity ok, newest report 12 min old): `min_controller_version` 0.322.0, `…declared_min_agent`
|
||||
`0.322.0=0.158.1`, the three per-box floors empty.
|
||||
|
||||
## 6. Observations
|
||||
|
||||
1. Tester 2 is not checked for plain http while it is off. FILED: R-1104
|
||||
2. Debian 13 would move the off-site engine to restic 0.18.0 with assumptions measured on 0.14.0. FILED: R-1105
|
||||
3. govulncheck names `x/crypto/openpgp` in both new binaries. NOT-A-FINDING: neither program links it (`go mod why`: not needed); the report is module-level because the binaries are stripped.
|
||||
4. The parity case `deploy_new_memory_blocked` set „the current drive is missing" on a NOT-installed app, which the handler never does. NOT-A-FINDING: test data only; the re-captured fixture now shows what production renders.
|
||||
5. The Samba image (48 fixable high in the review) was not rebuilt in this task. NOT-A-FINDING: it is already named in R-972, which stays NARROWED for it.
|
||||
|
||||
## 7. Teardown (three layers)
|
||||
|
||||
- **Machine:** demo-hp 9201 ran rc.1, rc.2, rc.3, then the release by the floor; two by-day backup runs (off-site
|
||||
included). The door-check script ran inside the controller container and was removed; the key never left the box.
|
||||
- **Host:** nothing.
|
||||
- **Hub:** per-box floors set and cleared; the global floor raised (the release). Hub image 0.156.0 pushed, not deployed.
|
||||
@@ -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
|
||||
- **The hub's key can no longer open anything on a box** (controller 0.322.0, 2026-10-16): the settings route is gone, and every former door answers 401 even with the box's own key. Every box that reports answers only over https (35 names). The controller is built on a supported Go (1.27.2): known Go weaknesses in it 62 → 1, fixable OS ones 0.
|
||||
- **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).
|
||||
@@ -52,6 +53,7 @@
|
||||
| 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 | — |
|
||||
|
||||
## Waiting on you — do
|
||||
- **Say yes in chat to deploy hub v0.156.0** (built, scanned: 0 critical, 0 high; no code change — only the new Go and Alpine). If you do nothing: the hub keeps running on Go 1.24 with 36 known high findings.
|
||||
- **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/<platform>/`. 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.
|
||||
|
||||
@@ -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