Day 2026-10-19: agent 0.160.0 (released, not vouched), controller 0.332.0 floor (one sign-in R-1100, escrow confirm R-1151), hub 0.159.0 deployed (R-1150, R-1154), R-1153 not a defect, old-restic check passed; register 168 -> 166 (closed 10, opened R-1155..R-1162)
gates / gates (push) Successful in 7m20s

This commit is contained in:
2026-10-19 05:38:38 +02:00
parent 200ab6530b
commit db2371c6f5
35 changed files with 525 additions and 39 deletions
+12
View File
@@ -16,6 +16,18 @@
> and holds nothing of its own; this file does hold its own content, namely the standing rulings below.
> **2026-10-19 (day) — controller 0.332.0 is the floor (MinAgent 0.158.1, 05:24 local); agent 0.160.0 released and on the
> three test hosts, NOT vouched; hub 0.159.0 DEPLOYED (operator yes in advance).** One sign-in for the file manager (R-1100,
> D16): dashboard pass → host-only `felhom_files` cookie → traefik forwardAuth `/__felhom_gate/files` sets the per-box
> SECRET header (`<data>/filebrowser-sso-header`, never logged) naming FileBrowser user `felhom-household`; traefik makes a
> RELATIVE Location in a forwardAuth answer absolute against the auth server — always answer absolute URLs. R-1151: the
> escrow package uploads at `POST /escrow/ceremony/confirm` (two typed words), an old controller keeps the claim order.
> R-1149 (D17): local tier `keep-daily=N`. R-1154 rule (after a security review of the helper's first draft): the
> host-enroll reuse path NEVER re-issues — the passphrase must not reach the operator's re-issue powers; it mails the
> operator. R-1153 was the test file (FileBrowser previews .docx with MuPDF before OnlyOffice). R-887 closed (381 jobs, 0
> lost since S21). The old-restic check after demo-hp's first real 0.19.1 prune passed. Register 168 → 166.
> `REPORT-day-2026-10-19.md`.
> **2026-10-18 (evening) — controller 0.331.0 is the floor (MinAgent 0.158.1): OnlyOffice opens and saves from FileBrowser
> and Nextcloud, FileBrowser back at once after a restart (R-1018), a removal deletes a volume the definition dropped
> (R-1117); catalog `30eea97`: Immich/Nextcloud Redis without a volume; golden 0.331.0 baked, NOT vouched; a fresh install
+156
View File
@@ -0,0 +1,156 @@
# REPORT — day 2026-10-19
The operator answered the two STOPs in advance („YES to the agent and the hub release") and earlier: golden 0.331.0
vouched; D16 (one sign-in) yes; D17 „one per day, 3 days". No question was asked during the session.
## In plain words
- **Agent 0.160.0 runs on all three test hosts.** A new recovery code replaces the old one only after the household types
two of its words back; a broken-into box cannot flood the whole-box backups; the local copies are one per day for 3
days; an empty partition does not ask „data will be erased". **It is not vouched — that is yours.**
- **One sign-in for the file manager is live on every box** (controller 0.332.0, the floor). „Fájlok ↗" opens the app's
folder with no second login. A sign-out on the dashboard asks again. No app on the box can pretend to be the household.
- **Hub 0.159.0 is live.** A refused backup has its own alarm text. A reinstall over an old host record now mails you; it
does not re-issue the codes by itself (a security review stopped the first draft that did).
- **The preview error (R-1153) was a broken test file**, not the box.
- **The old backup program still reads demo-hp's off-site copies** after the first real clean-up with the new one.
## 1. Agent 0.160.0 (R-978, R-973, R-1093, R-1149, R-1151) — `audits/day-2026-10-19/P1/`
- **Content:** R-978 (an abandoned second ceremony keeps the first code), R-973 (the cadence cap: daily tier 3 per 24 h,
off-site 1 per 6 days), R-1093 (an empty partition is not data), **R-1149** (local tier `keep-daily=N`, operator D17),
**R-1151** (the package uploads only at `POST /escrow/ceremony/confirm` with two typed words; a claim without
`defer_upload` keeps the R-978 order, so an old controller still completes a ceremony). Commits `1b6e316`, `c11e3e2`,
`124164d` (CHANGELOG), `4545819` (two texts, after the release, unreleased). Released with `release-agent.sh`: sha256
`7ebb547f2b1ab93544ba828d3040351b60c4126327a5b85bbcb33f8f2b3dfca1`, bundle
`6b06090190ff609af1fe9842447ebf385fd52da4be382bc7ca708b558c8e202f`, round trip verified, tag `v0.160.0`.
- **Red-proofs:** keep-last for the primary → „primary tier prune = "keep-last=3"" and „holds 0 copies made before the
compromise"; the claim's defer flag ignored → „a code that was shown but never typed back replaced the package: code 1
no longer opens it (uploads=2)" (all three abandon paths); the word check skipped → „one wrong word: got 200".
- **demo-hp proof** (signed jobs 01:22 UTC, running 01:23, „update committed" 01:24; probe 75/75):
- a normal „Mentés most" of 9201: green, 6.9 GB in 443 s; yesterday's copy pruned (keep-daily=1, the same copy keep-last
would prune); the cap's history on disk holds both successes;
- escrow through the wizard's own calls: ceremony 1 — one wrong word 422, the right words 200; code 1 opens the hub's
package, code 0 is a kept earlier one (422, superseded at the confirm second 01:47:47). Ceremony 2 shown and abandoned —
code 1 still opens it (200), code 2 opens nothing (400). demo-hp's stored code is now code 1 (CC's secret store);
- R-1093 on the real route: a 64 MB RAM disk (`scsi_debug`, `/dev/sdb`), one empty GPT partition → `POST /disks/format`
→ 200 formatted, no confirmation; control: the same partition, now ext4 → 403 `needs_confirmation`. RAM disk removed.
**A loop device cannot be used:** the route and the probe accept only sd/nvme/vd/hd names (by design).
- **The answer:** the operator's „yes" (in advance). Tester 1 and demo-felhom took 0.160.0 by signed jobs (01:52 UTC →
01:54 / 01:55, probe 75/75, `keep-daily=3` armed on both); the hub's host rows read 0.160.0 for all three, and fresh
reports after the hub restart. **Old controller with the new agent:** pinned by `TestEscrowCeremony_R1151_OldControllerKeepsTheClaimOrder`; **new controller with agent 0.159.2:** pinned by `TestR1151_OldAgentClaimCompletesAtOnce` and the agentapi test — no host runs 0.159.2 any more, so not walked live.
## 2. One sign-in (R-1100) — `audits/day-2026-10-19/P2/`, `P4/05-*`, `P4/06-*`
- **Built** exactly the design note's recommendation (controller `9bdd8f8`, `601bdba`): the dashboard pass, the host-only
files cookie (derived from the session fingerprint, the R-976 shape), the per-box secret header name, the forwardAuth
answer; header user `felhom-household` with the household's file rights from userDefaults; `logoutRedirectUrl` = the
dashboard; `admin` stays a password user; the data tab's „asks for its own password" note only without the sign-in.
- **Tests, each red-proved:** replayed pass, expired pass, pass for another host (two guards: the host check and the
host-bound session key — red only with both removed), wrong X-Forwarded-Host, no session → no pass, signed out → asks
again (and after a password change), cross-site → no pass, dot-segments in `to`, the absolute redirect, the config
trusts only the secret name, the header user's rights, the route asks the controller and strips the header, the note.
- **Live, found and fixed before release:** on rc.2 the pass answer's relative redirect became
`http://felhom-controller:8080/files/` — traefik resolves a forwardAuth answer's Location against the auth server. Fixed
(absolute), red-proved, rc.3. A background security review then flagged CSRF and revocation: `/__files/open` now refuses
a cross-site request and dot-segments; the revocation residual is R-1155.
- **Live proof (rc.3, 9201, headless Chrome through the public names mapped to the box):** signed in → calibre-web's
„Fájlok ↗" → FileBrowser at `NVME 1TB/media/books`, no login form, user `felhom-household` (admin false, modify true,
login method proxy); signed out → `files.<domain>` went to the dashboard login. Screenshots `browser/01-…`, `02-…`,
`03-…`. Forged headers (`forged-headers.txt`): through traefik with no cookie — the old public name, a random name and the
REAL secret name all 401; from the bookstack container straight to `filebrowser:80` — the old name and a random name 401;
control: the real name from the same container 200 (the protection is the name's secrecy). The brief named Nextcloud;
bookstack was used (an app on 9201's network with curl).
## 3. Preview check (R-1153) — `audits/day-2026-10-19/P3/`
On 9202 (controller 0.331.0): OnlyOffice installed, the link on (FileBrowser back in 4 s). The hand-made `teszt.docx`
(912 bytes) → 500 „fitz: cannot open document"; OnlyOffice's own `sample.docx` → **200, a 256×256 JPEG, 0.3 s**. Same with
the link OFF. FileBrowser 1.5.6 previews .docx with its own MuPDF before OnlyOffice (`preview.go` `determinePreviewType`).
**Closed: not a product defect.** OnlyOffice removed through the product (both volumes gone), test files deleted.
## 4. Controller release 0.332.0 — `audits/day-2026-10-19/P4/`
Image `0.332.0` from `601bdba` (digest `sha256:245266a4…`), 03:16 UTC. Per-box floors 03:16:43 → Tester 1 and demo-felhom
on 0.332.0 at 03:16:45–55, demo-hp (from rc.3) at 03:17. Box walk on all three: 0 unhealthy, 0 allow-list refusals,
FileBrowser re-published 6 s after its re-create, public pages 200; https-only 28 OK, 0 failures, both controls right.
„Fájlok ↗" with no login through the public internet on demo-hp and Tester 1 (pass → cookie → `felhom-household`; a fresh
browser 401; sign-out → back to the dashboard). demo-felhom: CC holds no dashboard password for the lived-in box, so its
check is the files (secret 0600, config and route carry it) and the refusals (forged secret name through traefik 401, old
name from outside 401, a forged pass 403). **Global floor 0.332.0 / MinAgent 0.158.1 at 03:24:18 UTC**, read back with the
WAL; per-box floors cleared (read back empty). MinAgent 0.158.1 ≤ the vouched agent 0.159.2. i18n fixtures changed (named):
`escrow_first`, `escrow_receremony`; all others byte-identical. No golden (decision 250); the floor is above golden 0.331.0
until Sunday's bake.
## 5. Hub 0.159.0 (R-1150, R-1154) — `audits/day-2026-10-19/P5/`
- Written by a helper (`5121536f`, `b505ce4b`). **R-1154's rule, and why:** the helper's first draft re-issued the claim
code and the off-site provider password automatically when the old host was stale or switched off. A background
security review flagged it; the helper then read what the retrieval passphrase already grants (`/config` → the
controller API key; the enroll → the host key) and found the re-issues are the operator's session-only powers
(decision 252). So the passphrase must not reach them: **the reuse path re-issues nothing; when the old box looks gone it
mails the operator** („delete the old host and run the installer again, or re-issue …"), a live box gets an info record.
The operator's delete stays the gate. **R-1150:** `whole_guest_backup_refused` (operator-only, hub-origin; NOT in
`allowedEventTypes`, against the brief's wording — the suspected controller must not post or pre-empt it, R-895's
shape). Tests red-proved (helper's list in `hub/CHANGELOG.md`).
- **A push blocker on the way:** R-887's dated check was due today. Measured: 381 CI jobs since the S21 change, 0 lost (the
three re-runs' first attempts ran 47–49 s; control: the known lost attempt of 2026-10-10 ran 11 min 48 s and the attempt
count finds it). Closed.
- **Deploy:** image from a clean clone (the shared checkout held my uncommitted evidence), manifest `200ab653`, ArgoCD
synced the hub Deployment only (the only OutOfSync resource), Synced/Healthy, no ERROR, the floor read back, then fresh
reports from all three hosts (02:23–02:25 UTC) and all three controllers (02:29–02:31 UTC).
## 6. The old-restic check — `audits/evening-2026-10-18/P8/old-restic-check.txt`
The night watcher ran it: window 12 at 02:18 UTC removed 63 of demo-hp's 324 copies (→ 261); the old restic (0.14.0) and
the new (0.19.1) both list 261 and restore a 0.19.1-made copy and an August copy to identical content hashes. STATUS item
closed; `REPORT-evening-2026-10-18.md` §9 filled.
## 7. Versions, commits, fixtures, rows
- Agent **0.160.0** (released, not vouched) — `1b6e316`, `c11e3e2`, `124164d`, `4545819`. Controller **0.332.0** (floor) —
`ba4a6f0`, `9bdd8f8`, `601bdba`; rc.1–rc.3 on 9201 only. Hub **0.159.0** (deployed) — `5121536f`, `b505ce4b`, manifest
`200ab653`. felhom.eu `afc939bb` (R-887), `5622ce27`, this commit. Catalog: none.
- Fixtures: `escrow_first`, `escrow_receremony` (controller); no other page changed.
- **Register: 168 before, 166 after; closed 10** (R-887, R-978, R-973, R-1093, R-1149, R-1151, R-1100, R-1153, R-1150,
R-1154), **opened 8** (R-1155 … R-1162).
- `unproven.py --summary`: not walked 35 of 55 — unchanged. Capability map: not changed. System poster: text only — the
local whole-guest „Kept" cell („one copy per day, up to 3 days") edited in both the facts file and the drawing.
## Observations
1. FileBrowser's own 2-hour token can outlive the dashboard session if only the files cookie is lost. FILED: R-1155.
2. A reinstall within 45 min of the old box's last report reads „live" (no mail). FILED: R-1156.
3. Repeated cap refusals inside the 24 h hold raise no new alarm. FILED: R-1157.
4. The off-site leg of the clean-slate re-issue may be obsolete since hub v0.127.0. FILED: R-1158.
5. The retrieval passphrase yields the controller API key, which registers and confirms off-site keys. FILED: R-1159.
6. Hub and agent have no gofmt gate (~20 and 14 files drift). FILED: R-1160.
7. The escrow wizard calls the code „live" at the reveal; since R-1151 it is live only after the typed-back words.
FILED: R-1161.
8. R-973's off-site half (ep0 prunes by count) outlives the closed row. FILED: R-1162.
9. traefik resolves a forwardAuth answer's relative Location against the auth server. NOT-A-FINDING: found on rc.2 and
fixed before the release (red-proved); recorded as a rule in CONTEXT and memory.
10. The ceremony log line and two R-973 comments were stale. NOT-A-FINDING: fixed in agent `4545819` (the size rule).
11. My first OnlyOffice link call sent `{}` and switched the link off. NOT-A-FINDING: my call; the API wants
`{"enabled": true}`; it was already off, nothing restarted.
12. The R-1093 route refuses loop devices. NOT-A-FINDING: by design (only real disk names are allowed); a RAM disk was used.
13. CC holds no dashboard password for demo-felhom, so its „Fájlok ↗" was not walked as a household. NOT-A-FINDING: the
lived-in box's household password is not CC's (decision 254); its files and refusals were checked instead.
14. A helper's commits sat unpushed in the shared felhom.eu clone for ~40 minutes (blocked by R-887's due check).
NOT-A-FINDING: the gate did its job; pushed with my R-887 commit.
15. Two shell scripts of mine were refused by Claude Code's removal check (a cleanup `rm` it could not read behind
`bash -c`). NOT-A-FINDING: rewritten without the removal; nothing was deleted.
## Teardown — three layers
- **Machines:** 9202 — OnlyOffice removed through the product (volumes gone), test documents deleted; 9201 — on 0.332.0
(the floor); the escrow test left demo-hp on code 1 (stored); demo-hp host — the RAM disk module removed, helper scripts
deleted, no file left in `/root` (an older helper, `/root/opens.sh`, removed by name at the end). Cookie jars and
password files on DooPlex shredded or emptied. One debugging sign-in on demo-hp's dashboard (curl, 03:03 UTC) was not
signed out; its cookie file is shredded, so the session expires unused.
- **Hub:** global floor 0.332.0 / 0.158.1, no per-box floor; hub 0.159.0; no vouch.
- **DooPlex:** builds (agent, controller rc.1–3 + 0.332.0, hub 0.159.0 from a clean clone) and the hub deploy; no Docker
cleanup.
Architecture read: `01`, `02` „Logins and folders", `03` §8a, `04`, `05`, `07` §6/§6.1; the design note. Documents changed:
`02`, `03` §8a, `07` §6.1, the poster facts + drawing text, `05` §8 (helper). Instruction files: none changed.
+6 -2
View File
@@ -95,8 +95,12 @@ after it, round trip = bake sha, token checks 0 with a working control, teardown
## 9. The old-restic check (Part 8)
PENDING — filled in when demo-hp's clean-up window has run (after its 04:15 CEST night run). A watcher runs the check by
itself; at 20:57 UTC both restics listed 310 snapshots (`audits/evening-2026-10-18/P8-precheck.txt`).
**Done 2026-10-19 by the watcher** (filled in by the day session, `audits/evening-2026-10-18/P8/old-restic-check.txt`):
demo-hp's clean-up window 12 ran at 02:17:53–02:18:24 UTC after the night run — „63 of 193 planned snapshot(s) removed,
324 -> 261" (the hub's row: `pruned`, 324 → 261). Then `rx.sh` (read-only): the new restic (0.19.1) and the OLD one (image
0.322.0, restic 0.14.0) both list **261**; both restore `c0b6299c` (made by 0.19.1, 02:17:48) to the same content hash
`d808e6537c7bcdb6` (7 files) and `05c3346a` (2026-08-31) to `8df241f4cf889265` (10 files). The old restic still reads and
restores demo-hp's store after its first real 0.19.1 prune. (Pre-check at 20:57 UTC: 310 and 310.)
## 10. Versions, commits, fixtures, rows
+6 -7
View File
File diff suppressed because one or more lines are too long
@@ -539,8 +539,28 @@ state (both demo boxes) the panel says „Ezt a jelszót te állítottad be a F
the press. The box uses FileBrowser's own CLI (`set -u admin,<pw>`) with the server STOPPED (the CLI opens the
database with no lock timeout), verifies new=200 and admin/admin refused, stores the value encrypted and records
`generated`; a failed verify leaves `operator`. Not the `FILEBROWSER_ADMIN_PASSWORD` env: it re-applies on every
start. Spike: `audits/logins-folders-2026-10-16/P1b`. FileBrowser still asks for its own login the first time; one
sign-in for both is R-1100.
start. Spike: `audits/logins-folders-2026-10-16/P1b`. Since v0.332.0 the folder links open FileBrowser without its own
login (the next paragraph); `admin`'s password is the fallback and the operator's way in.
**[DESIGN] One sign-in for the file manager (v0.332.0, R-1100, operator decision D16 = yes 2026-10-19; spike
`audits/proxyauth-spike-2026-10-18/DESIGN-NOTE.md`).** Every dashboard link to a FileBrowser folder goes to
`/__files/open?to=<path>` on the dashboard host, which — for a signed-in, not-cross-site request — mints a pass (60 s,
one use, bound to `files.<domain>` and to the session) and sends the browser to `files.<domain>/_felhom/sso`. traefik
asks the controller before every FileBrowser request (forwardAuth `felhom-files-sso` → `/__felhom_gate/files`, the setup
gate's handshake): the pass becomes the host-only `felhom_files` cookie (a value derived from the session's fingerprint
for that host — the R-976 shape); a valid cookie → 200 with the box's **secret header** naming FileBrowser user
`felhom-household`; no cookie → 200 without it (FileBrowser's own password); a stale cookie (sign-out, password change)
→ both cookies cleared, back to the dashboard. **Why a secret header name:** FileBrowser trusts the header blindly and
every app container reaches `filebrowser:80` directly — measured: a forged fixed-name header from an app logged in. The
name (`X-Felhom-User-<32 hex>`, per box, 0600 in the data dir) is written only into FileBrowser's config and traefik's
route (`authResponseHeaders`, which strips a client's copy); never a log, a page or a report. **Why not a parent-domain
cookie:** every app subdomain is third-party code and would receive the dashboard session. **What stays:** `admin` is a
password user (R-513's reveal and reset, the R-1126 gate unchanged); the header user is a separate FileBrowser user whose
rights come from the box's userDefaults (change, add, delete, share, download — not admin, not API); FileBrowser's logout
goes to the dashboard. Residual: FileBrowser's own 2-hour token for the header user is cleared with the files cookie,
but a browser that lost only the files cookie keeps it until it expires. Measured live on all three test boxes
(`audits/day-2026-10-19/P2/`, `P4/`): opened at the folder with no login; forged headers refused through traefik and
from an app container; a sign-out asks again.
**[DESIGN] A stored FileBrowser password is checked before it is offered (v0.321.0, R-1101).** In `generated`, the
page asks FileBrowser whether the stored value still logs in; only FileBrowser's own 401/403 counts as refused (a
@@ -562,8 +582,9 @@ role, else the first declared folder, else (a media app) the first present drive
`movies`, the players both, a download client its `media_library.folder`) with NO backup sentence — the bind's class
there means „not this app's data", which is not the class-driven „temporary, not backed up". **A folder whose drive
is not bound into FileBrowser now is text, never a link** (`drivePresent` = the same gates the FileBrowser sync
applies). The button never promises one click: FileBrowser's router brings a cold visitor back to the folder after its
login (measured in the headless browser, `audits/logins-folders-2026-10-16/browser/`).
applies). Since v0.332.0 the button opens the folder signed in (one sign-in, above); on a box without it, FileBrowser's
router brings a cold visitor back to the folder after its login (measured in the headless browser,
`audits/logins-folders-2026-10-16/browser/`).
**[DESIGN] „Cím és kulcsok" is rows, not the install form.** The settings tab does not re-render the
install wizard's fields: the address is the „Cím" row, the storage is a „Tárhely: <név>" row, every
+10 -3
View File
@@ -544,11 +544,18 @@ nicety.
**loses Felhom's backup guarantees** — no PBS verify / monitoring on storage we can't reach. An
honest signup-time tradeoff, not a hidden one.
**When the new package reaches the hub (R-978, agent `main` 2026-10-18, unreleased):** a ceremony started again no
**When the new package reaches the hub (R-978; agent 0.160.0, 2026-10-19):** a ceremony started again no
longer replaces the hub's package when it starts. The package travels with the pending ceremony and is uploaded when its
code is claimed, before the code is handed out (an upload failure is a 502 and the code stays claimable). An abandoned
ceremony — TTL, a new start, a restart — leaves the first code opening the hub's package. Residual: the typed-back check
is the browser's, so a code opened and not written down still replaces the package (R-1151).
ceremony — TTL, a new start, a restart — leaves the first code opening the hub's package. **[DESIGN] Only after the
household typed it back (R-1151; agent 0.160.0 + controller 0.332.0, 2026-10-19):** a controller ≥ 0.332.0 claims with
`{"defer_upload": true}`; the agent hands out the code with two word positions it drew, keeps the package (and only those
two words, never the code) for 30 minutes, and uploads it only when `POST /escrow/ceremony/confirm` brings the two words
back — so a code opened and not written down never replaces the package. Wrong words 422 (still waiting), window gone 410,
nothing waiting 404, hub failure 502 (still waiting). A controller that has not updated claims with `{}` and gets the
R-978 order. Proven on demo-hp 2026-10-19: ceremony 1 typed back → code 1 opens the hub's package, code 0 is a kept
earlier one (422); ceremony 2 shown and abandoned → code 1 still opens it, code 2 opens nothing
(`audits/day-2026-10-19/P1/escrow-*.txt`).
#### Custody across generations — what the hub retains when a blob is superseded (hub v0.60.0; **contract corrected hub v0.93.0, R-198**)
@@ -383,7 +383,7 @@ never moved by an update or an undo, and the unit restore accepts it.
| **Tier-1** recovery unit | `<nsRoot>/backups/primary/<app>/` on the app's own drive | compose + `.felhom.yml` + **secret-stripped** `app.yaml`, `db-dumps/*.sql`, `volume-dumps/*.tar`, `manifest.json` | nightly at W, plus a checksum-gated refresh on the periodic status pass | one point per app (`restore_points.go:14-18`) | no |
| **Tier-2** cross-drive | `<target nsRoot>/backups/secondary/<app>/` | **always** a full mirror of the unit (`tier2.go:368-369`) **plus** `mandatory + optional` file legs, v2 layout `hdd/<rel>` + `userdata/<rel>` | nightly at W+60m | mirror (rsync) | no |
| **Tier-3** restic offsite | Hetzner Storage Box over SFTP, one multi-path snapshot per app per run | the unit **plus** `mandatory` legs only; a separate `_shares` snapshot | nightly at W+105m | `--keep-daily 7 --keep-weekly 4 --keep-monthly 6` | **yes** (restic) |
| **Plane-2** whole-guest, local | `local:` → `/var/lib/vz/dump` on the host | rootfs + `mp0 /var/lib/felhom` (its `docker/` and `sys_drive/` folders, bound in the guest to `/var/lib/docker` and `/mnt/sys_drive` — §7.5.1; `mp1` no longer exists, corrected 2026-10-15) | **24 h** (`backup_cadence_seconds: 0`) | `local_backup_retention: 3` | **no** |
| **Plane-2** whole-guest, local | `local:` → `/var/lib/vz/dump` on the host | rootfs + `mp0 /var/lib/felhom` (its `docker/` and `sys_drive/` folders, bound in the guest to `/var/lib/docker` and `/mnt/sys_drive` — §7.5.1; `mp1` no longer exists, corrected 2026-10-15) | **24 h** (`backup_cadence_seconds: 0`) | `local_backup_retention: 3` → `--prune-backups keep-daily=3`: one copy per day, 3 days (keep-last until agent 0.160.0, R-1149) | **no** |
| **Plane-2** whole-guest, offsite | `felhom-pbs:` → ep0 datastore `felhom-offsite`, per-customer namespace, over WireGuard | same contents | **7 days** (`604800`) | server-side prune on ep0, `keep-last 2` at `03:30` — **and the box asks for none** (R-191) | **yes** (per-customer key) |
> **[FACT] Tier-3 has a fifth state the table above does not show: PAUSED (R-543, controller
@@ -479,15 +479,20 @@ upload library is a mandatory class) or uploaded again by the household — its
`pct config 9201`, both hosts). So the whole-guest tiers carry the guest and **none of the customer's
data drives** — 916 GB on demo-felhom, 938 GB on demo-hp.
> **The whole-guest tiers are capped per guest (R-973, agent `main` 2026-10-18, unreleased) — *decided by CC unattended,
> operator may reverse*.** Because the tiers prune by COUNT, a compromised controller that runs `POST /backup` again and
> **The whole-guest tiers are capped per guest (R-973; agent 0.160.0, released 2026-10-19 on the operator's yes — the
> numbers were *decided by CC unattended, operator may reverse*, and the operator said yes to the release).** Because the tiers prune by COUNT, a compromised controller that runs `POST /backup` again and
> again pushes out older restore points. The agent now refuses (429, logged, kept across a restart, a CRITICAL degraded
> status for 24 h) a run beyond: the daily tier — **3 successful runs per rolling 24 h** (the night run, one „Mentés
> most", the next night: a press never costs the night, R-899); the weekly off-site tier — **one per 6 days**
> (max(cadence − 24 h, cadence / 2)). The question: how many runs may a guest make before the cap refuses? Options: no cap
> (today, floodable); a per-tier count per rolling window (chosen: no ordinary day is refused); a time-bucketed prune
> instead (the durable shape, but a retention change — R-1149 for the local tier, R-973's design line for ep0). Limits:
> keep-last 3 can still be replaced within one day (R-1149); ep0's keep-last 2 is a count.
> ep0's keep-last 2 is a count. **[DESIGN] The local tier keeps one copy per day (R-1149, operator decision D17,
> 2026-10-19; agent 0.160.0):** `keep-daily=3` instead of keep-last 3 — the same at most three archives, but extra runs on
> one day replace only that day's copy, so with the cap a day of flooding leaves the two earlier days' copies
> (`TestBackupCap_R1149_LocalFloodKeepsOlderDays`). Measured at the first new-rule prune on demo-hp (retention 1: the
> same single newest copy either way) and read before on Tester 1 and demo-felhom (one copy per day each: the same copy
> goes at their next night under either rule) — `audits/day-2026-10-19/P1/`.
### 6.1.1 A box that is not always on — the catch-up, the banner, the alarms (R-871..R-874)
@@ -87,7 +87,7 @@ Title: **"Where a household's data is copied"**
| **Tier 1 — recovery unit** | each app: its settings, database dump, volumes | data drive 1, on the box | nightly at **02:30** (W) | 1 point per app | not encrypted (it never leaves the home) |
| **Tier 2 — cross-drive** | a mirror of Tier 1, plus the app's files | data drive 2, on the box (counted only on a different physical disk; in a VM made, not counted) | nightly **03:30** | mirror | not encrypted |
| **Tier 3 — off-site, per app** | Tier 1 + the app's must-keep files | **Hetzner Storage Box** (Germany), one folder per household | nightly **04:15** | 7 daily, 4 weekly, 6 monthly | the household's own restic password |
| **Whole guest — local** | the guest's system (not the data drives) | the box's own system disk | daily, in the window **04:30–08:30** | up to 3 copies (fewer on a small disk) | not encrypted |
| **Whole guest — local** | the guest's system (not the data drives) | the box's own system disk | daily, in the window **04:30–08:30** | one copy per day, up to 3 days (fewer on a small disk) | not encrypted |
| **Whole guest — off-site** | the same | **ep0** (Hetzner, Germany), one namespace per household | weekly, same window | last 2 | the household's own key, escrowed with a recovery code |
Footnote: "W = the household's backup window start, 02:30 by default. Tiers 1–3 follow at fixed offsets so they cannot run out of order. A missed night is caught up 15 minutes after the box starts."
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,17 @@
03:05:34 goto https://felhom.enkisfelhom.hu/login -> https://felhom.enkisfelhom.hu/login
03:05:39 eval -> "focused"
03:05:43 eval -> "https://felhom.enkisfelhom.hu/launcher"
03:05:43 goto https://felhom.enkisfelhom.hu/apps/calibre-web -> https://felhom.enkisfelhom.hu/apps/calibre-web
03:05:48 eval -> "https://felhom.enkisfelhom.hu/__files/open?to=… | button: Fájlok ↗ | folder: /files/NVME 1TB/media/books"
03:05:48 shot 01-app-page-files-button
03:05:49 eval -> "pressed Fájlok"
03:05:57 eval -> "files.enkisfelhom.hu/files/NVME 1TB/media/books | login form: no | title: FileBrowser Quantum - Files - books"
03:05:57 eval -> "FileBrowser user: felhom-household | admin: false | modify: true | loginMethod: proxy"
03:05:59 shot 02-filebrowser-opened-signed-in
03:05:59 goto https://felhom.enkisfelhom.hu/launcher -> https://felhom.enkisfelhom.hu/launcher
03:06:04 eval -> "signed out on the dashboard"
03:06:08 eval -> "https://felhom.enkisfelhom.hu/login"
03:06:08 goto https://files.enkisfelhom.hu/files/ -> https://felhom.enkisfelhom.hu/login
03:06:15 eval -> "felhom.enkisfelhom.hu/login | login form: YES"
03:06:15 shot 03-after-sign-out-asks-again
LOG ('netfail', 'net::ERR_ABORTED', 'Fetch')
@@ -0,0 +1,9 @@
== through traefik (https://files.enkisfelhom.hu, no files cookie), GET /api/users?id=self
forged header named X-Felhom-User -> HTTP 401 (user None)
forged header named RANDOM -> HTTP 401 (user None)
forged header named SECRET -> HTTP 401 (user None)
== from the bookstack container (on traefik-public), straight to http://filebrowser:80/api/users?id=self — no traefik
forged header named X-Felhom-User -> HTTP 401 (user None)
forged header named RANDOM -> HTTP 401 (user None)
== CONTROL: the same container with the box's REAL secret name (read from the box, never printed)
real secret name -> HTTP 200 (user felhom-household)
@@ -0,0 +1,4 @@
Mon Oct 19 03:08:04 UTC 2026
200
{
"ok": true,
@@ -0,0 +1,5 @@
Mon Oct 19 03:08:23 UTC 2026
05:08:24 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
05:09:29 [1] deployed, controller state=running, pinned={'onlyoffice': 'onlyoffice/documentserver:9.4.0'}
deploy: True
Mon Oct 19 03:09:29 UTC 2026
@@ -0,0 +1,16 @@
Mon Oct 19 03:09:40 UTC 2026
onlyoffice health: healthy
200
{"ok": true, "data": {"enabled": false, "status": "disabled"}, "message": "Integráció kikapcsolva"}
03:09:42 files.<domain> 200
onlyoffice Up 28 seconds (healthy)
filebrowser Up 6 hours (healthy)
Mon Oct 19 03:10:35 UTC 2026
200
{"ok": true, "data": {"enabled": true, "enabled_at": "2026-10-19T03:10:36Z", "status": "active"}, "message": "Integráció engedélyezve"}
03:10:39 files.<domain> 200 (try 1)
filebrowser Up 5 seconds (healthy)
onlyoffice Up About a minute (healthy)
url: "https://office.enkisfelhom.hu"
internalUrl: "http://onlyoffice:80"
2
@@ -0,0 +1,4 @@
login 200 token_len 355
resource 200 application/vnd.openxmlformats-officedocument.wordprocessingml.document
03:12:19Z preview small HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
03:12:19Z preview large HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
@@ -0,0 +1,10 @@
== /r1153/sample.docx
login 200 token_len 355
resource 200 application/vnd.openxmlformats-officedocument.wordprocessingml.document
03:13:40Z preview small HTTP 200 content-type image/jpeg bytes 8714 in 0.3s
03:13:40Z preview large HTTP 200 content-type image/jpeg bytes 22734 in 0.3s
== /r1153/teszt.docx
login 200 token_len 355
resource 200 application/vnd.openxmlformats-officedocument.wordprocessingml.document
03:13:40Z preview small HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
03:13:40Z preview large HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
@@ -0,0 +1,9 @@
Mon Oct 19 03:13:53 UTC 2026
{"ok": true, "data": {"enabled": false, "status": "disabled"}, "message": "Integráció kikapcsolva"}
03:13:57 files.<domain> 200 (try 1)
== link OFF, /r1153/sample.docx
03:13:58Z preview small HTTP 200 content-type image/jpeg bytes 8714 in 0.2s
03:13:58Z preview large HTTP 200 content-type image/jpeg bytes 22734 in 0.2s
== link OFF, /r1153/teszt.docx
03:13:58Z preview small HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
03:13:58Z preview large HTTP 500 content-type application/json; charset=utf-8 bytes 167 in 0.0s b'{"status":500,"message":"failed to create image for PDF file: failed to open PDF from memory for file \'/srv/scratch_hdd/r1153/teszt.docx\': fitz: cannot open document"}'
@@ -0,0 +1,8 @@
Mon Oct 19 03:14:09 UTC 2026
test files removed
05:14:54 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'onlyoffice', 'volumes_removed': ['onlyoffice_onlyoffice_data', 'onlyoffice_onlyoffice_logs'], 'hdd_paths_removed': [], 'hdd_pa
05:15:03 [X] after remove: deployed=False leftovers='/opt/docker/stacks/onlyoffice'
remove: 200
0
0
ls: cannot access '/opt/docker/stacks/onlyoffice/app.yaml': No such file or directory
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

@@ -0,0 +1,4 @@
## per-customer floors 0.332.0 / MinAgent 0.158.1 — 03:16:43
tester-1: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
demo-hp: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
demo-felhom: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
@@ -0,0 +1,14 @@
03:16:55 demo-hp=0.332.0-rc.3 tester-1=0.332.0 demo-felhom=0.332.0
03:17:28 demo-hp=0.332.0
== hp
2026/10/19 03:16:57 scheduler.go:85: [DEBUG] [scheduler] periodic job registered: name="selfupdate-check" interval=6h0m0s totalJobs=23
2026/10/19 03:17:04 updater.go:104: [DEBUG] [selfupdate] SetFloor: floor "" → "0.332.0"
2026/10/19 03:17:04 updater.go:104: [DEBUG] [selfupdate] maybeAutoUpdate: current 0.332.0 >= floor 0.332.0 — no action
== root@192.168.0.154
2026/10/19 03:16:55 [INFO] [selfupdate] Post-update startup: update successful (0.331.0 → 0.332.0)
2026/10/19 03:16:55 [INFO] [scheduler] Registered periodic job: selfupdate-check (every 6h0m0s)
2026/10/19 03:17:26 [INFO] [selfupdate] Current version 0.332.0 is up to date
== felhom-pve
2026/10/19 03:16:45 [INFO] [selfupdate] Post-update startup: update successful (0.331.0 → 0.332.0)
2026/10/19 03:16:45 [INFO] [scheduler] Registered periodic job: selfupdate-check (every 6h0m0s)
2026/10/19 03:17:17 [INFO] [selfupdate] Current version 0.332.0 is up to date
@@ -0,0 +1,33 @@
=== demo-hp (enkisfelhom.hu) 03:17:58
containers: 24 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 03:16:57 updater.go:855: [INFO] [selfupdate] Post-update startup: update successful (0.332.0-rc.3 → 0.332.0)
allow-list refusals since start: 0
2026/10/19 03:16:58 sync.go:142: [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 12
public https://felhom.enkisfelhom.hu/login: 200
public https://files.enkisfelhom.hu/: 200
=== demo-felhom (demo-felhom.eu) 03:18:03
containers: 9 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 03:16:45 [INFO] [selfupdate] Post-update startup: update successful (0.331.0 → 0.332.0)
allow-list refusals since start: 0
2026/10/19 03:16:46 [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 5
2026/10/19 03:16:45 [INFO] [infra] wrote filebrowser route → /opt/docker/stacks/traefik/dynamic/filebrowser.yml (Host files.demo-felhom.eu → filebrowser:80 by name, R-1126; one sign-in true, R-1100)
2026/10/19 03:16:46 [INFO] [web] FileBrowser mounts synced (recreated) — 1 storage path(s), config updated
2026/10/19 03:16:52 [INFO] [infra] filebrowser: default login refused for this run — joined traefik-public, files.<domain> served (R-1126)
public https://felhom.demo-felhom.eu/login: 200
public https://files.demo-felhom.eu/: 200
=== tester-1 (enkicsifelhom.hu) 03:18:06
containers: 10 running; not healthy/running: traefik Up 3 days;
unhealthy: 0
2026/10/19 03:16:55 [INFO] [selfupdate] Post-update startup: update successful (0.331.0 → 0.332.0)
allow-list refusals since start: 0
2026/10/19 03:16:56 [INFO] [sync] Initial sync: Sablonok naprakészek — nincs változás
installed apps (stacks with app.yaml): 3
2026/10/19 03:16:55 [INFO] [infra] wrote filebrowser route → /opt/docker/stacks/traefik/dynamic/filebrowser.yml (Host files.enkicsifelhom.hu → filebrowser:80 by name, R-1126; one sign-in true, R-1100)
2026/10/19 03:16:56 [INFO] [web] FileBrowser mounts synced (recreated) — 5 storage path(s), config updated
2026/10/19 03:17:02 [INFO] [infra] filebrowser: default login refused for this run — joined traefik-public, files.<domain> served (R-1126)
public https://felhom.enkicsifelhom.hu/login: 200
public https://files.enkicsifelhom.hu/: 200
@@ -0,0 +1,33 @@
control NEGATIVE (a stub answering 200 over plain http): convicted, as it must (200 -)
== tester-1 (enkicsifelhom.hu): 6 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://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://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,16 @@
=== demo-hp (enkisfelhom.hu) 03:23:50
dashboard sign-in: 302
the Fajlok button goes to: https://files.enkisfelhom.hu/_felhom/sso
the pass answer goes to: https://files.enkisfelhom.hu/files/; files cookie set: 1
FileBrowser with the files cookie: felhom-household admin: False modify: True
FileBrowser without it, a fresh browser: HTTP 401
dashboard sign-out: 302
after the sign-out, the files cookie sends the browser to: https://felhom.enkisfelhom.hu/__files/open?to=%2Ffiles%2F
=== tester-1 (enkicsifelhom.hu) 03:23:51
dashboard sign-in: 302
the Fajlok button goes to: https://files.enkicsifelhom.hu/_felhom/sso
the pass answer goes to: https://files.enkicsifelhom.hu/files/; files cookie set: 1
FileBrowser with the files cookie: felhom-household admin: False modify: True
FileBrowser without it, a fresh browser: HTTP 401
dashboard sign-out: 302
after the sign-out, the files cookie sends the browser to: https://felhom.enkicsifelhom.hu/__files/open?to=%2Ffiles%2F
@@ -0,0 +1,7 @@
=== demo-felhom (demo-felhom.eu) 03:24:06 — no dashboard password is held for this box: file and refusal checks only
-rw-------
valid shape: 1
config carries the secret: 1, route carries it: 1
forged secret name through traefik, no files cookie: HTTP 401
from outside, old public name: HTTP 401
from outside, the pass path with a forged pass: HTTP 403
@@ -0,0 +1,16 @@
## global floor 0.332.0 / 0.158.1 — 03:24:18
HTTP/1.1 303 See Other
Location: /configuration?flash=floor_set
## read back (DB with WAL) 03:24:18
min_controller_version|0.332.0
min_controller_version_declared_min_agent|0.332.0=0.158.1
## per-box floors cleared — 03:24:28
tester-1: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set
demo-hp: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set
demo-felhom: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set
min_controller_declared_min_agent
min_controller_set_at
min_controller_version
demo-felhom|''|''
demo-hp|''|''
tester-1|''|''
@@ -0,0 +1,29 @@
Mon Oct 19 02:14:15 UTC 2026
application.argoproj.io/felhom patched
deployment "hub" successfully rolled out
sync=Synced health=Progressing rev=200ab6530bf2bec9a9b341e4bc8e2bfd09f6a609
gitea.dooplex.hu/admin/felhom-hub:0.159.0
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hub-67bff8bcb6-8pgxf 0/1 Running 0 9s 10.42.0.56 dooplex <none> <none>
2026/10/19 04:14:21 [INFO] Offsite pool-box checker initialized: box=611714 fill warn=80% crit=90%, oversub warn=2.00x, unreachable after 3 consecutive failed reads, refresh 15m0s
2026/10/19 04:14:21 [INFO] osupdates: approval rule = every ring-0 box healthy for 24h0m0s and 1 night run(s)
2026/10/19 04:14:21 [INFO] osupdates: the Docker engine set is approved only by the operator, after 2 healthy ring-0 night(s)
2026/10/19 04:14:21 [INFO] osupdates: alarms = no OS leg 168h0m0s, reboot needed 336h0m0s, ring 0 stalled 168h0m0s, not covered 336h0m0s
2026/10/19 04:14:21 [INFO] Off-site key registrar enabled; daily key check at 07:10 Budapest
2026/10/19 04:14:21 [INFO] access log: /data/access-log, one file per UTC day, kept 90 days
2026/10/19 04:14:21 [INFO] Registry version checker started (every 6h)
2026/10/19 04:14:21 [INFO] offsite-key-audit: next run at 2026-10-19 07:10 CEST (in 2h55m39s)
2026/10/19 04:14:21 [INFO] WG peer-sync enabled (endpoint 167.233.158.164:22, user felhom-peersync)
2026/10/19 04:14:21 [INFO] PBS DR tenantsync enabled (endpoint 167.233.158.164:22, user felhom-peersync; WG-registration auto-provision hook armed)
2026/10/19 04:14:21 [INFO] PBS-DR box checker initialized: fill warn=80% crit=90%, unreachable after 3 consecutive failed reads, refresh 15m0s
2026/10/19 04:14:21 [INFO] agent-plane poke enabled (endpoint 167.233.158.164:22, user felhom-peersync; web + api admin seams armed)
2026/10/19 04:14:21 [INFO] PBS-DR self-heal reconciler started (interval 5m)
2026/10/19 04:14:21 [INFO] offsite credential self-heal reconciler started (interval 5m, debounce 2 reports)
2026/10/19 04:14:21 [DEBUG] Registry version check: latest = 0.331.0
Mon Oct 19 02:15:36 UTC 2026
sync=Synced health=Healthy
hub-67bff8bcb6-8pgxf 1/1 Running 0 79s
--- floor (DB with WAL):
min_controller_version|0.331.0
min_controller_version_declared_min_agent|0.331.0=0.158.1
--- errors since start:
@@ -0,0 +1,9 @@
Mon Oct 19 02:32:34 UTC 2026
hosts (agent reports) after the 02:14:21 start:
demo-felhom-8363b5|0.160.0|2026-10-19 02:25:35
demo-hp-bb76ea|0.160.0|2026-10-19 02:23:50
tester-1-d70be4|0.160.0|2026-10-19 02:24:21
controller reports:
demo-felhom|2026-10-19 02:29:12
demo-hp|2026-10-19 02:31:13
tester-1|2026-10-19 02:29:13
@@ -0,0 +1,31 @@
## 2026-10-19T02:19:24Z hub window (id|opened|closed|result|before|after): 12|2026-10-19 02:17:53|2026-10-19 02:18:24|pruned|324|261
2026/10/19 02:17:01 offbox.go:1068: [INFO] [offbox] pre-push dump leg completed in 2m1.262s — snapshot pair is coherent
2026/10/19 02:17:04 offbox_capture.go:47: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:04 offbox_capture.go:47: [WARN] [offbox] qbittorrent: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:04 offbox_capture.go:47: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:04 offbox_capture.go:47: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:04 offbox_capture.go:47: [WARN] [offbox] prowlarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:09 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 dat
2026/10/19 02:17:09 offbox.go:1420: [WARN] [offbox] qbittorrent: no recovery unit found on any connected drive — skipping
2026/10/19 02:17:15 offbox.go:1504: [INFO] [offbox] backed up romm (/mnt/felhom-drives/hdd_1/backups/primary/romm, 0 mandatory path(s))
2026/10/19 02:17:17 offbox.go:1504: [INFO] [offbox] backed up calibre-web (/mnt/felhom-drives/hdd_1/backups/primary/calibre-web, 1 mandatory path(s))
2026/10/19 02:17: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/19 02:17:23 offbox.go:1504: [INFO] [offbox] backed up bookstack (/mnt/sys_drive/felhom-data/backups/primary/bookstack, 0 mandatory path(s))
2026/10/19 02:17:25 offbox.go:1504: [INFO] [offbox] backed up opengist (/mnt/sys_drive/felhom-data/backups/primary/opengist, 0 mandatory path(s))
2026/10/19 02:17:27 offbox.go:1504: [INFO] [offbox] backed up privatebin (/mnt/sys_drive/felhom-data/backups/primary/privatebin, 0 mandatory path(s))
2026/10/19 02:17:29 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 dat
2026/10/19 02:17:32 offbox.go:1504: [INFO] [offbox] backed up docmost (/mnt/sys_drive/felhom-data/backups/primary/docmost, 0 mandatory path(s))
2026/10/19 02:17:32 offbox_capture.go:47: [WARN] [offbox] radarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:34 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
2026/10/19 02:17:34 offbox_capture.go:47: [WARN] [offbox] sonarr: not deployed — offsite push is unit-only (mandatory userdata not resolvable)
2026/10/19 02:17:36 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
2026/10/19 02:17:46 offbox.go:1504: [INFO] [offbox] backed up home-assistant (/mnt/sys_drive/felhom-data/backups/primary/home-assistant, 0 mandatory path(s))
2026/10/19 02:17:48 offbox.go:1504: [INFO] [offbox] backed up kimai (/mnt/sys_drive/felhom-data/backups/primary/kimai, 0 mandatory path(s))
2026/10/19 02:17:51 offbox.go:1504: [INFO] [offbox] backed up plex (/mnt/felhom-drives/hdd_1/backups/primary/plex, 0 mandatory path(s))
2026/10/19 02:18:23 offbox_window.go:276: [INFO] [offbox] clean-up window 12 (after-run): 63 of 193 planned snapshot(s) removed, 324 -> 261, in 30s
2026/10/19 02:18:28 offbox.go:1266: [INFO] [offbox] backup OK: 14 app(s) backed up, 1 skipped (no unit), 261 snapshot(s), 3m25s
## the old-restic check (rx.sh, read-only; OLD = image 0.322.0 = restic 0.14.0, NEW = 0.331.0 = restic 0.19.1)
store listed: new restic 261 snapshots; old restic 261 snapshots
c0b6299c (felhom-offbox, made by restic_0.19.1, 2026-10-19T02:17:48): old restic → d808e6537c7bcdb6 (7 files) new restic → d808e6537c7bcdb6 (7 files)
05c3346a (felhom-offbox, made by ?, 2026-08-31T21:29:03): old restic → 8df241f4cf889265 (10 files) new restic → 8df241f4cf889265 (10 files)
## done 2026-10-19T02:19:44Z
+10 -1
View File
@@ -26,11 +26,20 @@
---
## 2026-10-19 (day) — agent 0.160.0, one sign-in for the file manager, hub small fixes (full text of each row: `git show e4f022a0:documentation/backlog/OPEN-ITEMS.md`)
## 2026-10-19 (day) — agent 0.160.0, one sign-in for the file manager (controller 0.332.0), hub 0.159.0 (full text of each row: `git show e4f022a0:documentation/backlog/OPEN-ITEMS.md`)
| Row | What | Closed | Evidence |
|---|---|---|---|
| **R-887** | **Some CI jobs are never run, and Gitea fails them ~10–13 minutes later with no log** (Gitea's zombie-task stop after the runner abandoned its fetch; S21 on 2026-10-12: runner `fetch_timeout` 5 s → 30 s, crawler rate limit). (P3) | CLOSED 2026-10-19 — **the dated check is met:** since 2026-10-12 12:00Z the four repos ran **381 jobs, 0 lost**. 25 ended `failure`, every one with a readable log (the runner ran them); 3 jobs had a second attempt (controller 1944, 1959, 1991), and each first attempt (tasks 1962, 1978, 2011) failed in 47–49 s — a run that did work, not the lost shape (one cause on record: the register not pushed yet, `REPORT-burndown2-2026-10-16.md`). **Control:** the known lost attempt of controller job 1639 (task 1653, 2026-10-10) ran 11 min 48 s, and the attempt count finds it. **Reasoning kept:** the jobs endpoint shows only the LAST attempt (R-1137), so „a failed job with no log" alone cannot find a lost attempt that was re-run — count `run_attempt > 1` and read the first attempt in `actions/tasks`. | `audits/day-2026-10-19/R-887/` |
| **R-978** | **Starting the escrow ceremony again can make the customer's written-down recovery code useless: the new package replaces the old one before the new code is confirmed.** (P3) | CLOSED 2026-10-19 — **agent 0.160.0 released** (the operator's yes, given in advance in the day brief) and running on demo-hp, Tester 1 and demo-felhom by the signed-job path. Live on demo-hp 9201: an abandoned second ceremony left the first code opening the hub's package (with R-1151's confirm on top, below). | `audits/day-2026-10-19/P1/escrow-*.txt` |
| **R-973** | **A compromised controller can make the whole-guest backup run again and again, and each run prunes older restore points: the local tier directly, and the off-site tier through the store's own nightly keep-last-2.** (P3) | CLOSED 2026-10-19 — **agent 0.160.0 on all three test hosts**; a normal „Mentés most" whole-guest backup of demo-hp 9201 ran green (6.9 GB, 443 s) and the cap counted it (two successes in the rolling 24 h on disk). The numbers (3 a day, 1 per 6 days off-site) were *decided by CC unattended*; the operator said yes to the release. The local half's remaining flood within a day is closed by R-1149 (keep-daily). **The off-site half (ep0's keep-last 2 is a count) moves to R-1162.** | `audits/day-2026-10-19/P1/backup-press.txt`, `demo-hp-after-backup.txt` |
| **R-1093** | **An EMPTY partition asks for the typed „data will be erased" confirmation.** (P3) | CLOSED 2026-10-19 — **agent 0.160.0, proven on the real route** on demo-hp: a 64 MB RAM disk from the kernel's `scsi_debug` module (`/dev/sdb`, the route accepts only sd/nvme/vd/hd names — a loop device is refused before any probe, by design), one empty GPT partition → `POST /disks/format` with no confirmation → 200 `formatted`, `data_bearing: false`. **Control:** the same call on the now-ext4 partition → 403 `needs_confirmation` („device has a ext4 filesystem"). The RAM disk removed (`rmmod`). | `audits/day-2026-10-19/P1/r1093-format-route.txt` |
| **R-1149** | **The local whole-guest tier prunes keep-last 3, so even with the R-973 cadence cap a compromised controller can replace all three restore points within one day.** (P3) | CLOSED 2026-10-19 — **operator decision D17 = „one per day, 3 days"; agent 0.160.0**: the local tier prunes `keep-daily=N` (the „backup tier armed" line names it on all three hosts). Before/after: demo-hp (retention 1) kept the same single newest copy as keep-last would; Tester 1 and demo-felhom hold one copy per day, so their next night prunes the same copy under either rule. Test: a day-long flood with the cap leaves the two earlier days' copies (red-proved). | `audits/day-2026-10-19/P1/demo-hp-*.txt`, `others-before.txt` |
| **R-1151** | **The new escrow package now uploads when its code is claimed (R-978), but the wizard's typed-back check is the browser's alone:** (P3) | CLOSED 2026-10-19 — **agent 0.160.0 + controller 0.332.0 (the floor)**: the new package reaches the hub only after `POST /escrow/ceremony/confirm` brings two words of the code back (the agent draws the positions, keeps only those two words, 30-minute window); an old controller keeps the R-978 order. Live on demo-hp 9201 through the wizard's own calls: ceremony 1 — one wrong word 422, the right words 200, then code 1 opens the hub's package and code 0 is an earlier kept package (422, superseded at the confirm second); ceremony 2 shown and abandoned — code 1 still opens it, code 2 opens nothing. demo-hp's stored code was replaced with code 1 (CC's secret store). | `audits/day-2026-10-19/P1/escrow-*.txt` |
| **R-1100** | **One sign-in for the dashboard and FileBrowser (proxy auth) — spike first.** (P3) | CLOSED 2026-10-19 — **built (operator decision D16 = yes) and shipped in controller 0.332.0, the floor for every box**: a dashboard pass (60 s, one use, host- and session-bound, none for a cross-site request) → a host-only files cookie → traefik forwardAuth sets the box's SECRET header naming `felhom-household`; the dashboard cookie is not widened. Live: on 9201 in the headless browser „Fájlok ↗" opened the app's folder with no login, a dashboard sign-out asked again; on demo-hp and Tester 1 the same path through the public internet; forged headers (the old public name, a random name, the real name with no cookie through traefik) all 401, and only the real name from an app container straight to `filebrowser:80` (the control) got in. A live bug was fixed before release: traefik made the pass answer's relative redirect point at `felhom-controller:8080`. | `audits/day-2026-10-19/P2/`, `P4/05-files-one-sign-in.txt`, `P4/06-demo-felhom-checks.txt` |
| **R-1153** | **FileBrowser's preview of a .docx answered 500 while the OnlyOffice link was on** (P4) | CLOSED 2026-10-19 — **not a product defect.** FileBrowser 1.5.6 previews a .docx with its own MuPDF before it ever asks OnlyOffice (`preview.go` `determinePreviewType`: document conversion first). On 9202 (controller 0.331.0) a real .docx (OnlyOffice's own `sample.docx`) previews **200, a 256×256 JPEG in 0.3 s** with the link on AND off; the 500 is the 912-byte hand-made test file, which MuPDF cannot open („fitz: cannot open document"), with the link on or off. OnlyOffice removed through the product after. | `audits/day-2026-10-19/P3/` |
| **R-1150** | **A refused whole-guest backup (the R-973 cap) reaches the operator as `agent_capability_degraded`, whose text reads „agent privileged capability degraded”.** (P4) | CLOSED 2026-10-19 — **hub 0.159.0 deployed** (the operator's yes, given in advance): the cadence cap alone raises the operator-only, hub-origin `whole_guest_backup_refused` with plain text (its clearing: `whole_guest_backup_refusal_cleared`); other critical capabilities keep `agent_capability_degraded`, which is now operator-only by register too. Not in `allowedEventTypes` on purpose (the suspected controller must not post or pre-empt it, the R-895 shape). Deploy: ArgoCD synced the hub Deployment only, Synced/Healthy, the floor read back, reports from all three hosts and controllers after the restart. | `audits/day-2026-10-19/P5/` |
| **R-1154** | **A reinstall while the customer's OLD host record still exists skips both clean-slate re-issues (the claim code and the off-site credential) in silence.** (P3) | CLOSED 2026-10-19 — **hub 0.159.0 deployed: the reuse path never re-issues; it makes the reinstall LOUD.** A first draft re-issued automatically when the old host was stale or switched off; a security review found that would let anyone with the household's retrieval passphrase trigger the operator's re-issues (claim code, off-site provider password) while the box is off. Now: old box gone (no wait connection, and switched off on purpose or both reports older than the stale threshold) → the operator-only warning `host_reinstall_over_old_host`, mailed: „… delete the old host and run the installer again, or re-issue …"; a live box → the info record `host_reenroll_while_live`. The mint after the operator's delete re-issues as before. Not walked live (no reinstall today). Residual: R-1156. | `hub/internal/api/reenroll.go`, `05` §8 |
## 2026-10-18 (evening) — a fresh install from scratch, OnlyOffice saves, Redis without a volume, CI mail link (controller v0.331.0, catalog `30eea97`)
+14 -15
View File
@@ -112,7 +112,7 @@ match what the reader sees is how an instrument stops being believed (R-421). It
**nothing was proven on the day this line was drawn** and a stopping line that moves a status is a
stopping line that lies.
## Install & onboarding — 8 rows (P3 6, P4 2)
## Install & onboarding — 9 rows (P3 5, P4 4)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -123,9 +123,10 @@ stopping line that lies.
| **R-993** | Install & onboarding | P3 | **Operator: approve the minimum machine in `documentation/audits/sizing-2026-10-13/SIZING.md` §5** (the input R-991's hardware page needs). MEASURED 2026-10-13 on the N100 demo box (61 apps deployed and removed through the product, 15 min at rest each; heavy apps with one real action). Proposal: **minimum** x86-64, 4 cores (N100 class), **8 GB**, 256 GB SSD; **recommended 16 GB**, 512 GB SSD; out: ARM / Raspberry Pi, Synology-QNAP, routers, cloud VPS. Worked from the floor (2.5 GB for the whole box with no apps) + Immich, Jellyfin, Nextcloud, Home Assistant, Vaultwarden, Paperless-ngx at their peaks (5.0 GB) + 50 % headroom ≈ 12.3 GB. 8 GB runs that set without Immich; 16 GB runs all of it twice over. Data for the estimator: `sizing.json`. **2026-10-13 (estimator session) — the installer accepts a host BELOW this minimum:** `autosize_guest_caps` has no floor on the host, only on the guest; on a 4 GB box (MemTotal ≈ 3.5 GiB) the guest gets host − 1 GB ≈ 2.4 GB and Proxmox keeps ≈ 1 GB, where it used ≈ 2.0 GB on the N100 (SIZING.md §1). Whatever minimum is approved, nothing enforces it at install today. The three-verdict wording of §5 now matches the estimator page (`/gepigeny`, R-997). | **WAITING-ON-OPERATOR** — filed 2026-10-13 | the operator's decision | Operator: approve or change §5. If nothing is done: no hardware page; a buyer with a Raspberry Pi can still download the installer (R-991 stays open) | operator |
| **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 |
| **R-1154** | Install & onboarding | P3 | **A reinstall while the customer's OLD host record still exists skips both clean-slate re-issues (the claim code and the off-site credential) in silence.** `handleHostEnroll` returns the existing host early (mint-once-reuse), so `reissueOnReenroll` is never reached; its own comment assumes „the clean-slate flow deletes the stale host” — nobody does on a plain reinstall. Proven live on the demo-hp reinstall 2026-08-21 (CC memory), never filed. Now recurring: the operator keeps Tester4 as the standing fresh-install customer (2026-10-18), and its old host `Tester4-31fa0e` stays after each teardown. Filed 2026-10-18. | OPEN — filed 2026-10-18 | — | Decide: the enroll re-issues when the old host is stale or off, or the teardown deletes the host first (operator, session only); a test on the enroll path | CC |
| **R-1156** | Install & onboarding | P4 | **A reinstall done within the stale threshold (45 min) of the old box's last report reads as „live":** the hub writes only the info record `host_reenroll_while_live`, no warning mail, so the operator is not told to delete the old host and re-issue. The residual of R-1154's loud-only rule. Found 2026-10-19. | OPEN — filed 2026-10-19 | — | Decide: mail the live case too, or a shorter window for this check | CC |
| **R-1158** | Install & onboarding | P4 | **The off-site re-issue on a clean-slate re-enroll may be obsolete:** since hub v0.127.0 no box receives the provider password (a fresh box registers its key with the controller API key), so `reissueOnReenroll`'s off-site leg may now only reset a password nobody uses — and CC's memory note on reinstalls (2026-08-21) may be stale on this point. Read from code, not measured. Found 2026-10-19. | OPEN — filed 2026-10-19 | — | Measure on the next Tester4 install (after deleting its old host) | CC |
## Apps & catalog — 15 rows (P3 6, P4 9)
## Apps & catalog — 13 rows (P3 5, P4 8)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -141,9 +142,7 @@ stopping line that lies.
| **R-995** | Apps & catalog | P4 | **Removing an app through the product can leave unnamed Docker volumes and an empty import folder behind.** MEASURED 2026-10-13 on demo-felhom after 61 removals (`documentation/audits/sizing-2026-10-13/METHOD.md` „Teardown”, `evidence/leftover-volumes-after-bench.txt`): 8 unnamed volumes (4–72 KB; immich 1, romm 2, privatebin 3, onlyoffice 2 — volumes the images declare themselves) and `userdata/import/{paperless,calibre,grimmory}` (empty, created by the deploy). The removal answered 200 and its own leftover check was clean, because it looks only at labelled volumes. No data risk; disk and tidiness only. Removed by name on the bench. | **READY** — filed 2026-10-13 | — | Make the removal account for the containers' unnamed volumes (and an empty import folder it created), with a test that deploys an image that declares a VOLUME | CC |
| **R-1067** | Apps & catalog | P4 | **The catalog still carries `pi_compatible:` on every template, and nothing reads it any more.** controller v0.315.0 removed the „Pi kompatibilis" badge and the „csak x86" tag from every page (Raspberry Pi is no longer supported, so one was noise and the other was a warning on almost every app). The `ResourceHints.PiCompatible` struct field and the catalog key were deliberately LEFT: a box still on 0.314.0 reads the key, and removing it would make that box show „csak x86" on every app. **Gated on the floor:** once the fleet floor is at or above 0.315.0, drop `pi_compatible:` from the templates and the field from `stacks.ResourceHints` in the same pair of commits. Harmless until then — an unread key costs nothing. | **BLOCKED** — filed 2026-10-15. Checked 2026-10-16 (apps burndown): the floor is 0.321.0 and demo-hp, Tester 1 and the N100 run 0.321.0, but **Tester-2 still reports 0.292.0** (its newest report 2026-10-04; hub DB with WAL) — so nothing removed. | every box on ≥ 0.315.0 — today Tester-2 | Drop the catalog key and the struct field; grep the hub and the website for Pi wording at the same time (none found 2026-10-15) | CC |
| **R-1043** | Apps & catalog | P4 | **Sonarr, Radarr and Prowlarr build their login redirect as an absolute `http://` URL.** They trust forwarded headers only from their „Trusted networks” (the household set `192.168.0.0/24`; traefik is `172.18.x`), so they ignore `X-Forwarded-Proto: https` and answer `Location: http://<name>/login` to an https visitor. Since controller v0.312.0 that hop is sent back to https by traefik (a GET, no password), so it is cosmetic; a template could seed traefik's network as trusted. (`audits/https-only-2026-10-14/``1A-3-4.txt`) | **OPEN** — filed 2026-10-14 | — | Seed the docker network as a trusted proxy in the *arr templates (with the R-1030 seed), measure the redirect | 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". | **READY** — spike done 2026-10-18 (`audits/proxyauth-spike-2026-10-18/DESIGN-NOTE.md`): Traefik forwardAuth with `authResponseHeaders` strips a client's header (any case, twice) and its own value wins — measured; the config key is `auth.methods.proxy` (not `proxyAuth`); **but an app container reaching `filebrowser:80` directly logs in with a forged header (measured 200)**, and the dashboard cookie is host-only. Build = a files-only ticket cookie + a per-box secret header name. Filed 2026-10-16. | — | Build per the design note (controller only, ~one release), or decide not to | 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-1153** | Apps & catalog | P4 | **FileBrowser's preview of a .docx answered 500 while the OnlyOffice link was on** (`/api/resources/preview`, scratch 9202, controller 0.330.0, 2026-10-18). FileBrowser builds an office preview through OnlyOffice's converter from `server.internalUrl` when it is set (`backend/http/preview.go:243` at 1.5.6-stable) and from the request's host when not — so 0.331.0's `internalUrl` (R-1018) may already fix it. Not re-checked: OnlyOffice was removed after the walk. | OPEN — filed 2026-10-18 | — | One preview request on a box with 0.331.0 and the link on | CC |
## App updates — 4 rows (P3 4)
@@ -155,12 +154,10 @@ stopping line that lies.
| **R-683** | App updates | P3 | **[P3-LOW] Watch: after a power cut during an update's health check, the hold named an HOUR-OLD second-drive copy, not the one the update's own backup should have just made.** 2026-09-24 chaos round 3 (nextcloud, `backup_max_age: 1m`): no `backing-up` phase was seen and the hold named Tier 2 at 13:04 for an update pressed at 14:04; the pre-cut controller log was lost with the container (the runner now saves it at arm time — R-320). Round 11, the same action without a power cut, named a fresh 14:34 copy and logged the Tier-2 copy. The sentence was TRUE (it named the copy it offered); the question is why the update did not back up first. Not reproduced; watch the next power-cut drill. `audits/night-2026-09-24/E/round-03*.json`, `E/round-11-controller-pre.log` | **OPEN — P3; owner: CC (watch)** | — | — | CC |
| **R-785** | App updates | P3 | **[P3-LOW] SparkyFitness is pinned 11 releases and a major behind upstream (v0.17.3; upstream v1.7.3, v1.6.0 dated 2026-07-24).** READ 2026-10-01 (`audits/visitors-2026-10-01/C/bench/C1-previous-tag.txt`). **Needs:** an update walk 0.17 → 1.x through the ladder (bench + box), after R-784 is decided. | **OPEN — rank P3-LOW; owner: CC (after R-784)** | — | — | CC |
## Backup & restore — 30 rows (P2 3, P3 16, P4 11)
## Backup & restore — 28 rows (P2 3, P3 13, P4 12)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
| **R-978** | Backup & restore | P3 | **Starting the escrow ceremony again can make the customer's written-down recovery code useless: the new package replaces the old one before the new code is confirmed.** It can happen to an ordinary customer who starts and walks away. Fix: keep the previous package until the new code is confirmed. 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 — the fix is in agent `main`, unreleased** (`8561dc8`, 2026-10-18): the new package reaches the hub only when its code is claimed; an abandoned second ceremony leaves the first code opening the hub's package (tests + four red-proofs). Residual R-1151. Rides the next vouched agent release. | — | Release with the operator's yes; then close | CC |
| **R-973** | Backup & restore | P3 | **A compromised controller can make the whole-guest backup run again and again, and each run prunes older restore points: the local tier directly, and the off-site tier through the store's own nightly keep-last-2.** So `07` §8 row 10 (ransomware) is weaker than it reads. The second reader confirmed the local half; the off-site half is inferred. Fix: a cadence or daily cap on the local-API backup route, and off-site snapshots that a nightly prune cannot remove. 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 — the local-half fix is in agent `main`, unreleased** (`f17e197`, 2026-10-18): a cadence cap on `POST /backup` — daily tier ≤ 3 successful runs per rolling 24 h, the weekly off-site tier one per 6 days; a refusal is 429, logged, persisted across a restart, and a CRITICAL degraded status for 24 h (operator-only `agent_capability_degraded`, wording R-1150). *Decided by CC unattended — operator may reverse:* the numbers. **Off-site half, design line (not built):** ep0 prunes the namespace keep-last 2 — a count, so each extra run pushes out the oldest; with the cap a pre-compromise copy survives ≥ ~6 days and each extra attempt alarms; to make it unfloodable, ep0's prune moves to time buckets (e.g. keep-weekly 2) — an ep0/hub prune-job change, the operator's; check that DooPlex's pull copy of ep0 is keep-weekly, not keep-last. Local keep-last 3: R-1149. | — | Release with the operator's yes; decide the off-site prune shape | CC |
| **R-232** | Backup & restore | P2 | **DooPlex's backup makes every copy inside the same box — and nothing tells anyone when it fails.** Surveyed read-only 2026-08-06 (`audits/RECON-dooplex-backup-2026-08-06.md`). **What works:** five sets, 14/14 successful runs in 14 days; a file was restored from the `data` repo and matched the live original **byte for byte**; every set except two is cross-disk; k3s is integrity-checked on every run. **What the matrix exposes, ranked:** (a) **`notify_failure` is a no-op** — `NOTIFY_ON_FAILURE=true` but `NOTIFY_WEBHOOK_URL` is commented out, so a failed backup notifies **nobody**; the project already has a working Resend path that CI uses. Cheapest item, and it makes every other failure visible. (b) **Nothing leaves the box** — no rclone, no remote repo, no off-site target anywhere; Longhorn's target is `nfs://192.168.0.180:` pointing at DooPlex itself, and the only outbound-looking cron pulls *inbound* from Hetzner for a different project. The machine that runs the hub managing the customers' off-site chain has no off-site copy of its own. (c) **The backup tree is a single writable path** and the restic repos are not append-only — one bad script or ransomware destroys every copy at once. (d) **Two same-disk sets**: `.claude-memory` and the PostgreSQL dumps, whose source directory sits *inside* the backup tree. (e) **Longhorn `retain=1`** — one generation per volume, so a corruption noticed a day late has no earlier copy. (f) **`/opt/backup/docs/BACKUP-RESTORE.md` does not exist** though the systemd unit advertises it. (g) **`secrets/restic-repo` has never held a snapshot** — `backup-secrets.sh` contains no `restic` call; the secrets are GPG files on `sda1` only. (h) **No restore has ever been run** beyond today's single-file probe — the matrix's "ever demonstrated?" column is otherwise entirely empty. **Not a finding:** the restic passphrase. The on-box copy is on `sdb1`, a different disk from the backups, and the **operator holds an offline copy out of band** — so a disk loss is recoverable. The narrow residual is that it is operator-held rather than system-held, unlike the customer case's hub-vaulted escrow, so it should be confirmed current and findable by someone else. **Nothing was changed by the recon.** | **NARROWED 2026-10-08 — owner Viktor.** (a) DONE with the operator's yes in chat: `notify_failure` now mails admin@felhom.eu through Resend; proven by one test mail that reached the inbox (`audits/day-2026-10-08/r232/`; no backup was started). (b) partly: the hub database leaves DooPlex nightly to ep0 (R-173); everything else stays on the box. (c)–(h) unchanged. **2026-10-09 (operator yes in chat): (b) and (h) DONE for Gitea and the secrets.** Nightly 00:20 `felhom-dooplex-offsite` pushes Gitea (614 MB of repositories, the `gitea` database dump taken first, `app.ini`) and the nightly GPG secrets export, encrypted with a NEW key, to ep0 `operator` (`host/dooplex-gitea`) on the hub-DB write-only token — no change on ep0; first push 57 s; restore test weekly (manifest, `git fsck` every repo, `pg_restore --list`), run once: 27 805 files, 10 repos OK; `DooplexGiteaOffsiteStale`/`DooplexGiteaRestoreTestStale` in force (`absent()` included, promtool + 2 red-proofs); every backup unit now mails on failure (`OnFailure=`, dry run reached the inbox). **First real Gitea restore:** into a throwaway on the bench, 10/10 repos, product `main` = live, a file byte for byte, a login (`runbooks/gitea-restore.md`). The hub came back too (R-173, closed). **The registry is left out on purpose:** its images rebuild from the code. **Correction to the recon §7:** the API mirror holds ONE repo (`homelab-manifests`), not all — Gitea had a single copy, on DooPlex. `audits/dooplex-survival-2026-10-09/` **LEFT:** (c) append-only for the on-box tree, (d)–(g) as written; the paper copy of the new key (operator). **READY** for the rest | — | — | operator |
| **R-893** | Backup & restore | P3 | **After a failed OFF-SITE replay, the rollback pours the NEWER pre-restore copy over the OLDER volume just put back.** Read in source 2026-10-06 (R-638 option A, not measured): `internal/backup/offbox_reconstitute.go` writes the undo copy from the live (newer) database, replaces the volumes with the snapshot's older tars, then — when the replay fails — `rollbackSafetyDump` loads that newer dump over the older database volume. The loader only drops what the dump knows, so tables the newer migration removed stay; and when the snapshot's older definition was written, the rollback branch does not put the newer definition back, so the older app starts on rolled-back data; non-database volumes stay at the snapshot's state. An order change cannot fix it (the only undo is a logical dump, and its volume was replaced). Known limit in `07` §6.3. **-- 2026-10-08 design:** `audits/day-2026-10-08/design-R-893.md` — re-verified; and the screen's „your data is back as it was" (`err.backup.db_restore_failed_rolled_back`) is false for files, other volumes and the app version. Question D8 on STATUS's decision sheet. **-- 2026-10-08 14:16 operator ruling D8 (`09` §3 decision 192):** yes, both, in that order — first the app stays stopped for support (option C), next „put back exactly as it was" (option A). | **NARROWED — 2026-10-09: the first half DELIVERED in controller 0.304.0. Slice 0 (the live measurement) NOT run: 9202 has no off-site, and a dump cannot be cut by hand inside an encrypted off-site copy on Tester 1; it needs a scratch off-site repo — a plan, not a finding.** **NARROWED 2026-10-08 — the first half (D8 option C, the app held stopped) is built on controller main and ships tomorrow; what stays open is the second half, „put back exactly as it was” (option A: a pre-restore copy of the app's volumes and placed files, a fit check, disk for one copy; the slice-0 measurement on 9202 first).** **OPEN — filed 2026-10-06** **2026-10-06 night: verified in source, no code** — `offbox_reconstitute.go:758` (undo dump from the live DB), `:778-860` (files and volumes from the snapshot), `:804` (the snapshot's definition is written when its version differs), `:898` (the rollback loads the newer dump over the older volume; nothing writes the live definition back). Not a reorder fix: it needs R-638 option B (a rebuilding loader) or a pre-restore volume copy (disk cost; R-685's class). Which state a household gets after a failed off-site replay is the operator's call. Next: the 9202 measurement, then the design. | a design: R-638 option B (a loader that rebuilds instead of overlays) or a pre-restore volume copy | Measure it once on 9202 (a forced replay failure after an off-site restore over a migrated app); then a design for the operator | CC |
| **R-895** | Backup & restore | P2 | **The hub's clean-up-window check trusts the snapshot counts the box sends, so a broken-into box (or past-dated fakes added through the add-only key) can shrink the real off-site history without an alarm.** READ 2026-10-06 night in source (R-822's design): the before/after comparison uses counts the box itself reports (`hub/internal/offsitekeys/service.go:284`, `:343`); new fakes keep the count level. Decision 68 already accepts a box-trusted count. | **NARROWED 2026-10-18 — slice 2 built, not deployed** (hub v0.158.0): the 07:10 audit compares the `snapshots/` listing with yesterday's by name — files gone that no clean-up window explains → `offsite_snapshots_vanished` (error), files no reported box run explains → `offsite_snapshots_unexplained` (warning); operator-only, hub-origin (not in `allowedEventTypes`). First listing = baseline; a move-aside or RESET drops it; vanishing judged on a pinned tier only; files younger than 1 h judged the next day. Red-proofed twice (`snapshot_watch_test.go`). **Not built:** the window's before/after on the hub's own count (design §3.1) and refusing a window while unexplained files exist (§3.2). Unmeasured: the provider shell's `ls -l` time zone (± 3 h slack until then). **Deployed 2026-10-18 12:23 (hub v0.158.0).** The first listing (tomorrow 07:10) is a baseline; the first verdict is 2026-10-20 07:10. **Known limit (independent review 2026-10-18, F2):** `_unexplained` is explained by the box's OWN reported `offsite.last_run`, so whoever holds a box's key can silence it; `_vanished` is judged on the hub's own listing and is not affected. | the operator's yes to deploy hub v0.158.0 | Deploy; read the first 07:10 lines (newest upload vs the box's last run = the time-zone calibration), shrink the slack; then decide §3.1/§3.2 | CC |
@@ -187,15 +184,14 @@ stopping line that lies.
| **R-878** | Backup & restore | P4 | **A catch-up (R-871) runs the database-dump leg in the DAY, and that leg stops an app with a volume for its copy — the household may notice the stop, and a large volume makes it longer.** MEASURED 2026-10-05 on demo-felhom: the catch-up at 08:25:02 stopped opengist, copied 182.5 KB, started it again — about 1 s, then a few seconds of `health: starting`; the night does exactly the same, unseen. Nothing measured for a large volume. Fix direction (if it matters): skip the volume copy of a running app in a DAYTIME catch-up and leave it to the next night, or warn. `audits/catchup-2026-10-05/partA/live-demo-felhom.txt` | **READY — owner: CC** **2026-10-05 (burn-down night): NEEDS A LIVE MEASUREMENT** — a timed catch-up on 9202 for an app with a multi-GB volume, then the skip-or-warn choice. | — | measure a large volume first | CC |
| **R-32** | Backup & restore | P4 | **[P2-HIGH] RESET must purge the customer base dir; the orphan card must stay honest; unattributed bytes must be visible.** The rehearsal's S7 said in advance that an orphan card would BE a finding — and one appeared (16:58:14). Cause: RESET's `"hetzner":"ok"` leg destroys the sub-account, but **a Hetzner sub-account is an access-control object, not a data object** — its directory survives, so re-enabling offsite recreated an account over the previous lifecycle's ciphertext, encrypted under a key that same RESET had destroyed. **MIGRATED FROM `ROADMAP.md` 2026-08-22 (R-369) — originally filed 2026-07-21, size M, roadmap state `idea`.** Moved verbatim; nothing added or reinterpreted. The roadmap keeps its copy as history, marked moved. | **OPEN — migrated from ROADMAP 2026-08-22, rank unchanged** **2026-10-06 night: design written** (`audits/night-burndown-2026-10-06/design-R-32.md`) — pick A: purge the repo + set-aside copies through the sub-account's own password login BEFORE deleting it (no main-account credential); the „repo data destroyed" comment at `hub/internal/offsite/offsite.go:276-280` is false for the shared tier; part (3) waits on a read-only measurement (`du` on port 23). Operator: agree to route A. **2026-10-07 07:58: `09` §3 decision 167 — option A yes, and delete the old RESET leftovers once.** **2026-10-07 (Part D step 2, read only):** the hub shows bx11 holding 3.3 GB of data while the four live customers account for ~0.6 GB → **~2.7 GB belongs to no live customer**; no live customer shows a set-aside folder, so the leftovers are in homes whose sub-account is gone, reachable only by the pool box's MAIN account. **The one-time clean-up is STOPPED:** no main-account login for bx11 is held on DooPlex (the API token in the credentials file sees no box). Needs: the main-account login, or the operator lists and deletes in the Hetzner console (`audits/day-2026-10-07/D/D2-listing-readonly.txt`). **2026-10-07: option A DELIVERED (hub 0.142.0):** RESET purges the repo and every `.orphaned-*` through the sub-account's own login before deleting it, and keeps the sub-account if the purge fails (red-proved, `audits/day-2026-10-07/D/`). Not yet seen live (no scratch RESET today). **The one-time clean-up of the ~2.7 GB of leftovers is STOPPED:** it needs the pool box's main-account login (see above). **2026-10-07 14:43 — `09` §3 decision 173: leave the ~2.7 GB of old leftovers for now; re-ranked P2 → P4.** Option A itself is delivered (hub 0.142.0). What stays open is ONLY the leftovers: they were computed (box total minus live customers), never listed, and deleting them needs the pool box's MAIN login, which reaches every customer's data. **Blocker: a safe main-account path.** | — | **Ruling from the run (three parts, deliberately separate):** (1) because RESET destroys custody, the ciphertext it leaves behind is unrecoverable **BY DESIGN** → RESET gains a **main-account purge of the customer base dir** (the existing operator ack already covers it); (2) the **move-aside guard STAYS** for reinstall-*without*-RESET — there custody survives and the card's "history recoverable" promise is true (R-26 depends on exactly that); (3) the operator **Restic tab shows per-customer directory bytes vs attributed snapshot bytes**, so dead data cannot hide. Measured on the pool box that night: **49 M attributed** (2 snapshots, 48.717 MiB) against **1.4 G + 3.0 M unattributed** across TWO `.orphaned-*` dirs. Evidence `restic-and-pool.txt` | CC |
| **R-1087** | Backup & restore | P4 | **Whole-box copies left on the OLD local target are never pruned after the target moves.** MEASURED 2026-10-15 (storage-identity build B space check): the N100's host root (`local`) holds three copies from 2026-07-28 (5.5–5.7 GB each, ≈ 16.8 GB); its agent's `local_backup_target` is now `felhom-backup` (the USB disk, retention 3), and retention prunes only the current target. On a small-root box such leftovers would starve the local copies (R-548). Evidence: `audits/storage-identity-buildB-2026-10-15/space/felhom-pve-9201.txt`. | OPEN | — | Decide whether the agent prunes its own `vzdump-lxc-<vmid>-*` files on a former target (after a newer copy exists on the new one), or the target move asks; until then, the N100's three July files can be removed by hand with the operator's word. | CC |
| **R-1149** | Backup & restore | P3 | **The local whole-guest tier prunes keep-last 3, so even with the R-973 cadence cap a compromised controller can replace all three restore points within one day.** A time-bucketed prune (`--prune-backups keep-daily=3`) has the same disk cost and cannot be flooded within a day. It changes `07` §6's retention, so it is the operator's call. Found by the R-973 helper 2026-10-18. | OPEN — filed 2026-10-18 | — | Operator decides keep-last vs keep-daily; then the agent's vzdump argument | operator + CC |
| **R-1151** | Backup & restore | P3 | **The new escrow package now uploads when its code is claimed (R-978), but the wizard's typed-back check is the browser's alone:** a household that opens the new code and closes the page without writing it down has still replaced the hub's package, and the first code no longer opens it. Gate the upload on the typed-back confirmation with a new controller → agent call. Found 2026-10-18. | OPEN — filed 2026-10-18 | — | Controller + agent: the confirm call; red-proof the abandoned-after-claim case | CC |
| **R-1161** | Backup & restore | P4 | **The escrow wizard still says „Ez mostantól az élő helyreállítási kód" („this is now the live recovery code") the moment the code is shown;** since R-1151 (agent 0.160.0, controller 0.332.0) the code becomes live only after the two words are typed back, and closing the page keeps the old one. The safe direction (it overstates), but it is now untrue until the confirm. A Hungarian + English copy change; not done in-session (the controller's one release of the session was out). Found 2026-10-19. | OPEN — filed 2026-10-19 | — | Next controller release: say the code becomes live after the check | CC |
| **R-1162** | Backup & restore | P3 | **The whole-guest off-site tier (ep0) prunes by count (keep-last 2), so a compromised controller can still push out the pre-compromise copy, slowly:** with R-973's cap one run per 6 days gets through, so a copy older than the compromise survives about 6 days and every extra attempt alarms. Time buckets on ep0's prune job (e.g. keep-weekly 2) would make it unfloodable; check that DooPlex's pull copy of ep0 is keep-weekly, not keep-last. The off-site half of R-973, carried over when R-973 closed. An ep0/hub prune-job change. | OPEN — carried from R-973 2026-10-19 | — | Operator decides the ep0 prune shape | operator + CC |
## Storage & devices — 10 rows (P3 4, P4 6)
## Storage & devices — 9 rows (P3 3, P4 6)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
| **R-1073** | Storage & devices | P3 | **Felhom in a virtual machine sees no disk health: SMART is UNKNOWN for its own system disk and any passed disk.** MEASURED 2026-10-15 on the OMV-VM copy: every storage target reported `smart.health = UNKNOWN`, all counters null (the guest sees QEMU disks). On the pilot's box the NVMe and the „old, not stable” Toshiba would be watched by OMV only, and Felhom's drive-health alarms (R-330/R-332's whole point) cannot fire. Evidence: the host report in `audits/omv-vm-2026-10-15/README.md` (memory/disk section) and `C-hub-events-Tester4.txt`. | WAITING-ON-OPERATOR | Operator: is a VM install a supported shape, and if so, is „OMV watches SMART” acceptable or must Felhom say on the dashboard that it cannot watch the disks? | Operator rules; then CC adds a household line on the Meghajtók page when SMART is unavailable. | Operator |
| **R-1093** | Storage & devices | P3 | **An EMPTY partition asks for the typed „data will be erased" confirmation.** MEASURED 2026-10-16 (build B, scratch VM, agent 0.158.1): `POST /api/storage/init` for a blank part → `needs_confirmation`, reason „device has a partition table" — `lsblk` reports the PARENT disk's table (PTTYPE) for a partition, so the agent's probe reads an empty part as data-bearing. Safe direction (more friction, never less), but `16` §6 says an empty part is self-serve and the wizard shows „adatot tartalmaz". Evidence: `audits/storage-identity-buildB-2026-10-15/live/12-partitions-format.txt`. | **OPEN — the fix is in agent `main`, unreleased** (`f7b7308` + `0963bae`, 2026-10-18): lsblk's inherited PTTYPE is ignored only when `blkid -p` on the partition exits 0 with `PART_ENTRY_NUMBER` and the same scheme; every error path stays data-bearing (14-row test, three red-proofs; no sudoers change). Live on demo-hp (a loop file in `/root`, removed): the empty GPT part → blkid rc 0 with only `PART_ENTRY_*`; ext4 → `TYPE`; a table inside → `PTTYPE=dos` (`audits/agent-fixes-2026-10-18/r1093-loop-check.txt`). Not walked: `/api/storage/init` on a VM disk. | — | Release with the operator's yes; one init call on a VM with a spare disk | CC |
| **R-1062** | Storage & devices | P4 | **The N100's USB drive cannot be split in two without losing the box's whole-guest backup target.** The operator asked (2026-10-15 16:31) for two halves for multi-drive tests. Read first (`audits/n100-lived-in-2026-10-15/B/`): no family files — 2.7 GB nightly vzdumps of guest 9201 (`dump/`, PVE storage `felhom-backup` = the whole-guest backup target), 3.1 GB app backups + an off-site restore copy, 422 MB leftover app data of removed apps, empty media folders. The agent refuses a decommission of a backup target (`refuseIfBackupTarget`) and the N100 has no other drive to move that role to. Nothing changed. | **WAITING-ON-OPERATOR** — filed 2026-10-15 | operator decision | Keep one drive (CC's pick: the lived-in box keeps its local whole-guest backup), or give a word to drop that backup leg / add a second drive first | operator |
| **R-1064** | Storage & devices | P4 | **Removing a storage path does not mention the cross-drive backup copies it holds.** demo-hp 2026-10-15: `/mnt/sizing-bench` removed through `/settings/storage/remove` (302, „Adattároló eltávolítva") while calibre-web's and paperless-ngx's `cross_drive.destination_path` pointed at it; the copies stay on disk, the records still name the removed path. The in-use check counts HDD_PATH only (`appsUsingPath`). | **OPEN** — filed 2026-10-15 | — | Read what tonight's tier-2 run (01:30) does with the two records; then: name the copies in the removal's confirmation (or count them as in use) | CC |
| **R-330** | Storage & devices | P3 | **Disk health Phase 2 — the three SMART attributes the wire does not carry.** The failing drive's most telling counter was **187 `Reported_Uncorrect`**, sitting at normalized **1** against threshold **0** with a raw count of **1001** — one point from failing and structurally unable to get there. Also wanted: **199 `UDMA_CRC_Error_Count`** (cabling) and **188 `Command_Timeout`**. None are on the agent→controller wire today, so v0.215.0's ladder could not use them. Phase 2 also persists periodic SMART **samples** (the right home is `metrics.MetricsStore`, NOT the Phase-1 state file, which is one record per disk and must stay that way). **This is a declared WIRE change, so under the G-1 gate the hub must model the new fields in the SAME session** — that is precisely why it was kept out of Phase 1, where it would have turned a one-word severity fix into a three-repo change | **READY** — **2026-10-12: the wire half is CLOSED in this row** — released in agent v0.150.0 (boxes run 0.154.0), controller and hub decode 187/188/199. **Only open work:** the verdict slice (use the three counters in the disk verdict and the household's words) and persisting samples. **READY (M) — NEW 2026-08-14** **2026-10-06 night: the wire half BUILT on main (unreleased):** the agent sends SMART 187 `reported_uncorrect`, 188 `command_timeout` (raw, vendor-packed on some drives — carried as reported), 199 `udma_crc_errors` (pointer + omitempty: unknown is absent, never 0); the controller decodes them and the hub's host-report mirror models them (G-1 wire gate green; it convicted the agent half alone). **Carried only** — no verdict, banner, mail or alarm reads them (`TestR330_CountersChangeNoVerdictYet`); using them changes what a household is told and is the next slice, with persisting samples. Red-proofs `audits/night-burndown-2026-10-06/r330/`. Ships with agent v0.150.0 and the next controller and hub releases. | R-328 (closed) | The verdict slice: 187/188/199 into the disk verdict with tests; then persist samples | CC |
@@ -205,7 +201,7 @@ 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 — 20 rows (P1 1, P2 2, P3 11, P4 6)
## Security & access — 22 rows (P1 1, P2 2, P3 12, P4 7)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -229,6 +225,8 @@ stopping line that lies.
| **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-1118** | Security & access | P3 | **CC's secrets moved off the plain `~/.config/credentials` into an encrypted store with a broker and guards; what is left is retiring the old file.** BUILT 2026-10-17 (store B, operator ruling 11:28): private repo `admin/cc-secrets` (`ARCHITECTURE.md`, the design verbatim in `docs/`), `sec` (no get), PreToolUse refusals + PostToolUse masking for Bash and Read in `~/.claude/settings.json` (every CC session on DooPlex), nightly transcript sweep with mail. 55 tests, 18 red-proofs, 36-row child-session proof in both launch modes, fresh-session proof (`audits/cc-creds-build-2026-10-17/`). 25 names in the store (23 imported, `RESEND_API` from CC's environment, a self-test value). `12-agent-tooling.md` §5 points at it. | NARROWED — built; the old file's retirement is R-1134 | the week with no reader (R-1134) | `sec doctor` stays all OK; R-1134 | CC |
| **R-1121** | Security & access | P3 | **`~/rotated-secrets-2026-10-09.txt` still exists on DooPlex** — 632 bytes, modified 2026-10-09 13:08 +0200, mode 600 (SPIKE Q6: `test -e` + `stat` only, not opened). Left by the R-925 rotation work. If any value in it is still valid, it is one more plaintext secrets file in the home folder, readable by every CC session. | WAITING-ON-OPERATOR | what the file holds — only the operator knows | Operator: confirm every value in it is revoked, then `shred -u` it; or move the live ones into the R-1118 store | operator |
| **R-1155** | Security & access | P4 | **FileBrowser's own 2-hour token for the one-sign-in user can outlive the dashboard session.** The controller clears it together with the files cookie whenever a stale files cookie arrives (sign-out, password change), but a browser that lost only the files cookie (or a token copied out of it) keeps FileBrowser's token until it expires, and with no files cookie the forwardAuth answer passes the request through. Closing it would need the box to read FileBrowser's token (a box-held `auth.key`) or a shorter FileBrowser token life. Found 2026-10-19 (R-1100 build, security review). | OPEN — filed 2026-10-19 | — | Decide whether a box-held FileBrowser key is worth it | CC |
| **R-1159** | Security & access | P3 | **The household's retrieval passphrase already yields the controller's hub API key, and that key alone registers and confirms off-site keys.** `GET /config/{id}` (passphrase-gated) returns `controller.yaml` with `hub.api_key`; `/offsite/register-key` and `/offsite/confirm-key` accept that key (`offsiteKeyAuth`). Pre-existing, and wider than R-1154 (whose fix keeps the passphrase away from the operator's re-issues). Read from code by the hub helper 2026-10-19, not measured. | OPEN — filed 2026-10-19 | — | Measure what a passphrase holder can do to the off-site tier; decide a second factor or a scope | CC + operator |
## Box system & updates — 12 rows (P2 1, P3 7, P4 4)
@@ -263,7 +261,7 @@ stopping line that lies.
| **R-285** | Monitoring & notifications | P4 | **A planned, supervised reinstall pages the operator as if the machine had died — there is no notion of expected downtime anywhere.** During the 2026-08-09 rehearsal the hub sent, all `status: sent` to the operator channel: `host_stale` 08:58 UTC, `node_stale` 09:00, **`host_down` 09:28 (error)**, **`node_down` 09:30 (error)**, `host_leaf_changed` 09:31, `host_recovered` 09:31, `node_recovered` 09:34, `offsite_delivery_stuck` 09:34 — eight operator mails for work that was deliberate, attended and announced. **This is the OPPOSITE gap from the one R-281 filed:** the alarms are not missing, they are indiscriminate. `host_stale` at 30 min and `host_down` at 60 min (`monitor/host_staleness.go:22-23`, `downAfter = 2 * threshold`) cannot distinguish a wiped-on-purpose box from a dead one, and `host_leaf_changed` firing on a reinstall is correct-but-expected. **Note the interaction with the mute used on 2026-08-09 evening:** blocking a customer silences everything, so today the only two settings are *page me for planned work* and *tell me nothing at all*. **What is owed is a middle:** a maintenance window, or an operator-set expected-downtime flag, that suppresses staleness and leaf-change while leaving genuine faults audible | **READY (M) — NEW 2026-08-09** | — | The evidence is the operator's mailbox plus `events`/`notification_log` for 2026-08-09 | CC |
| **R-1005** | Monitoring & notifications | P4 | **A recovery mail says how long the box was „stale", not how long it was gone: after a 51-minute power cut demo-hp's `host_recovered` read „was stale for 8m" and `node_recovered` „23m".** An operator reading only the recovery mail under-reads the outage (`audits/night-power-2026-10-13/` R6). Same kind, the other way: the off-site mail of 2026-10-14 02:18 was titled „3 of 3 apps failed to back up (offsite run)" while 11 other apps were backed up in that run — only the 3 flagged apps were counted. | **READY** — filed 2026-10-14 | — | CC: add the time since the last report before the gap to both recovery messages, and count every app of the run in the off-site subject, with a test that asserts each text; next hub release | CC |
| **R-1049** | Monitoring & notifications | P3 | **A pulled drive mails the operator „unexpectedly disconnected” (error), and nothing ever mails that it came back.** MEASURED 2026-10-14 night (`audits/media-drive-events-2026-10-14/K8-hub-mails.txt`, the hub DB with its WAL from the running pod, and the mailbox as the second channel): two pulls → two `storage_disconnected` mails; the hub RECEIVED both `storage_reconnected` and both `health_recovered` (severity info) and logged no notification for any of them — they are not in `recoveredPairedDownTypes` (`hub/internal/notify/dispatcher.go`), so the severity gate drops them. The R-97a shape: told it broke, never told it healed — the operator cannot tell a returned drive from one still gone without opening the hub. **BUILT 2026-10-15 — hub v0.151.0 (`12552aca`): both pairs in `recoveredPairedDownTypes`, cooldowns ended by the next loss (`TestR1049_DriveBackMailsBothEdges`, red-proved). Not deployed: the operator's „yes” first (decision 162); the image build waits for it too.** | **WAITING-ON-OPERATOR** — hub v0.151.0 code pushed, not built, not deployed | the operator's „yes” to the hub deploy | On the „yes”: build + deploy hub v0.151.0; read a „came back” mail on the next pull | CC |
| **R-1150** | Monitoring & notifications | P4 | **A refused whole-guest backup (the R-973 cap) reaches the operator as `agent_capability_degraded`, whose text reads „agent privileged capability degraded”.** True but misleading for a refused backup; on the night path the controller may also mail its own backup failure. Add a dedicated operator-only type (`whole_guest_backup_refused`: `allowedEventTypes` + `operatorOnlyEvents`) or adjust the text, in the next hub release (a two-repo change: the agent's status name stays). Found 2026-10-18. | OPEN — filed 2026-10-18 | — | Next hub release | CC |
| **R-1157** | Monitoring & notifications | P4 | **While the cadence-cap flag stays degraded (the agent's 24 h hold), further refused backups raise no new alarm:** a guest refused every day reads as one long episode, one mail. The same before hub 0.159.0. Found by the hub helper 2026-10-19. | OPEN — filed 2026-10-19 | — | Decide whether a refusal count in the event detail or a daily repeat is wanted | CC |
## Hub & operator — 12 rows (P3 6, P4 6)
@@ -303,7 +301,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 — 31 rows (P3 9, P4 22)
## Process & tooling — 31 rows (P3 8, P4 23)
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|---|---|---|---|---|---|---|---|
@@ -337,6 +335,7 @@ stopping line that lies.
| **R-1128** | Process & tooling | P4 | **The controller's decoy suite edits real tracked files (`handler_debug.go`, `update.go`, the locale bundles, templates) and plants `.go` files, with no lock** — a full run while another session edits the controller tree can overwrite that session's edits. felhom.eu's suite took an `flock` after the 2026-10-08 clobber. Found by the R-1116 helper 2026-10-17. | OPEN — filed 2026-10-17 | — | Run the suite in a scratch copy of the tree, or take the felhom.eu-style lock | CC |
| **R-1123** | Process & tooling | P4 | **`reuse_refs_check.py` treats every git directory beside the repo as a sibling repo**, including stray clones in the workspace (`felhom.eu-media`, `felhom.eu-sizing-push`, `felhom.eu-wt-sizing`), so a citation can resolve against a stale clone and pass. Found by the R-1116 helper 2026-10-17 (it also makes the agent suite 20 s here vs 5 s in CI). | OPEN — filed 2026-10-17 | — | Search only the four known repo names (or the main clones), with a decoy | CC |
| **R-1152** | Process & tooling | P4 | **felhom CI's 5-minute job timeout is inert** (act_runner 0.6.1 does not enforce a job-level `timeout-minutes`, R-1136), so a hung step runs up to the runner's 3 h. Move it to a step-level `timeout-minutes` on each workflow's gate step (e.g. 20 min for felhom.eu, which runs ~7 min), measured first: a step timeout fails the job AND the `if: failure()` alarm still runs. Needs a scratch repo (the admin token cannot create one — the operator, or a token with `write:user`). Found 2026-10-18. | OPEN — filed 2026-10-18 | — | Measure on a scratch repo, then the four workflows | CC |
| **R-1160** | Process & tooling | P4 | **The hub and the agent have no gofmt gate, and both drift:** about 20 hub files and 14 agent files are not gofmt-clean (`gofmt -l`, 2026-10-19); the controller has `gofmt_gate.py`. Not fixed in-session: a mechanical rewrite of files other sessions own. | OPEN — filed 2026-10-19 | — | One gofmt commit per repo in a quiet window, then the controller's gate in both runners | CC |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.
+1 -1
View File
@@ -1,4 +1,4 @@
## v0.159.0 — a refused whole-guest backup has its own alarm text (R-1150); a reinstall over the old host record warns the operator instead of staying silent (R-1154) (2026-10-19)
## v0.159.0 — a refused whole-guest backup has its own alarm text (R-1150); a reinstall over the old host record warns the operator instead of staying silent (R-1154) (2026-10-19) — DEPLOYED 2026-10-19 04:14 local on the operator's yes (given in advance in the day brief)
- **`whole_guest_backup_refused`** (R-1150). The agent's R-973 cadence cap reports a CRITICAL capability
`localapi:backup-cadence-cap` as degraded for 24 h after it refused a whole-guest backup a guest asked for beyond its