R-1299 CLOSED: the clean-up window button (hub 0.174.0/0.174.1, live grant + cancel on tester-1); R-1326 filed (hub start-up vs liveness); R-1237 updated (46 dead names removed with sec rm); 07/05 design lines; secrets runbook: sec rm
gates / gates (push) Successful in 1m59s

This commit is contained in:
admin committed 2026-10-27 21:16:48 +01:00
1 parent 714ee236b2
commit f633d48bab
14 files changed
+553 -7

No files matched your search

+10
View File
@@ -16,6 +16,16 @@
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
> **2026-10-27 evening — R-1299 the clean-up window button (hub v0.174.0 + v0.174.1); cc-secrets 0.3.0 `sec rm`.** The
> customer page's „Backup & DR" tab has the „Off-site clean-up window" card (`internal/web/offsite_window.go`): the
> one-shot grant through a confirm step, same route and rule (`/offsite/window-grant/<id>`, `wr(grpOffsite)`), cancel,
> a limit bounded by the store's snapshot count (ceiling 500). Every grant/cancel is a STORED operator-only info event
> `offsite_window_grant` — 0.174.0 only called the dispatcher (which writes no row) and its test asserted the call; the
> operator's live click exposed it, 0.174.1 writes the row (`store.SaveEvent`) and the test reads rows. Hub builds came
> from a clean clone (`FELHOM_HUB_REPO_DIR`) — the shared clone held another session's uncommitted installer edit.
> Start-up 31–36 s vs a liveness kill at ~65 s: R-1326. `sec rm NAME` (cc-secrets `dc82cf9`); 46 dead names removed on
> the operator's yes (92 → 46). Agent 0.167.0 vouched by the operator; `pinned.day0_agent` = 0.167.0 (agent `3055bc5`).
> **2026-10-27 (attended release block) — RELEASED: hub v0.173.0, agent v0.167.0, controller v0.346.0.** Hub 0.173.0
> deployed 09:35 CET (image `sha256:458bfb03…`, from `29134c52`, ArgoCD sync of Deployment/hub only, 30-min watch clean).
> Agent 0.167.0 by `release-agent.sh` from a clean clone (agent `28bdd282…`, bundle `9d034416…`; tag `v0.167.0`), signed
+55
View File
@@ -0,0 +1,55 @@
# REPORT — 2026-10-27 evening: the clean-up window button (R-1299) and `sec rm`
## Plain words first
1. **The button is live** on the hub: customer page → **Backup & DR** tab → „Off-site clean-up window". You granted a
window on Tester 1 and cancelled it again; nothing was removed.
2. **`sec rm NAME` works** (cc-secrets 0.3.0): one name per call, saved in git, undo by `git revert`.
3. **46 store entries removed** on your yes (names in `documentation/audits/r1299-window-button-2026-10-27/sec-rm.txt`):
the two D55 token copies, 20 of the 10-20 rehearsal box, 14 of the night/fixes test boxes, 3 of the rig and the
release BYO box, 5 of the 10-24 sweep box, `R_C11_REWALK`, `R_PART4`. The store went from 92 to 46.
4. **You still do:** nothing from this run. (Today's three do-items are done: agent 0.167.0 vouched, the D55 token
revoked, `Tester4-b5dbbd` deleted.)
**A defect of my own, found by your click and fixed:** hub 0.174.0 logged each grant but stored NO event row (my code
called only the dispatcher, which writes no row, and my test checked the call, not the row). Hub 0.174.1 stores the row;
the test now reads the stored rows and fails on 0.174.0's shape. 0.174.1 also moved the card to Backup & DR, where you
looked for it first.
## Versions, digests, CI
| | Version | Digest | From | CI |
|---|---|---|---|---|
| hub | 0.174.0 | `sha256:4f9ee04a9ff7c108a37a411be3c3de521b6acccb431c9e550c3567f7211e2e94` | `d85198b8` | 2433 ✓ |
| hub | 0.174.1 | `sha256:bb43f88acd59b8b20914381932eb2d9f779d44c190c34d3349eed91d37ce71fb` | `de57c090` (manifest `714ee236`) | 2437 ✓ |
| cc-secrets | 0.3.0 | — | `dc82cf9` | no CI in that repo; local suite 72 OK, red-proofs 28/28 |
| agent pin | day0_agent 0.167.0 | — | `3055bc5` | (end-of-run check) |
Live: 0.174.0 at 19:25 CET, 0.174.1 at 20:26 CET; both Synced/Healthy, 0 restarts, hub-only ArgoCD sync, built from a
clean clone (the shared clone held another session's uncommitted installer edit). 30-minute watch after 0.174.1
(to 21:13 CET): 0 warning-or-worse events, 0 WARN/ERROR lines, 10 box reports in the last 20 minutes, 0 restarts, no
new alarm mail.
## Tests
Hub `TestR1299_*` (five): the page per branch (and in the Backup & DR tab), a grant through the form stores one grant with
its limit and one stored row, refusals store nothing (above the count, not a number, no confirm, no CSRF), the ceiling
when no count is known (260 passes, 501 refused), cancel. Red-proofs: seven mutations, all caught
(`redproof-0174.0.txt`, `redproof-0174.1.txt`). **The tests were written after the code in Part 1** — so each was then
seen failing by a mutation; Part 2's `sec rm` tests were written first and seen failing before the code existed.
## Rows
Before 224 open → after 224. Closed: R-1299. Opened: R-1326 (the hub's start-up, 31–36 s, eats half its liveness margin
of ~65 s). Updated: R-1237 (its dead names are gone from the store; the sweep no longer watches those values; the
transcripts are unchanged).
## Observations
- FILED R-1326 — hub start-up vs the liveness probe.
- FIXED in-session — the window event wrote no row (0.174.0 → 0.174.1).
- FIXED in-session — the card's place (Backup & DR).
- NOT-A-FINDING: CC cannot press the button — its token has no `offsite:write` and CC holds no hub password, by design
(`09` §3 decision 251); the live click was the operator's.
- NOT-A-FINDING: the `sec doctor` sweep line still lists the removed names — it reports the LAST night's sweep (03:40 UTC),
before the removal.
+2 -2
View File
@@ -23,7 +23,7 @@
- DooPlex: something sets the shared checkout to „bare" (R-935).
## What we work on
- Released 2026-10-27: held apps reach you; safer kept data, ceremony and app import; reinstalled boxes back up off-site at once; the line test works while seeding; Go 1.27.3. Open: R-1285 (needs a BYO box on an old agent), R-1312 (what the hub's off-site switch does on the box).
- Released 2026-10-27: held apps reach you; safer kept data, ceremony and app import; reinstalled boxes back up off-site at once; the line test works while seeding; Go 1.27.3. Open: R-1285 (needs a BYO box on an old agent), R-1312 (what the hub's off-site switch does on the box). Evening: the one-time off-site clean-up window has a button (customer page → Backup & DR; you tested it); `sec rm` deleted 46 dead test-box names from CC's store.
- Dated checks, run by CC. 2026-10-30: D35, website and installer to Cloudflare, starts (R-1228, ruled yes).
## Waiting on you — decide
@@ -52,7 +52,7 @@ Newest first. CC's pick is in the „Pick" column. Each row also has its full te
6. BIOS: on the N100, read „After Power Loss"; on the HP, set „Bypass F1 Prompt on Configuration Changes" to Enable. (since 10-14)
7. Pin the first Facebook post (R-917), and look at the new cover in the Facebook phone app (R-919). (since 10-12)
**2026-10-27.** 1. Vouch agent 0.167.0 (hub → Configs → Day-0; both sha256 in the release report). If not: new installs start on 0.166.0. 2. Revoke the token `cc-d55-window`; the window ran (R-1294). 3. Delete hub host `Tester4-b5dbbd`; if not, it keeps alarming. New: a nightly security watch mails you (R-1303).
**2026-10-27.** Done today: agent 0.167.0 vouched, the D55 token revoked, `Tester4-b5dbbd` deleted. New: a nightly security watch mails you (R-1303).
**2026-10-23 do list.**
1. Print the break-glass sheet (`documentation/runbooks/break-glass-sheet.md`, the part from „The sheet”, one A4), fill it by hand, keep it outside the home (D32; R-923, R-924). If not: after a DooPlex loss no off-site copy opens.
2. On the hub's Hosts page: Discard appliance 49. If not: a dead entry alarms.
@@ -759,7 +759,7 @@ method (405), so a token gets 403.
| `/configs/{id}/edit` | `customers:read` | **session only** | Edit can change the household's e-mail — and a new e-mail auto-mails the self-bind link (ruling 13:20). |
| `/offsite/remove-unpinned/{id}` | — | **session only** | |
| `/offsite/abandon-cancel/{id}` | — | **session only** | Cancelling a pending set-aside deletion: no group names it; until the operator places it, session only. |
| `/offsite/window-grant/{id}` | — | `offsite:write` | |
| `/offsite/window-grant/{id}` | — | `offsite:write` | The one-shot clean-up window: the script address AND the customer page's „Backup & DR" card (R-1299, hub v0.174.0): `from=page` + `confirm=1` from the form; `cancel=1` removes a waiting grant; a limit above the store's snapshot count is refused. |
| `/offsite/windows-enabled` | — | `offsite:write` | |
| `/os/{id}` | `os:read` | `os:write` | |
| `/offsite/key-audit` | — | `offsite:read` | Runs the daily key check now: it reads keys and reports, it changes none. |
@@ -261,6 +261,14 @@ window adds a week of snapshots and removes none, so the plan grows faster than
(2026-10-16, 152 snapshots) aged past 7 days and the 10-26 window refused 209 > 179 — an HONEST plan (every removal 7–30
days old, every app keeps ≥ 9 or its last copies; `audits/r1294-offsite-cap-2026-10-26/`). The existing way out is the
operator's one-shot raised cap (R-833, `GrantOffsiteWindowOnceMax`).
**[DESIGN — R-1299, built hub v0.174.0/v0.174.1 2026-10-27, `d85198b8` + `de57c090`] The one-shot window has a button.**
The customer page's „Backup & DR" tab carries the „Off-site clean-up window" card: the weekly switch, the normal cap,
a waiting grant and the last window; an optional limit, a confirm step that names the customer and the limit, then the
same route and rule as before (`POST /offsite/window-grant/<customer>`, `offsite:write` or a session). A raised limit
above the store's measured snapshot count is refused (a window cannot remove more than exist; the 500 ceiling stays). A
waiting grant can be cancelled before the box uses it. Every grant and cancel is a stored operator-only info event
`offsite_window_grant` (who, customer, limit). Live: granted and cancelled on tester-1 by the operator, nothing removed
(`audits/r1299-window-button-2026-10-27/`).
**[DESIGN, built hub 2026-10-07 — R-32 option A, `09` §3 decision 167] What RESET does to the household's off-site
copy.** On the shared pool box a sub-account is a LOGIN, not the data: deleting it leaves its folder (measured
@@ -0,0 +1,41 @@
# R-1299 — the one-time clean-up window gets a button (hub 0.174.0 → 0.174.1), and `sec rm` (cc-secrets 0.3.0), 2026-10-27 evening
## The button
- **Where:** customer page → „Backup & DR" tab → card „Off-site clean-up window" (0.174.0 had it in the Overview's Backup
card; the operator looked under Backup & DR first, so 0.174.1 moved it). Shown only when the box reports off-site
enabled.
- **Shows:** the weekly switch, the normal cap (half the snapshots, at least 5), a waiting one-time grant and its limit,
the last window (id, time, result, count, cap).
- **Form:** optional limit → confirm step (a GET; names the customer and the limit; stores nothing) → POST to the SAME
route as the script address, `/offsite/window-grant/<customer>`, rule unchanged (`wr(grpOffsite)`), the page's CSRF.
A waiting grant shows „Cancel the waiting window".
- **Bound:** a raised limit above the store's measured snapshot count is refused (a window cannot remove more than exist);
the 500 ceiling stays. D55's 260-for-373 stays possible.
- **Record:** every grant and cancel writes an operator-only info event `offsite_window_grant` (who, customer, limit) and a
log line.
## Live check (operator's clicks; nothing removed)
1. 0.174.0, 20:07 CET: the operator granted ONE window on tester-1 with the normal cap through the confirm step (screenshots
in the session). Hub DB (with WAL): `offsite_window_grant:tester-1 = 1`; log „ONE clean-up window granted for tester-1
with the normal cap, by operator session". **No event row** — the defect below.
2. 0.174.1, 20:42 CET: the operator pressed „Cancel the waiting window" under Backup & DR. Hub DB: the grant is empty; ONE
stored row `offsite_window_grant` info „the waiting one-time window was cancelled (by operator session)",
`{"by":"operator session","cancelled":true}`, source hub; the log line matches.
**Defect found by step 1 and fixed in 0.174.1:** the event went only to the dispatcher, which writes no row (it routes
mail; info routes nowhere). The test had asserted the dispatcher CALL; it now reads the STORED rows — red with 0.174.0's
shape (`redproof-0174.1.txt`).
## Deploys
0.174.0: `sha256:4f9ee04a…` from `d85198b8` (CI 2433), live 19:25 CET. 0.174.1: `sha256:bb43f88a…` from `de57c090` (CI
2437), live 20:26 CET. Both built from a clean clone (the shared clone held another session's uncommitted installer
edit), hub-only ArgoCD sync, Synced/Healthy, 0 restarts. Start-up 36 s / 31 s against a liveness kill at ~65 s → R-1326.
## `sec rm` and the tidy-up
cc-secrets 0.3.0 (`dc82cf9`): one name per call, unknown refused, `rm NAME` commit + push, undo by `git revert`. 7 tests,
2 red-proofs (28/28 in all). The operator approved removing 46 dead names; `sec-rm.txt` holds the run (names only): 92 →
46 names, every removal committed and pushed, `sec doctor` all OK after.
@@ -0,0 +1,170 @@
[STEP] 0/3 — Checking the registry: is 0.174.0 already published?
[INFO] 0.174.0 is not present in the registry — proceeding.
[STEP] 1/3 — Pulling latest code (the tree must be clean and at origin/main)...
[INFO] ╔══════════════════════════════════════╗
[INFO] ║ felhom-hub build ║
[INFO] ╚══════════════════════════════════════╝
[INFO] Version: 0.174.0 (build-hub.sh v1.25.0)
[INFO] Commit: d85198b8
[INFO] Source: /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/felhom-eu-clean/hub
[INFO] Build: /mnt/5_hdd/felhom.eu/build/felhom-hub/workspace
[INFO] Image: gitea.dooplex.hu/admin/felhom-hub:0.174.0
[STEP] 2/3 — Syncing source to workspace...
[INFO] Source synced to /mnt/5_hdd/felhom.eu/build/felhom-hub/workspace
[INFO] App assets synced (263 files)
[INFO] Package structure verified ✓
[INFO] Running go mod tidy...
[STEP] 3/3 — Building Docker image...
[INFO] Building for current platform + pushing...
#0 building with "default" instance using docker driver
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 1.46kB done
#1 DONE 0.0s
#2 [internal] load metadata for docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#2 ...
#3 [auth] library/alpine:pull token for registry-1.docker.io
#3 DONE 0.0s
#4 [auth] library/golang:pull token for registry-1.docker.io
#4 DONE 0.0s
#5 [internal] load metadata for docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#5 DONE 1.1s
#2 [internal] load metadata for docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#2 DONE 1.8s
#6 [internal] load .dockerignore
#6 transferring context: 2B done
#6 DONE 0.0s
#7 [stage-1 1/5] FROM docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#7 resolve docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 0.0s done
#7 DONE 0.0s
#8 [builder 1/6] FROM docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#8 resolve docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c 0.0s done
#8 DONE 0.0s
#9 [internal] load build context
#9 transferring context: 22.60MB 0.3s done
#9 DONE 0.3s
#8 [builder 1/6] FROM docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#8 DONE 0.2s
#8 [builder 1/6] FROM docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#8 extracting sha256:7b229517ed222597277036d94892173207d01a731ab3a88eeb1de2e963a2c788 0.1s done
#8 sha256:c8e3100880d834be60a9562375f4c5018b2cb26d64591bd0060aa3a80b935f43 126B / 126B 0.2s done
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 0B / 71.15MB 0.3s
#8 sha256:c012d78a862fe59ae6789c91f982ea68a882c9c44bbde97013914458dcdd9c4c 0B / 244.47kB 0.2s
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 11.53MB / 71.15MB 0.6s
#8 sha256:c012d78a862fe59ae6789c91f982ea68a882c9c44bbde97013914458dcdd9c4c 244.47kB / 244.47kB 0.7s done
#8 extracting sha256:c012d78a862fe59ae6789c91f982ea68a882c9c44bbde97013914458dcdd9c4c 0.1s done
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 35.65MB / 71.15MB 0.9s
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 49.28MB / 71.15MB 1.1s
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 59.77MB / 71.15MB 1.2s
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 71.15MB / 71.15MB 1.4s
#8 sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 71.15MB / 71.15MB 1.5s done
#8 extracting sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b
#8 extracting sha256:82e0bb27ae78502103ed383ed33346f88a83943fb7bd93977850d5890ee8fb3b 5.5s done
#8 DONE 7.3s
#8 [builder 1/6] FROM docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#8 extracting sha256:c8e3100880d834be60a9562375f4c5018b2cb26d64591bd0060aa3a80b935f43 0.0s done
#8 extracting sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1 0.0s done
#8 DONE 7.3s
#10 [builder 2/6] WORKDIR /app
#10 DONE 1.0s
#11 [builder 3/6] COPY go.mod go.sum* ./
#11 DONE 0.0s
#12 [builder 4/6] RUN go mod download || true
#12 DONE 8.5s
#13 [builder 5/6] COPY . .
#13 DONE 0.3s
#14 [builder 6/6] RUN go mod tidy && CGO_ENABLED=0 go build -ldflags "-s -w -X main.Version=0.174.0 -X main.BuildTime=2026-10-27T18:21:23Z" -o /felhom-hub ./cmd/hub/
#14 0.393 go: downloading gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
#14 0.428 go: downloading modernc.org/fileutil v1.3.40
#14 0.428 go: downloading github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e
#14 0.444 go: downloading golang.org/x/term v0.47.0
#14 0.604 go: downloading modernc.org/ccgo/v4 v4.30.1
#14 0.604 go: downloading modernc.org/goabi0 v0.2.0
#14 0.604 go: downloading golang.org/x/tools v0.38.0
#14 0.604 go: downloading modernc.org/cc/v4 v4.27.1
#14 0.960 go: downloading modernc.org/opt v0.1.4
#14 0.960 go: downloading modernc.org/sortutil v1.2.1
#14 0.960 go: downloading modernc.org/strutil v1.2.1
#14 0.960 go: downloading modernc.org/token v1.1.0
#14 1.085 go: downloading modernc.org/gc/v2 v2.6.5
#14 1.085 go: downloading golang.org/x/mod v0.29.0
#14 1.085 go: downloading modernc.org/gc/v3 v3.1.1
#14 1.189 go: downloading github.com/hashicorp/golang-lru/v2 v2.0.7
#14 1.591 go: downloading golang.org/x/sync v0.17.0
#14 DONE 23.2s
#15 [stage-1 2/5] RUN apk add --no-cache ca-certificates tzdata
#15 CACHED
#16 [stage-1 3/5] COPY --from=builder /felhom-hub /usr/local/bin/felhom-hub
#16 DONE 0.5s
#17 [stage-1 4/5] RUN mkdir -p /data /etc/felhom-hub
#17 DONE 0.6s
#18 [stage-1 5/5] COPY assets/ /usr/share/felhom/assets-seed/
#18 DONE 0.7s
#19 exporting to image
#19 exporting layers
#19 exporting layers 3.1s done
#19 exporting manifest sha256:d0107a4fbc74cb575ab0ffdf9313560c09f5346604850cc63f9b94a094652a50 0.1s done
#19 exporting config sha256:9fd122d68270b24d81dff95b3ae0bd8087c395d88fe6dc1b0ec9a245b1c5fa41 0.0s done
#19 exporting attestation manifest sha256:3116065764acf34a6699cfbb21347c49b143104754c9a8139da589cdd8516d83 0.0s done
#19 exporting manifest list sha256:4f9ee04a9ff7c108a37a411be3c3de521b6acccb431c9e550c3567f7211e2e94 0.0s done
#19 naming to gitea.dooplex.hu/admin/felhom-hub:0.174.0 done
#19 unpacking to gitea.dooplex.hu/admin/felhom-hub:0.174.0
#19 unpacking to gitea.dooplex.hu/admin/felhom-hub:0.174.0 0.3s done
#19 DONE 3.6s
[INFO] Pushing...
The push refers to repository [gitea.dooplex.hu/admin/felhom-hub]
93c1eb832da7: Waiting
44136fa355b3: Waiting
c89750c9a8b7: Waiting
e2de96513ba9: Waiting
e2de96513ba9: Layer already exists
93c1eb832da7: Layer already exists
44136fa355b3: Already exists
979db3c09e54: Pushed
c89750c9a8b7: Pushed
eb8433490c74: Pushed
142d086ef218: Pushed
0.174.0: digest: sha256:4f9ee04a9ff7c108a37a411be3c3de521b6acccb431c9e550c3567f7211e2e94 size: 856
[INFO] ╔══════════════════════════════════════╗
[INFO] ║ Build complete ✓ ║
[INFO] ╚══════════════════════════════════════╝
[INFO] Image: gitea.dooplex.hu/admin/felhom-hub:0.174.0
[INFO] Size: 73M
[INFO] Image pushed to registry.
[INFO] Deploy to k3s — GitOps ONLY. The manifest in git is the source of truth:
[INFO] 1. bump the image tag in /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/felhom-eu-clean/manifests/hub.yaml to 0.174.0, commit, push
[INFO] 2. hard-refresh + sync the ArgoCD app (auto-sync is OFF — the sync is deliberate):
[INFO] sudo kubectl -n argocd annotate application felhom argocd.argoproj.io/refresh=hard --overwrite
[INFO] sudo kubectl -n argocd patch application felhom --type merge \
[INFO] -p '"{"operation":"initiatedBy":{"username":"cc"}}"' -p '"{"operation":"sync":{"syncStrategy":{"apply":{}}}}"'
[INFO]
[INFO] NEVER `kubectl set image` or `kubectl apply` by hand — ArgoCD reverts it on the next
[INFO] sync, so the change looks applied, works for a while, and then silently disappears.
@@ -0,0 +1,154 @@
[STEP] 0/3 — Checking the registry: is 0.174.1 already published?
[INFO] 0.174.1 is not present in the registry — proceeding.
[STEP] 1/3 — Pulling latest code (the tree must be clean and at origin/main)...
[INFO] ╔══════════════════════════════════════╗
[INFO] ║ felhom-hub build ║
[INFO] ╚══════════════════════════════════════╝
[INFO] Version: 0.174.1 (build-hub.sh v1.25.0)
[INFO] Commit: de57c090
[INFO] Source: /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/felhom-eu-clean/hub
[INFO] Build: /mnt/5_hdd/felhom.eu/build/felhom-hub/workspace
[INFO] Image: gitea.dooplex.hu/admin/felhom-hub:0.174.1
[STEP] 2/3 — Syncing source to workspace...
[INFO] Source synced to /mnt/5_hdd/felhom.eu/build/felhom-hub/workspace
[INFO] App assets synced (263 files)
[INFO] Package structure verified ✓
[INFO] Running go mod tidy...
[STEP] 3/3 — Building Docker image...
[INFO] Building for current platform + pushing...
#0 building with "default" instance using docker driver
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 1.46kB done
#1 DONE 0.0s
#2 [internal] load metadata for docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#2 ...
#3 [auth] library/alpine:pull token for registry-1.docker.io
#3 DONE 0.0s
#4 [auth] library/golang:pull token for registry-1.docker.io
#4 DONE 0.0s
#5 [internal] load metadata for docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#5 DONE 1.6s
#2 [internal] load metadata for docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#2 DONE 1.6s
#6 [internal] load .dockerignore
#6 transferring context: 2B done
#6 DONE 0.0s
#7 [internal] load build context
#7 transferring context: 22.60MB 0.2s done
#7 DONE 0.2s
#8 [stage-1 1/5] FROM docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#8 resolve docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
#8 resolve docker.io/library/alpine:3.24.2@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6 0.5s done
#8 DONE 0.5s
#9 [builder 1/6] FROM docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c
#9 resolve docker.io/library/golang:1.27.3-alpine3.24@sha256:b8c9b82affbbb82a020bbcf998dad2ba9516944ac7e883524642a481a9505e9c 0.5s done
#9 DONE 0.6s
#10 [builder 3/6] COPY go.mod go.sum* ./
#10 CACHED
#11 [builder 2/6] WORKDIR /app
#11 CACHED
#12 [builder 4/6] RUN go mod download || true
#12 CACHED
#13 [builder 5/6] COPY . .
#13 DONE 1.2s
#14 [builder 6/6] RUN go mod tidy && CGO_ENABLED=0 go build -ldflags "-s -w -X main.Version=0.174.1 -X main.BuildTime=2026-10-27T19:22:07Z" -o /felhom-hub ./cmd/hub/
#14 0.852 go: downloading gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
#14 0.888 go: downloading modernc.org/fileutil v1.3.40
#14 0.888 go: downloading github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e
#14 0.952 go: downloading golang.org/x/term v0.47.0
#14 1.848 go: downloading modernc.org/goabi0 v0.2.0
#14 1.848 go: downloading modernc.org/cc/v4 v4.27.1
#14 1.848 go: downloading golang.org/x/tools v0.38.0
#14 1.848 go: downloading modernc.org/ccgo/v4 v4.30.1
#14 1.976 go: downloading modernc.org/token v1.1.0
#14 1.976 go: downloading modernc.org/sortutil v1.2.1
#14 1.976 go: downloading modernc.org/strutil v1.2.1
#14 1.976 go: downloading modernc.org/opt v0.1.4
#14 2.061 go: downloading modernc.org/gc/v2 v2.6.5
#14 2.061 go: downloading golang.org/x/mod v0.29.0
#14 2.061 go: downloading modernc.org/gc/v3 v3.1.1
#14 2.195 go: downloading github.com/hashicorp/golang-lru/v2 v2.0.7
#14 2.659 go: downloading golang.org/x/sync v0.17.0
#14 DONE 23.6s
#15 [stage-1 2/5] RUN apk add --no-cache ca-certificates tzdata
#15 CACHED
#16 [stage-1 3/5] COPY --from=builder /felhom-hub /usr/local/bin/felhom-hub
#16 DONE 0.1s
#17 [stage-1 4/5] RUN mkdir -p /data /etc/felhom-hub
#17 DONE 0.3s
#18 [stage-1 5/5] COPY assets/ /usr/share/felhom/assets-seed/
#18 DONE 0.2s
#19 exporting to image
#19 exporting layers
#19 exporting layers 1.7s done
#19 exporting manifest sha256:52497a2e4ced33643f95e2461b55c7632e271e6f30c2b4c1570e5274e1b69d7a done
#19 exporting config sha256:590eb0076b318559b5b7f3136359f369e2ee24ff9ace6cd9baedf44369282fc4 done
#19 exporting attestation manifest sha256:a310eaff8556f58dccd2ff8d2dcb4c3992fbeb1b4cdee1e97c821f8ef5e8eb21 0.0s done
#19 exporting manifest list sha256:bb43f88acd59b8b20914381932eb2d9f779d44c190c34d3349eed91d37ce71fb done
#19 naming to gitea.dooplex.hu/admin/felhom-hub:0.174.1 done
#19 unpacking to gitea.dooplex.hu/admin/felhom-hub:0.174.1
#19 unpacking to gitea.dooplex.hu/admin/felhom-hub:0.174.1 0.3s done
#19 DONE 2.1s
[INFO] Pushing...
The push refers to repository [gitea.dooplex.hu/admin/felhom-hub]
44136fa355b3: Waiting
93c1eb832da7: Waiting
e2de96513ba9: Waiting
63f117ea3134: Waiting
44136fa355b3: Waiting
93c1eb832da7: Waiting
e2de96513ba9: Waiting
63f117ea3134: Waiting
63f117ea3134: Waiting
b83b40f86264: Waiting
44136fa355b3: Waiting
93c1eb832da7: Layer already exists
e2de96513ba9: Waiting
e2de96513ba9: Layer already exists
44136fa355b3: Already exists
63f117ea3134: Pushed
36e3e6d81e76: Pushed
b83b40f86264: Pushed
f65c9a964e26: Pushed
0.174.1: digest: sha256:bb43f88acd59b8b20914381932eb2d9f779d44c190c34d3349eed91d37ce71fb size: 856
[INFO] ╔══════════════════════════════════════╗
[INFO] ║ Build complete ✓ ║
[INFO] ╚══════════════════════════════════════╝
[INFO] Image: gitea.dooplex.hu/admin/felhom-hub:0.174.1
[INFO] Size: 73M
[INFO] Image pushed to registry.
[INFO] Deploy to k3s — GitOps ONLY. The manifest in git is the source of truth:
[INFO] 1. bump the image tag in /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/0b76c4d7-9b39-4e4e-869d-d0d2c6006331/scratchpad/felhom-eu-clean/manifests/hub.yaml to 0.174.1, commit, push
[INFO] 2. hard-refresh + sync the ArgoCD app (auto-sync is OFF — the sync is deliberate):
[INFO] sudo kubectl -n argocd annotate application felhom argocd.argoproj.io/refresh=hard --overwrite
[INFO] sudo kubectl -n argocd patch application felhom --type merge \
[INFO] -p '"{"operation":"initiatedBy":{"username":"cc"}}"' -p '"{"operation":"sync":{"syncStrategy":{"apply":{}}}}"'
[INFO]
[INFO] NEVER `kubectl set image` or `kubectl apply` by hand — ArgoCD reverts it on the next
[INFO] sync, so the change looks applied, works for a while, and then silently disappears.
@@ -0,0 +1,40 @@
### 1: the block removed from the page
--- FAIL: TestR1299_ThePageShowsTheWindowBlockPerBranch (0.30s)
offsite_window_test.go:80: the page lacks "id=\"offsite-window\""
offsite_window_test.go:80: the page lacks "id=\"window-form\""
offsite_window_test.go:80: the page lacks "name=\"window\" value=\"confirm\""
offsite_window_test.go:80: the page lacks "Normal cap per window"
offsite_window_test.go:80: the page lacks "20 (half the snapshots"
offsite_window_test.go:80: the page lacks "at most 40"
offsite_window_test.go:80: the page lacks "none waiting"
### 2: max_remove ignored
--- FAIL: TestR1299_AGrantThroughTheFormStoresOneGrantWithItsLimit (0.14s)
offsite_window_test.go:118: stored grant: pending=true max=0, want true 33
--- FAIL: TestR1299_WithoutACountOnlyTheCeilingBounds (0.16s)
offsite_window_test.go:178: stored max 0, want 260
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 1.466s
FAIL
### 3: no upper bound
--- FAIL: TestR1299_ThePageShowsTheWindowBlockPerBranch (0.32s)
offsite_window_test.go:91: a limit above the snapshot count must render the refusal at the confirm step, not the confirm
--- FAIL: TestR1299_RefusalsStoreNothing (0.74s)
offsite_window_test.go:160: above the snapshot count: 303 Location="/customers/acme?flash=window_granted#offsite-window"
offsite_window_test.go:163: above the snapshot count: a grant was stored
offsite_window_test.go:166: above the snapshot count: 1 event(s) for a refused request
offsite_window_test.go:160: script above the snapshot count: 200 Location=""
offsite_window_test.go:163: script above the snapshot count: a grant was stored
### 4: no confirm check
--- FAIL: TestR1299_RefusalsStoreNothing (0.72s)
offsite_window_test.go:160: no confirm step: 303 Location="/customers/acme?flash=window_granted#offsite-window"
offsite_window_test.go:163: no confirm step: a grant was stored
offsite_window_test.go:166: no confirm step: 1 event(s) for a refused request
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 1.613s
FAIL
### 5: cancel not handled
--- FAIL: TestR1299_CancelRemovesAWaitingGrant (0.13s)
offsite_window_test.go:193: cancel with none waiting: Location="/customers/acme?flash=window_needs_confirm#offsite-window"
FAIL
FAIL gitea.dooplex.hu/admin/felhom-hub/internal/web 1.607s
FAIL
@@ -0,0 +1,8 @@
### 0.174.0's shape: dispatcher only, no row
--- FAIL: TestR1299_AGrantThroughTheFormStoresOneGrantWithItsLimit (0.14s)
offsite_window_test.go:142: stored rows: [] — want ONE info offsite_window_grant row naming the limit and who
--- FAIL: TestR1299_CancelRemovesAWaitingGrant (0.13s)
offsite_window_test.go:235: stored rows: [] — want ONE cancel row
### the block back in the Overview card (0.174.0's place)
--- FAIL: TestR1299_ThePageShowsTheWindowBlockPerBranch (0.28s)
offsite_window_test.go:95: the window block must sit in the Backup & DR tab (panel at 56541, block at 12480) — where the operator looked first
@@ -0,0 +1,46 @@
CC_D55_WINDOW: removed; committed and pushed; undo: git revert of the `rm CC_D55_WINDOW` commit
HUB_TOKEN_D55_WINDOW: removed; committed and pushed; undo: git revert of the `rm HUB_TOKEN_D55_WINDOW` commit
REHEARSAL_2026_10_20_APP_BOOKSTACK: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_APP_BOOKSTACK` commit
REHEARSAL_2026_10_20_APP_RADICALE: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_APP_RADICALE` commit
REHEARSAL_2026_10_20_APP_VAULTADMIN: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_APP_VAULTADMIN` commit
REHEARSAL_2026_10_20_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_BIND_TOKEN` commit
REHEARSAL_2026_10_20_BIND_TOKEN_B7: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_BIND_TOKEN_B7` commit
REHEARSAL_2026_10_20_BOOKSTACK_TOKEN: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_BOOKSTACK_TOKEN` commit
REHEARSAL_2026_10_20_DASH_PW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_DASH_PW` commit
REHEARSAL_2026_10_20_FAMILY_IRISZ: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_FAMILY_IRISZ` commit
REHEARSAL_2026_10_20_FAMILY_KATA: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_FAMILY_KATA` commit
REHEARSAL_2026_10_20_FAMILY_ODON: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_FAMILY_ODON` commit
REHEARSAL_2026_10_20_INSTALL_ROOTPW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_INSTALL_ROOTPW` commit
REHEARSAL_2026_10_20_IRISZ_PW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_IRISZ_PW` commit
REHEARSAL_2026_10_20_KATA_PW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_KATA_PW` commit
REHEARSAL_2026_10_20_ODON_PW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_ODON_PW` commit
REHEARSAL_2026_10_20_OLD_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_OLD_BIND_TOKEN` commit
REHEARSAL_2026_10_20_PAIRING_CODE: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_PAIRING_CODE` commit
REHEARSAL_2026_10_20_PAIRING_CODE_B7: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_PAIRING_CODE_B7` commit
REHEARSAL_2026_10_20_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_SETUP_CODE` commit
REHEARSAL_2026_10_20_SETUP_CODE_B7: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_SETUP_CODE_B7` commit
REHEARSAL_2026_10_20_SMB_PW: removed; committed and pushed; undo: git revert of the `rm REHEARSAL_2026_10_20_SMB_PW` commit
FIXES_2026_10_21_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm FIXES_2026_10_21_BIND_TOKEN` commit
FIXES_2026_10_21_PAIRING_CODE: removed; committed and pushed; undo: git revert of the `rm FIXES_2026_10_21_PAIRING_CODE` commit
FIXES_2026_10_21_ROOTPW: removed; committed and pushed; undo: git revert of the `rm FIXES_2026_10_21_ROOTPW` commit
FIXES_2026_10_21_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm FIXES_2026_10_21_SETUP_CODE` commit
NIGHT_2026_10_21_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_21_BIND_TOKEN` commit
NIGHT_2026_10_21_PAIRING_CODE: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_21_PAIRING_CODE` commit
NIGHT_2026_10_21_ROOTPW: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_21_ROOTPW` commit
NIGHT_2026_10_21_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_21_SETUP_CODE` commit
NIGHT_2026_10_23_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_BIND_TOKEN` commit
NIGHT_2026_10_23_DASH_PW: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_DASH_PW` commit
NIGHT_2026_10_23_PAIRING_CODE: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_PAIRING_CODE` commit
NIGHT_2026_10_23_ROOTPW: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_ROOTPW` commit
NIGHT_2026_10_23_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_SETUP_CODE` commit
NIGHT_2026_10_23_VAULTED_ROOTPW: removed; committed and pushed; undo: git revert of the `rm NIGHT_2026_10_23_VAULTED_ROOTPW` commit
RIG_2026_10_25_DASH_PW: removed; committed and pushed; undo: git revert of the `rm RIG_2026_10_25_DASH_PW` commit
RIG_2026_10_25_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm RIG_2026_10_25_SETUP_CODE` commit
REL_2026_10_26_ROOTPW: removed; committed and pushed; undo: git revert of the `rm REL_2026_10_26_ROOTPW` commit
SWEEP_2026_10_24_BIND_TOKEN: removed; committed and pushed; undo: git revert of the `rm SWEEP_2026_10_24_BIND_TOKEN` commit
SWEEP_2026_10_24_DASH_PW: removed; committed and pushed; undo: git revert of the `rm SWEEP_2026_10_24_DASH_PW` commit
SWEEP_2026_10_24_PAIRING_CODE: removed; committed and pushed; undo: git revert of the `rm SWEEP_2026_10_24_PAIRING_CODE` commit
SWEEP_2026_10_24_ROOTPW: removed; committed and pushed; undo: git revert of the `rm SWEEP_2026_10_24_ROOTPW` commit
SWEEP_2026_10_24_SETUP_CODE: removed; committed and pushed; undo: git revert of the `rm SWEEP_2026_10_24_SETUP_CODE` commit
R_C11_REWALK: removed; committed and pushed; undo: git revert of the `rm R_C11_REWALK` commit
R_PART4: removed; committed and pushed; undo: git revert of the `rm R_PART4` commit
+6
View File
@@ -26,6 +26,12 @@
---
## 2026-10-27 (evening) — R-1299 closed: the clean-up window button (full text of the row: `git show 714ee236:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-1299** | **The operator's one-shot clean-up grant (`POST /offsite/window-grant/<customer>`, optional `max_remove`) has no button on any hub page** (P4) | CLOSED 2026-10-27 evening — hub v0.174.0 (`d85198b8`, CI 2433) + v0.174.1 (`de57c090`, CI 2437; the event row and the Backup & DR tab). The customer page's „Backup & DR" tab has the „Off-site clean-up window" card: state, an optional limit (refused above the store's snapshot count; ceiling 500), a confirm step, the same route and rule; a waiting grant can be cancelled; every grant and cancel is a stored operator-only info event. Live: the operator granted on tester-1 (20:07 CET, grant read back `1`, log „by operator session") and cancelled on 0.174.1 (20:42 CET: grant gone, one stored cancel row). Nothing was removed. | `audits/r1299-window-button-2026-10-27/` |
## 2026-10-27 (day, attended) — security app updates: three rows closed; R-1314 was filed and closed the same day (full text of the rows: `git show 708a3451:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
+4 -4
View File
@@ -268,7 +268,7 @@ 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 |
| **R-1194** | Security & access | P3 | **On a fresh box the operator's break-glass SSH door was broken from the first minute: `oob_degraded` „felhom-sshd config invalid (sshd -t fails)".** Rehearsal 2026-10-20, Tester4-816e21, mail at 12:03 UTC (one minute after the host record was created): details `active:false, config_invalid:true, felhom_sshd_port:0, operator_key_configured:false, operator_key_reported:true, reachable:false`. Possibly the shape of a customer with no operator key set (Tester4 is a test customer) — then the alarm is a false one on every such box; or a real config defect. Not diagnosed (no shell). Checked the same day: the only `oob_degraded` in the operator mailbox in 30 days, and no recovery mail followed; the report says port 0. Two candidate mechanisms in the agent (`internal/felhomsshd/health.go:63-66`): `sshd -t -f <conf>` run by the NON-root agent fails on unreadable host keys, or a config rendered before the desired state arrived (port 0). **CAUSE FOUND AND FIXED 2026-10-21 night** (`audits/night-2026-10-21/P5-checks/`): reproduced on a fresh box from the public ISO — `oob_degraded` mailed 20:34 CEST while felhom-sshd was applied 0.4 s after the check and is healthy (port 8822 listening, `sshd -t` passes as the agent's user through its sudo grant — so the non-root theory is FALSE; without sudo it fails on the host key, which the agent never does). The first host report runs before the felhom-sshd loop's first pass and reads a config never rendered. Fix: agent `0da792e` — no OOB stanza until the first pass has run (the hub does not evaluate a missing one; a failed pass counts). `TestR1194_NoOOBStanzaBeforeTheFirstPass` red-proved. | WATCHING — released in agent v0.162.0 (2026-10-22, operator yes; on the 3 test boxes); the false alarm comes only on a FRESH box's first report | the next fresh install with agent ≥ 0.162.0 | At the next fresh install read the hub's events for `oob_degraded` in the first hour (none expected); then close | CC |
| **R-1237** | Security & access | P3 | **The nightly CC-secrets sweep found stored one-time codes in a session record dated 2026-10-23: 14 names in one file (bind tokens and setup codes of test boxes — NIGHT_2026_10_23_*, FIXES_2026_10_21_*, NIGHT_2026_10_21_*, REHEARSAL_2026_10_20_* — and R_DEMO_FELHOM in a file of 2026-10-20).** Mail „[DooPlex CC secrets] 2 secret name(s) found in old session records", 2026-10-23 01:57 UTC, read in the day session (names only). One file holding all of them suggests one command printed many stored values at once (a listing or a dump) in the night session. All but R_DEMO_FELHOM are one-time install codes of test boxes; NIGHT_2026_10_23_* die when Tester4-16fdef is deleted (the operator's do list). | OPEN | — | CC: find the file and the command by the sweep's own report (names only), add a hook shape for that command if it is not one; R_DEMO_FELHOM (demo-felhom's recovery code) — the operator decides whether to rotate it | CC |
| **R-1237** | Security & access | P3 | **The nightly CC-secrets sweep found stored one-time codes in a session record dated 2026-10-23: 14 names in one file (bind tokens and setup codes of test boxes — NIGHT_2026_10_23_*, FIXES_2026_10_21_*, NIGHT_2026_10_21_*, REHEARSAL_2026_10_20_* — and R_DEMO_FELHOM in a file of 2026-10-20).** Mail „[DooPlex CC secrets] 2 secret name(s) found in old session records", 2026-10-23 01:57 UTC, read in the day session (names only). One file holding all of them suggests one command printed many stored values at once (a listing or a dump) in the night session. All but R_DEMO_FELHOM are one-time install codes of test boxes; NIGHT_2026_10_23_* die when Tester4-16fdef is deleted (the operator's do list). | **OPEN** — 2026-10-27 (operator yes): the dead names were REMOVED from the store with `sec rm` (46, one commit each; `audits/r1299-window-button-2026-10-27/sec-rm.txt`, names only): every one-time code this row names is gone; R_DEMO_FELHOM stays (unsure). Consequence: the nightly sweep no longer watches for the removed values — they open nothing (their boxes are destroyed). The session transcripts themselves are unchanged — that question stays here. **Earlier:** OPEN | — | CC: find the file and the command by the sweep's own report (names only), add a hook shape for that command if it is not one; R_DEMO_FELHOM (demo-felhom's recovery code) — the operator decides whether to rotate it | CC |
| **R-1248** | Security & access | P3 | **An app's own numbers can lie: an attacker with code inside an app's container can answer the app's API with whatever matches its flood, and the upload watch (R-1244) would then call it explained.** Measured remedy, not built: a per-container upload ceiling with `tc` from a helper container in the app's network namespace — on scratch 9202 (unprivileged) a flood was held at 19.9–20.1 Mbit/s and the app kept answering (`audits/SPIKE-upload-watch-2026-10-23.md` §5). Costs: a helper image with `tc` (a new external image, or `tc` in the controller image), `NET_ADMIN` for that helper, re-applied after each app restart. | OPEN | an operator decision (the new image / capability) | Decide; if yes, CC builds the ceiling for the download and media apps first | operator |
| **R-1309** | Security & access | P4 | **What no automated source shows: a fix announced only by mail, forum or a blog we do not read; a security fix nobody calls one (cloudflared 2026.10.2's „bump distroless image"); closed-source apps (Plex, Emby).** The watch (R-1303) shows these only as BEHIND (weekly). The vendor mails the operator receives stay a source. `audits/security-watch-2026-10-26/SPIKE.md` | OPEN — filed 2026-10-26 (a stated limit) | — | none until a vendor offers a feed; add a vendor's feed to `security_watch.FEEDS` when one exists | CC |
| **R-1312** | Backup & restore | P3 | **The hub customer page's off-site switch did not reach the box: on Tester 1 (2026-10-27, operator-attended, ~14:00–14:12 UTC) the box's saved off-site target stayed `enabled` and its `controller.yaml` `offsite.enabled: true` (unchanged since 2026-10-04) while the hub showed off-site OFF.** So what the switch stops on the box side is not known: the controller reads `cfg.Offsite.Enabled` only at start (`cmd/controller/main.go:890`), and nothing rewrote the file. Either the switch is hub-side only by design (then the page must say so), or a box keeps pushing off-site while the operator believes it is off. Found by the R-1218 live attempt (R-1218 closed 2026-10-27 with its live proof waived by the operator). | OPEN | — | Read the hub handler behind the switch and what it changes (config, tenancy, storage access); then one attended run: switch off, watch the box's next off-site push (does it run, does it fail at the storage) | CC |
@@ -319,7 +319,7 @@ stopping line that lies.
| **R-1233** | Monitoring & notifications | P4 | **`[ERROR] [stacks] execCommand failed: exit status 1` names no command.** Seen once a night (2026-10-21 00:30:49 and 2026-10-23 02:17:14 UTC, each right after an app stopped for its dump); the command and stderr go only into the returned error, which the caller may drop (`internal/stacks/manager.go:1757`). An ERROR nobody can judge. | OPEN | — | Log the command's first two words (`docker inspect`, never the arguments — `docker exec … psql` can carry a password) and the stderr's first line; find the caller of the nightly one | CC |
| **R-1296** | Monitoring & notifications | P4 | **When ep0's tenancy script refuses, the hub log shows only its one-line error — PBS's own reason (the 403 `missing Datastore.Audit|Datastore.Backup on …`) stays in the script's stderr, which the hub drops on a refusal.** Found 2026-10-26 diagnosing R-1295: the cause was visible only by reading ep0's proxy journal over root SSH. `hub/internal/tenantsync/client.go` `parseResponse` returns the JSON error and ignores stderr; the script keeps secrets off stderr by design (its header), so the tail is safe to log. | OPEN | — | append the last ~3 stderr lines to the refusal error (bounded, never stdout); a test that a secret on stdout never appears | CC |
## Hub & operator — 12 rows (P3 3, P4 9)
## Hub & operator — 13 rows (P3 4, P4 9)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -335,6 +335,7 @@ stopping line that lies.
| **R-1056** | Hub & operator | P4 | **Five operator routes fit no token group, so they are session only until the operator places them:** apps reset-telemetry and dismiss-issues, the Configuration asset re-seed, the off-site WireGuard endpoint save, the off-site abandon-cancel (`05` §17.3). Nothing needs them by token today. | WAITING-ON-OPERATOR — filed 2026-10-15 by the token build | operator decision | Place each in a group, or leave session only | operator |
| **R-1164** | Hub & operator | P4 | **Move the website to Hetzner (maybe the JARRs.eu prod server) — an operator idea, no decision asked yet.** If it is done, the website's git-syncs must read the public `https://gitea.dooplex.hu/admin/felhom.eu.git` again: since 2026-10-19 (R-1163) all four read Gitea in-cluster, which works only inside the DooPlex cluster (`01-topology-and-trust.md` §7, `manifests/webpage.yaml`). | OPEN | the operator's idea becoming a decision | When decided: switch the four `--repo` values together (gate 6 keeps them identical); keep `--max-failures=-1` | operator |
| **R-1293** | Hub & operator | P4 | **Once a year the hub's 02:00 database copy never reaches ep0, and the 20:00 copy is pushed twice instead.** Measured 2026-10-26 during R-1183's read (`audits/night-2026-10-25/r1183-measurement-2026-10-26.txt`): on 2026-10-25 (the clocks go back) 02:00–03:00 happens twice. The hub's `nextDailyRun` uses `time.Date`, which picks the SECOND 02:00 (CET, 01:00Z — measured with Go). DooPlex's `felhom-hub-db-backup.timer` fired at the FIRST 02:30 (CEST, 00:31Z) and pushed `hub-20261024T180000Z.db`, 391 min old — under the 7 h limit, so no refusal and no alarm; the 08:30 push took the 08:00 copy. Effect: for one morning a year ep0's newest hub copy is 12 h old, not 6 h; nothing is lost on the hub. The spring night (2027-03-28, 02:00–03:00 does not exist) is not measured. | OPEN | — | pick one: the push refuses (and says so) a newest snapshot older than the slot it serves, or the hub and the timer agree on one 02:00; then read the 2027-03-28 night (DUE-CHECKS) | CC |
| **R-1326** | Hub & operator | P3 | **The hub's start-up eats half its liveness margin: hub 0.174.0 took 36 s from „starting" to „Listening on :8080" (0.174.1: 31 s), and the liveness probe (`initialDelaySeconds` 5, `periodSeconds` 30, `failureThreshold` 3) kills the container after ~65 s of failed probes.** Seen 2026-10-27 at both deploys: two `Liveness probe failed … connection refused` events at start, then ready. The hub's database keeps growing (559 MB on 2026-10-27 04:05); a start-up past ~65 s would restart the hub in a loop at every deploy. No startup probe is set. | OPEN | — | Measure what the start spends its 30 s on (DB open, migrations, checkers); then a `startupProbe` (e.g. 10 s × 18) in `manifests/hub.yaml`, or move the slow work after `Listen` | CC |
## Business & legal — 14 rows (P2 4, P3 2, P4 8)
@@ -355,7 +356,7 @@ stopping line that lies.
| **R-990** | Business & legal | P4 | **Operator: read `documentation/audits/forum-research-2026-10-12/FELHOM.md` §5 and rule on each recommendation.** READ-ONLY research 2026-10-12: three years (#7943–#11092, 2,908 posts, 195 people, 82 beginner threads) of the public PROHARDVER! topic „Home server / házi szerver építése”, tagged and counted by person (`FINDINGS.md`, method and spot-check in `METHOD.md`; no username or post text in git). Ten ranked recommendations, none acted on; the top three: a „which machine works” page (8 of 33 beginners who name their hardware own only a Synology, a router disk or a Raspberry Pi — the installer cannot run there; the same gap is R-989's first recommendation from a different source, and the shipped-page half is R-991), „you do not choose an operating system — we do” (the biggest beginner question, 20 of 82 threads), and „bring your old office PC” as the cheap entry. Catalog gap (≥ 3 people): a DLNA server (7), an ad/DNS blocker (5), a camera recorder (5). | **WAITING-ON-OPERATOR** — filed 2026-10-12 | the operator's reading | Operator: rule on each of the ten (yes / no / later). If nothing is done: nothing changes; the research ages | operator |
| **R-1035** | Business & legal | P4 | **Did the „felhő tárhely” page earn anything? Two dated checks.** **Baseline (reported by the operator from Search Console, 2026-10-14, last 3 months):** 1 click, 48 impressions, CTR 2.1 %, average position **73.6**; „felhő tárhely” 19 impressions, „felhőtárhely” 5, „felhő alapú tárhely” 3. CC measured separately that the phrase „felhő tárhely” appeared **0 times** on the home, FAQ and backup pages — Google was showing the home page for those queries by accident. `/sajat-felho-tarhely` + `/en/own-cloud-storage` published **2026-10-14**; full baseline, price sources and the power provenance in `audits/seo-2026-10-14/BASELINE.md`. **What to measure, both times:** impressions and average position for „felhő tárhely” and „saját felhő tárhely”, plus the new page's own impressions, clicks and position. **A null result at +4 weeks means nothing on its own** — a new page on a site at position 73.6 takes weeks to months to move, and „felhő tárhely” alone is held by Microsoft, Google and the big list sites; the page aims at the two longer phrases. That is why there are two checks and not one, and why neither should be read as a verdict on the copy. **There is no Search Console connector in this project, so CC cannot read these numbers** — the operator reads them, CC records and compares. **Blocked until the operator asks for indexing** (Search Console → URL-ellenőrzés → the URL → „Indexelés kérése”): until that happens, or Google finds the page itself, both checks read zero for a reason that is not the page. **This is a slow channel and does not replace talking to people.** | **WATCHING** | operator: request indexing, then supply the Search Console read | 2026-11-11: first read (+4 weeks); 2026-12-09: second (+8 weeks), compared with the first | operator + CC |
## Process & tooling — 34 rows (P3 8, P4 26)
## Process & tooling — 33 rows (P3 8, P4 25)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -407,5 +408,4 @@ stopping line that lies.
| R-1035 | 2026-12-09 | the same three numbers 8 weeks after publishing, compared with the 4-week read; a null at 4 weeks is not a verdict, both reads are needed; detail in the R-1035 row |
<!-- DUE-CHECKS-END -->
| **R-1297** | Process & tooling | P3 | **CI waits dominate a working day: felhom.eu's CI run takes ~13 min and the ONE runner makes every repo's job wait behind it (up to 10–17 min queued).** Measured 2026-10-26 (operator: „these CI waiting times are getting out of hand"): since 10-25 felhom.eu runs median 793 s, the controller/agent/catalog 57–102 s; queue median 5 s, max 548–1023 s. In run 2366, ~10 of 13 minutes are four suites — `test_gate_decoys.py` 232 s, `test_due_checks_gate.py` 133 s, `test_repo_gates.py` 132 s (it runs the whole gate set a SECOND time), `dooplex-offsite/test_dooplex_offsite.py` 69 s — plus `hub-gofmt` ~110 s (a cold Go cache per run). **Ruled 2026-10-26 by the operator: BOTH** — (A) trim felhom.eu CI: the heavy suites only when their own files change, no duplicate full run, a Go cache for hub-gofmt; (B) a second CI runner on DooPlex. | **OPEN — (A) BUILT 2026-10-26 evening, closes after the first nightly run (2026-10-27 03:20 UTC) is read.** Both runners proven at once (jobs 2374 + 2375 on `felhom-gates-runner` / `-2`, 17:11:41Z). felhom.eu CI, one push each: **(a) docs only** 810 s (old runner) → 351 s (2 CPUs) → see the close-out; **(b) a gate script** 351 → 203 s (`ecd82aba`, job 2376); **(c) a hub change** 351 → 118 s (`7e57e824`, job 2378). Heavy suites by path + tag + nightly; `test_repo_gates.py` and due-checks group G test the wiring (130 s each → under 3 s). Decoys through CI's entry point with the filter ON: a docs-only register break → red; an inert gate → GREEN at first (a decoy hole, fixed in `e33ec821`) → red. No Go cache: CI has no Go (the 110 s was security-scan, now 27 s). `audits/r1297-ci-trim-2026-10-26/`. (B) DONE by the operator (`09` §3 decision 267). | — | (A) CC, a run of its own (a CI scope change, ruled); (B) a DooPlex change — a brief or the homelab session; and CC waits on CI only before a release or at the end of a run | CC |
| **R-1299** | Process & tooling | P4 | **The operator's one-shot clean-up grant (`POST /offsite/window-grant/<customer>`, optional `max_remove`) has no button on any hub page** — the operator could not find it on 2026-10-26 (D55) and had to make a one-purpose access token so CC could call the route. The route, its scope (`offsite:write`) and its event exist (R-833); only the form is missing. | OPEN | — | a small form on the Off-site page (customer, optional cap ≤ 500), with a render test for both branches | CC |
| **R-1302** | Monitoring & notifications | P4 | **A fresh install's agent logs ERROR „store-grant: GRANT WAS MISSING AND HAS BEEN SELF-REPAIRED — investigate the loss (R-190)" and „capability DEGRADED" for `felhom-pbs` at its FIRST start, although the grant was simply never made yet** — the whole-guest tier is armed (21:00:45) before the PBS storage exists (created 21:00:57 on the hub's descriptor). Seen 2026-10-26 night on the R-1232 BYO box (VM 9375, `audits/night-fixes-2026-10-26/r1232/3-first-run.txt`) and on 2026-10-25's BYO box (`degraded=pve:store-grant:felhom-pbs`). An ERROR that says „investigate the loss" on every new box teaches the reader to skip it. | OPEN | — | on the first grant of a tier whose storage did not exist before, log INFO „granted" instead of the loss ERROR; keep the ERROR for a grant that existed and vanished; a test for both | CC |
+8
View File
@@ -61,6 +61,14 @@ sudo kubectl -n felhom-system rollout status deploy/hub deploy/contact-mailer -
- **hub:** trigger a notification (or the dispatcher test path) → mail arrives.
- **Gmail:** send a test from `info@felhom.eu` → delivers.
### Removing a name from the CC store
`sec rm NAME` (cc-secrets ≥ 0.3.0): one name per call; an unknown name is refused; it commits `rm NAME` and pushes, and
prints the name only. Undo = `git revert` of that commit in the cc-secrets repo. Remove a name only when its box or token
is gone (proof: the host is gone from the hub, or the token is revoked) and the operator approved the list
(2026-10-27: 46 dead test-box names, `audits/r1299-window-button-2026-10-27/sec-rm.txt`). Details: cc-secrets
`ARCHITECTURE.md` §4.
### Rotating away from a compromised key (ordered — load-bearing)
1. Create the **new** send-scoped key in Resend; store it out-of-band; on host 180 put it in the CC store from a 0600