Restore-holes run (2026-10-24 night): D43 = A recorded; 07/05/03/11 carry the R-1251, R-1252, R-1254 and D43 rules; register 207 -> 208 (R-1268 opened; R-1251/1252/1253/1257 VERIFY, R-1254 NARROWED); STATUS Monday list + D44/D45 picks; evidence (Tester 1, 9202)
gates / gates (push) Successful in 13m4s

This commit is contained in:
admin committed 2026-10-24 21:08:38 +02:00
1 parent dcf4f84cb7
commit 3add386aa9
60 files changed
+2082 -16

No files matched your search

@@ -130,6 +130,13 @@ the one trust root; the agent's API token is untouched; a box gets the new rule
0.146.0 cannot update itself on that box).
**[DESIGN, ruled 2026-10-22 15:47 (D4, R-982) — SHIPPED in agent v0.163.0 (`1109092`) and installer-v1.36.0 (`manifests/webpage.yaml:347`); corrected 2026-10-24] Root SSH from the LAN by key only; the console password stays.** The config bundle gains one fixed file, `/etc/ssh/sshd_config.d/10-felhom-root-key-only.conf` (`PermitRootLogin prohibit-password`), checked and reloaded by `felhom-os-apply`; the installer's uninstall removes it. Why a drop-in: Debian's `sshd_config` Includes `sshd_config.d/*` FIRST and sshd keeps the first value it reads, so the drop-in wins over the stock line; the out-of-band operator sshd (`FELHOM_SSHD`, port 8822) has its own config with no Include and is not touched. Proven on a fresh ISO 1.30.0 install: password SSH refused after, key SSH and the PVE web password login (200) and the console login still work (`audits/night-2026-10-22/P2-D4/`). It ships with the next agent release + installer tag.
**[DESIGN, operator ruling D43 = A, 2026-10-24 — committed agent `c8d3c17`, unreleased; R-1257]** **Appliances only.** The
drop-in's bundle policy is `appliance`: written only when the root-owned install record (`/var/lib/felhom-install/state.json`)
says `appliance` (the installer records the mode before it installs the bundle); on a `byo` record nothing is written and
a copy WE wrote (this path, beginning with our header line) is removed, then `sshd -t` and a reload; a box with no
readable record is left as it is. The „in force" check asks `sshd -T` for each of the host's own global IPv4 addresses
(a `Match Address <LAN>` block no longer reads as in force); a failing `sshd -t` puts back ONLY the SSH file and keeps
the rest of the bundle (the report says `sshd_step_undone`).
**[FACT, agent `a0c88e5`, released in v0.163.0 — corrected 2026-10-24] The heartbeat reports a WireGuard handshake from the future (R-1204).** WireGuard refuses an initiation whose TAI64N time is not newer than the last one accepted from that peer, so a box that once ran with its clock ahead is locked out of ep0 until real time passes that moment. The agent used to clamp the negative age to 0 (it looked healthy); `wgtunnel.handshakeFuture` now sets `handshake_future_s` when the handshake is more than 60 s ahead (`TestR1204_AFutureHandshakeIsReported`). The hub alarms on it (`05` §15.4b).
@@ -296,6 +296,12 @@ What remains:
PBS repo and namespace in the **DR recipe** (`dr_recipe`, reported every cycle — R-106 fixed it to match the
backup); the endpoint's PBS fingerprint in **tenantsync** (`hub/internal/tenantsync`); the wrapped key in
**`host_escrow.blob`** (and the identity blob beside it).
**[DESIGN] 2026-10-24 (R-1254; committed felhom.eu `dcf4f84c`, hub next v0.170.0 — NOT deployed):** the escrow PUT
stores the blob, its sealed-password hash and the identity blob in ONE transaction (`Store.SaveHostEscrowWithIdentity`;
the superseded copy is made inside it). A failure leaves all of the old package or all of the new — never the new hash
beside the old identity package, which a box would read as „escrowed" while the written code opened nothing. A
malformed identity blob is refused before anything is stored. (The agent ceremony's „the previous code still works"
after a lost answer, R-1254's other half, stays open.)
Both existing infra-backup tables retire — `infra_backup_versions` (the current/live one, all readers
hit it) **and** `infra_backups` (the deprecated legacy mirror). (The planned slim DR record on `hosts` was never
@@ -778,6 +778,28 @@ than the unit), the app running, the record reads the D22 sentence. Known limit
named volume the unit replaces (none in the catalog today — a drive-file app's unit restore is refused by R-538) is not
covered by the volume copy.
**`[DESIGN]` 2026-10-24 — a pending put-back blocks EVERY start (R-1251; review round 2 HUNT-1 H1, H2, H7; committed in
controller `bb2809d` + `062f9b4`, unreleased — rides the next controller release).** For a drive-backed app the copy lives on
the app's drive, and at start the drive is often not bound yet. A copy that cannot be reached while its drive is not
bound is **waiting, never „gone"**: the crash recovery writes a restore hold (`putback_pending`, the copy's folder
in it) and starts nothing. The hold is the one store every start path already honours — the household's
Start/Restart/Update, the boot recreate, the drive return, the media restart, the boot sweep, the night chain, the
update leg — so no path has its own check. When the drive binds, the put-back runs FIRST (the boot recreate and the
drive-return restart; the start and every drive return sweep the waiting holds), the hold is lifted, then the app
starts. The household reads „…A visszaállítás előtti adatai egy meghajtón vannak, amely most nem érhető el…" on the
app and „Ne indítsd újra a visszaállítást…" in the restore record. A put-back that FAILS (at start or when the drive
returns), or a copy gone while its drive is bound, leaves the app **held** with the D20 hold („félúton megszakadt"),
the copy kept, and the marker lets go of the app — never stopped-and-unheld (H2). Stop pressed during the restore still
gets the put-back; only the start is skipped (H7). A removal clears a waiting put-back; a later restore that completes
drops it (the household chose that data). The copy's folders are synced to disk BEFORE the marker names the copy.
**`[FACT]` 2026-10-24 night, Tester 1 (VM 341; MeTube on the data drive, 1.5 GB in its volume;
`audits/restore-holes-2026-10-24/P1-tester1/`):** a local restore cut by powering the VM off 2.1 s after „the restore
copy is recorded", the data disk taken off, the box booted: held as waiting, Start and Update refused (409, the
sentence); the disk plugged back: the agent re-mounted it, the boot recreate put the copy back BEFORE the start, the
household's newest data („gen2") and its download on the drive were there, and a later controller restart poured
nothing. Run 1 of the same proof found the journal left as `undo.json.tmp` (the rename lived only in the page cache) —
the app was held for support, never started on the cut data; the folder sync above fixed it, run 2 passed.
**[DESIGN] 2026-08-22 — the failure ladder of a database restore: replay → rollback → hold.**
Recorded here rather than only in a closed register row, because a decision that survives only inside
a closed work item is a decision nobody will find.
@@ -888,6 +910,8 @@ run makes it longer.
### 6.5 Kept data — what a removed app leaves on the drive (controller v0.274.0, `09` §3 decision 36)
*Second-drive copies that are KEPT (D25) and the rule that a rejected backup block never deletes one: §8.2a (R-1252, 2026-10-24).*
**What it is.** "Remove the app, keep my data" leaves the app's drive folder (`<drive>/appdata/<app>`, or the folder
its definition binds through `${HDD_PATH}`) in place. A reinstall over it no longer runs silently into the old files
(R-657): the install asks „A megőrzött adataimat használom" / "Use my kept data" (the database from the newest copy of
@@ -1597,6 +1621,19 @@ or pattern. **Why:** the data-loss hunt of 2026-10-20 proved two nights that del
(R-1175: one share group's reconcile removed the other groups' copies, and a dead drive's copy; R-1181: a missing or
emptied folder took its copy within one night). Controller: `internal/backup/tier2_kept.go`.
**`[DESIGN]` 2026-10-24 — a rejected backup block never deletes a copy (R-1252; review round 2 HUNT-2 H1, H2, H9;
committed in controller `518a135`, unreleased).** The night's reconcile (deleting dest dirs the legs no longer name)
runs ONLY under a valid classification. An app whose block is rejected (one bad line rejects the whole block) or absent
falls back to the legacy legs, and nothing is reconciled that night — every existing copy stays. A REJECTED block puts
„Az alkalmazás mentési beállításai most nem olvashatók…" on the app's second-drive status and tells the operator once a
day (the operator-only digest `backup_run_failures`, leg `tier2-settings-unreadable`). A kept copy (D25) is always in
the keep set, also when its leg later leaves the declared set (H2); Remove with „remove backups" drops the kept-copy
records of what it deleted (H9). **`[FACT]` 2026-10-24 night, 9202 (`audits/restore-holes-2026-10-24/P2-9202/`):**
audiobookshelf with a household file in `media/audiobooks`; two control runs kept the copy; one bogus path in the
block → „the backup block is REJECTED … every existing copy on the second drive is KEPT", the copy there, the status
sentence set, the operator digest sent (9202 has no hub: logged as dropped); the review's run on 0.342.0 the same day
deleted that copy (`review-round-2-2026-10-24/proofs/H2-bad-backup-block/`).
### 8.2b The second-drive copy runs in a memory-capped container that never outlives the copy [DESIGN + FACT, R-1214, 2026-10-22/23 nights; rides controller 0.340.0]
The copy (`rsync -a --delete`) runs in a container of the controller's own image, capped at 512 MB, so its page cache
@@ -76,6 +76,10 @@ boxes installed. Nobody keeps a hand-written list.
- **A BYO host** (the owner brought their own Proxmox). `[FACT]` The installer leaves its repositories
alone: *"apt repo alignment skipped (byo — the owner manages repos)"* (`scripts/felhom-host-install.sh`,
`align_apt_repos`). `[PROPOSAL]` On a BYO box we update the guest and Docker only, never the host.
**[DESIGN, operator ruling D43 = A, 2026-10-24 — committed agent `c8d3c17`, unreleased]** The host's root-key-only sshd
drop-in (D4, R-982) is an APPLIANCE-only file of the config bundle: written only when the root-owned install record
says `appliance`; on a `byo` record nothing is written and a copy the bundle wrote earlier (this path, our first line)
is removed; a box whose record proves nothing is left as it is (R-1257).
- **The Proxmox MAJOR upgrade** (PVE 9 → 10). It is a later step of its own, drilled first (R-808 item 5).
- **`cloudflared` and the other infrastructure images** (traefik, filebrowser). **[FACT] (C8)** They are containers in
the guest, pinned in the controller (`internal/infra/infra.go:26`: `cloudflare/cloudflared:2026.6.0`, since