From 29a3ce8cf2fc38b1d24243dd49c7221874d48098 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 16 Oct 2026 19:07:43 +0200 Subject: [PATCH] v0.322.0 released: REPORT, CONTEXT, README (R-970 routes removed) --- CONTEXT.md | 12 +++++++++++- REPORT.md | 43 ++++++++++++++++++++++++++----------------- controller/README.md | 44 ++++++++++++-------------------------------- 3 files changed, 49 insertions(+), 50 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index 2d77e70..54412b7 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -7,7 +7,17 @@ > > Ask Claude Code: "Please update CONTEXT.md with what we did today" -Last updated: 2026-10-16 (v0.321.0 — apps burndown: FileBrowser stale password + banners, one backup section, install address) +Last updated: 2026-10-16 (v0.322.0 — burndown 2: the hub key opens no route, Go 1.27.2 on Debian 12.13, Data tab leftovers, dead install-form branches) + +> **2026-10-16 — v0.322.0 RELEASED by CC; the global floor 0.322.0 / MinAgent 0.158.1.** R-970: `/api/config`, +> `/api/config/apply`, `/api/config/hash` are gone; the self-update and geo routes no longer take the box's hub key +> (`selfUpdateAuthMiddleware` removed), and the CSRF Bearer exemption is gone. The route table is `buildMux` in `main.go` +> so `TestR970_HubKeyOpensNoDoor` drives the real table. R-972: Go 1.27.2, x/crypto v0.58.0, runtime +> `debian:12.13-slim` pinned by digest + `apt-get upgrade`. **Decision (CC, inside the brief, reversible): the runtime +> stays on Debian 12** — Debian 13 brings restic 0.18.0 and the off-site code relies on measured 0.14.0 behaviour; the +> move is R-1105. Data tab: the export field is `fab_encryption_key`, `autocomplete="new-password"`; the 2nd-copy +> status rows are gone. R-1103: the installed-only branches of `app_fields.html` removed; their rules live in the +> app-page and settings-tab tests. Full report: felhom.eu `REPORT-burndown2-2026-10-16.md`. > **2026-10-16 — v0.321.0 RELEASED by CC; the global floor 0.321.0 / MinAgent 0.158.1.** R-1101: in `generated` the page asks > FileBrowser whether the stored password still logs in (`stacks.FileBrowserStoredPasswordRefused`, only a 401/403 counts) diff --git a/REPORT.md b/REPORT.md index 69f61c1..64dc27c 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,30 +1,39 @@ -# REPORT — v0.321.0: FileBrowser's stale password and its own banners, one backup section, the install address (2026-10-16) +# REPORT — v0.322.0: the hub key opens no route, Go 1.27.2, the Data tab leftovers, the dead install-form branches (2026-10-16) -Written in ASD-STE100 Simplified Technical English. Task: the apps burndown. Full report (RomM, the catalog, the build -guards, the Pi key, the rows): felhom.eu `REPORT-burndown-apps-2026-10-16.md`. Design: felhom.eu -`architecture/02-controller-module-map.md` „The app page" (v0.321.0 notes). Evidence: felhom.eu -`documentation/audits/burndown-apps-2026-10-16/`. +Written in ASD-STE100 Simplified Technical English. Task: burndown 2. Full report (the https close-out, the hub, the agent, +the scanners, the rows): felhom.eu `REPORT-burndown2-2026-10-16.md`. Design: felhom.eu +`architecture/01-topology-and-trust.md` §4 (now records R-970), `09-update-architecture.md` §0. Evidence: felhom.eu +`documentation/audits/burndown2-2026-10-16/`. ## 1. Plain words first -- **FileBrowser:** the page no longer says to change its password. If someone changes it in FileBrowser anyway, the page - notices (FileBrowser refuses the stored one) and offers a new one. FileBrowser no longer shows its own „update - available" banner or „Install app" prompt. -- **Data tab:** the backup part is one section with rows and plain words. -- **An installed app's install address** goes to its settings tab. +- **The box's hub key opens no route any more.** The routes that read and replaced the box's settings file are gone. + The self-update and geo routes take only a logged-in session. Nothing called them with the key. +- **New Go (1.27.2) and an updated Debian 12 base.** Scanner counts fell: critical 16 → 4 (none fixable), fixable high + 64 → 4 (all in Docker's own compose plugin), govulncheck 62 → 1 (a package not linked). +- **The runtime stays on Debian 12**, so restic stays 0.14.0. Debian 13 is its own row (R-1105). +- **Data tab:** the export password field tells the browser „new password", so it does not fill a saved one. The 2nd + copy's status rows are gone; the form is the one place. +- **The install form** lost its branches for installed apps; no page served them. ## 2. Commits and images -`c02c9d8` (v0.320.0) → `7e791d9` (the work) → this docs commit. `0.321.0-rc.1` (demo-hp only), release **0.321.0**, -MinAgent 0.158.1. Floor: per-box for Tester 1, demo-hp, the N100, then the global floor 0.321.0. +`8fe46a1` (v0.321.0) → `f037be7` (R-970, Data tab, R-1103) → `fc77fcb` (Go 1.27.2) → `52619e8` (Debian 12.13) → +`e9d0d72` (CHANGELOG, `apt-get upgrade`) → this docs commit. Test images `0.322.0-rc.1` (Debian 13), `-rc.2`, `-rc.3` +(demo-hp only); release **0.322.0**, MinAgent 0.158.1. Per-box floors for Tester 1, demo-hp, the N100, then the global +floor 0.322.0, read back from the hub's page and its database. ## 3. Tests and gates -Red-proved: `TestFileBrowserStoredPassword_RefusedOnlyOnA401`, `TestFileBrowserLogin_StoredPasswordRefusedOffersReset` -(two mutations), `TestAppData_OneBackupSection` (two mutations), `TestInstallAddress_InstalledRedirectsToSettings`. -Green gate rc 0; `controller_gates.py --fast` all OK. Fixtures: 13 re-captured, 1 new (named in the CHANGELOG). +Red-proved: `TestR970_HubKeyOpensNoDoor`, `TestR970_ConfigRoutesAreGone`, `TestR970_HubKeyIsNoCSRFExemption`, +`TestAppData_OneBackupSection`, `TestR694_TheSettingsTabDoesNotOfferAGeneratedLogin`. Retired with the route: +`config_perms_test.go`, the `TestConfigApply_*` tests. Retired with the branches (their rules named in the full report): +`TestDeployPage_DeployedApp_DoesNotCarryTheSecret`, `TestR709_AnInstalledAppsPasswordIsNotInThePage`, +`TestR694_TheDeployPageDoesNotOfferAGeneratedLogin`, parity cases `deploy_installed`, `deploy_installed_restored`. +Green gate rc 0 on Go 1.27.2; `controller_gates.py --fast` all OK. Fixtures: 8 re-captured, 2 removed. ## 4. Observations -1. The install form keeps branches no page serves since this release; the guard tests pin them. FILED: R-1103 -2. No eye icon exists in the dashboard's icon set; adding one to the shared set would change every page. NOT-A-FINDING: the export toggle uses the text every other reveal button uses. +1. Debian 13 would move the off-site engine to restic 0.18.0 with assumptions measured on 0.14.0. FILED: R-1105 +2. govulncheck names `x/crypto/openpgp` in the binary. NOT-A-FINDING: the program does not link it (`go mod why`); the report is module-level because the binary is stripped. +3. The parity case `deploy_new_memory_blocked` set „the current drive is missing" on a not-installed app. NOT-A-FINDING: test data only; the handler never does it, and the re-captured fixture shows what production renders. diff --git a/controller/README.md b/controller/README.md index 95a5ce4..6612a61 100644 --- a/controller/README.md +++ b/controller/README.md @@ -3185,17 +3185,12 @@ volumes: | Method | Path | Auth | Description | |--------|------|------|-------------| -| GET | `/api/selfupdate/status` | Session or API key | Current status (cached, no network call) | -| POST | `/api/selfupdate/check` | Session or API key | Force registry check, return result | -| POST | `/api/selfupdate/update` | Session or API key | Trigger update (async, returns immediately) | +| GET | `/api/selfupdate/status` | Session | Current status (cached, no network call) | +| POST | `/api/selfupdate/check` | Session | Force registry check, return result | +| POST | `/api/selfupdate/update` | Session | Trigger update (async, returns immediately) | -Self-update endpoints accept either session auth (for UI) or hub API key as bearer token (for external triggering from build scripts or hub). This enables the post-v0.16.0 deploy workflow: - -```bash -# After building + pushing new image: -curl -s -X POST https://felhom.demo-felhom.eu/api/selfupdate/update \ - -H "Authorization: Bearer " -``` +Self-update endpoints take a logged-in session only. **v0.322.0 (R-970):** the hub API key no longer opens them (it did +from v0.16.0 for a build-script trigger nobody used); boxes move by the hub's floor. Pinned by `TestR970_HubKeyOpensNoDoor`. ##### Settings Page UI @@ -3319,7 +3314,7 @@ Synchronizer-token CSRF protection on all browser-facing state-mutating endpoint - Logs: `[WARN] CSRF rejected: METHOD /path from addr (reason)` **Exempt paths (no CSRF check):** -- Requests with `Authorization: Bearer ...` header — hub→controller API calls (selfupdate, config/apply). Browsers cannot auto-send Bearer headers, so cross-site requests are impossible on these endpoints. +- *(Removed v0.322.0, R-970: the `Authorization: Bearer` exemption. No route takes the hub key, so such a request needs the session's token like any other — `TestR970_HubKeyIsNoCSRFExemption`.)* - Auth-disabled mode (`authEnabled() == false`) — CSRF is meaningless when there is no session. **Token delivery to templates:** @@ -3471,12 +3466,10 @@ Bearer token authentication, 3-attempt retry with 5-second backoff. Push status **Direction 2 — hub→box wait channel (v0.140.0):** the reverse immediacy path, so an OPERATOR action on the hub reaches the box in seconds. `report.Waiter` (`internal/report/waiter.go`) holds a hanging authenticated `GET {hub}/api/v1/wait?gen=N` (same hub URL + key as the pusher; no new config keys) against hub ≥ v0.58.0's in-memory operator-intent generation counter. The hub completes the hold the instant any operator intent bumps that customer's generation (config save/delete, claim resend, offsite re-issue/freeze, floor, block/unblock, log-pull); on a generation **change** the Waiter fires the same Direction-1 `report.Trigger` — and nothing else, so the immediate report's ACK delivers everything through the unchanged config-refresh/escrow/claim/floor machinery (the box pulls even the wake-up; the hub never connects inbound). Its `http.Client` has **no overall timeout** (a held GET must stay open for the hub's ~240 s hold, which streams a 25 s heartbeat newline to defeat the nginx 60 s read-timeout — no ingress change needed); a per-request context bounds a dead connection. First-observation records-not-fires (no restart echo); a same-generation timeout fires nothing; any error (transport, a 404 from a pre-v0.58.0 hub, malformed body) backs off 5 s→5 min and the 15-min cycle keeps reconciling. Constructed beside the trigger under the same `hubPusher != nil && cfg.Hub.Enabled` gate. Grounding: `felhom.eu/documentation/audits/SPIKE-immediate-sync-transport-2026-07-16.md`. -#### Config apply + self-restart (`internal/api/router.go`, `internal/api/selfrestart.go`) +#### Self-restart (`internal/api/selfrestart.go`) -`POST /api/config/apply` (Hub-authed) writes a new `controller.yaml`, but the new config only takes effect on **restart** — singletons such as the Cloudflare client are built once at startup (so a rotated CF API token would otherwise keep failing). Behaviour (v0.70.0): - -- **No-op guard:** if the pushed body is byte-identical to the current file, do nothing — no rewrite, no restart (the Hub may re-push idempotently; never flap on a no-op). -- Otherwise: write `0600`, respond `200` (flushed), then **gracefully self-restart** — `gracefulSelfRestart` waits ~500 ms for the response to flush, then `os.Exit(0)`. The container runs `restart: unless-stopped`, so Docker brings it back with the fresh config, and a startup report is pushed. The exit sits behind an injectable seam (`Router.restart` / `SetRestarter`) for unit testing. +*(The hub-authed `POST /api/config/apply` that wrote `controller.yaml` and restarted is removed in v0.322.0, R-970 — +no caller existed. Settings reach the box only through the hub report's ACK.)* **Manual restart (v0.70.0):** `POST /api/selfrestart` (session auth + CSRF via the `/api/` mount) runs the same helper — surfaced as the **"Vezérlő újraindítása"** button on the settings page (confirm → POST → poll `GET /` every 2 s → reload), so a customer can recover the controller without rebooting the whole guest. @@ -3796,7 +3789,7 @@ Thread-safe access via `GetGeoRestriction()`, `SetGeoRestriction()`, `SetGeoAppO | POST | `/api/stacks/{name}/geo/override` | Set per-app country override | | DELETE | `/api/stacks/{name}/geo/override` | Remove per-app override | -All mutating endpoints trigger an async Cloudflare sync. The `/api/geo/` path accepts both session auth and Hub Bearer token auth (via `selfUpdateAuthMiddleware`), enabling Hub-side geo-disable for lockout recovery. +All mutating endpoints trigger an async Cloudflare sync. The `/api/geo/` path takes a session only (v0.322.0, R-970: the hub key no longer opens it; the hub's geo switch goes to Cloudflare directly). #### Sync Triggers @@ -4575,22 +4568,9 @@ All daily jobs use Europe/Budapest timezone. Skip-if-running prevents concurrent | POST | `/api/selfupdate/check` | Force registry check | | POST | `/api/selfupdate/update` | Trigger self-update (async) | -Self-update endpoints accept session auth OR `Authorization: Bearer ` for external triggering. +Self-update endpoints take a session only (v0.322.0, R-970). -### Config Management - -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/config/apply` | Apply new controller.yaml from Hub (atomic write) | -| GET | `/api/config/hash` | Get SHA256 hash of current controller.yaml | -| GET | `/api/config` | Get raw controller.yaml content (text/yaml) for live diff and pull | - -Config endpoints accept session auth OR `Authorization: Bearer ` (same as self-update). The `/api/config/apply` endpoint: -- Accepts raw YAML body (the generated config from Hub) -- Validates YAML is parseable before writing -- Atomic write: writes to `.tmp` then `os.Rename` for crash safety -- Does NOT reload config — restart required to apply changes -- Returns `{"ok": true, "message": "Config applied. Restart controller to apply changes."}` +*(Config Management — `/api/config`, `/api/config/apply`, `/api/config/hash` — removed in v0.322.0, R-970.)* ### Metrics