v0.322.0 released: REPORT, CONTEXT, README (R-970 routes removed)
gates / gates (push) Successful in 52s
gates / gates (push) Successful in 52s
This commit is contained in:
+11
-1
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
+12
-32
@@ -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 <HUB_API_KEY>"
|
||||
```
|
||||
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 <hub_api_key>` 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 <hub_api_key>` (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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user