## Unreleased (2026-11-14 night) — release 3 („the hub tells you”) and the clock-step fix, the agent's parts, built and NOT released Commit only. Each part can be released alone; none needs release 2b. No `configs/` change in this section. - **Fixes after an independent review of the night's code (2026-11-14; felhom.eu `documentation/audits/night-build-2026-11-14/review/`).** (1) `config.FileMu`: the three functions that read `agent.json` whole and write it whole (the trust move, the PBS seed, the backup-target move) take one lock — two of them could interleave and the second put back the document it read before the first one's change; pinned from the source by `TestAgentJSONWritersTakeTheSharedLock`. (2) `pvetrust.Manager.Ensure` runs one at a time (start, timer, a failed collect and a bundle install could overlap, and the second parked the already-moved file as the „before” copy), and its root step has a 90-second deadline (it runs on the start path and inside the report loop). (3) **The CA copy keeps its previous certificate behind the new one** (`configs/felhom-os-apply`, mode `pve-ca-copy`): the copy was replaced before anything could be proven with the new root, and Proxmox serves its old certificate until pveproxy is reloaded — an agent that restarted in that window had no root that verified it. **Only for the changeover:** at every later run the step makes one handshake to the local Proxmox with the NEW root alone, and drops the previous root as soon as that verifies, or when the previous root is no longer a CA that has not ended. The copy is compared exactly, so nothing the step did not check stays in it. To drop the old root at once by hand (a root changed because it leaked): delete the copy. `configs/` changes again, so the release needs both signed jobs. (4) The profile-refresh stanza is built from the wrapper's ANSWER (its record of the last refresh comes back at every ask); the record file is a bonus — its folder is root-only on a box where a signed job made it first (read in source, not on a box). `rewritten` is absent when not known. (5) A watchdog give-up is forgotten when the guest is no longer the watchdog's (onboot off, locked, destroyed) — it was reported for ever. (6) `/disks` sends both boot tokens from ONE read of the guest's init (`GuestBootTokens`). 13 mutations seen red, two after a mutant that did not compile was done again. - **The hub is told what the kernel clean-up did and what the profile refresh did (R-1383; felhom.eu `11` §5.11.1, §5.12).** (1) The clean-up's report carries `kernel_cleanup` (`osupdate.CleanupDetail`: removed, kept, bytes_gained, leftovers). It still has no `kernel` view and `release_id` stays `kernel-cleanup` — the hub reads only those two as „the kernel this step was for” (the test asserts the wire form). (2) The host report carries `profile_refresh` (`osupdate.ProfileRefreshStatus`, from `Leg.ProfileRefreshStanza`, wired in `main.go`): the root wrapper's own record of the last refresh — so it outlives an agent restart — and how this process's last ask ended. One small file read, no wrapper call, names checked and capped. No OS report is sent for the refresh (an unknown layer reads as a guest step on the hub). Tests `TestKernelCleanup_NightOrderOnceAndNoNames` (extended), `TestProfileRefreshStanza_RecordAndLastAsk`, `TestMainWiresPVETrust`; 9 mutations seen red (one mutant did not compile at first and was done again). - **A boot token that a clock step cannot move (R-1395).** `/disks` answers `guest_boot_key` = `-` beside the unchanged `guest_boot_id` (`-`). The kernel computes the boot time from the wall clock, so a step of the host's clock moved the old token and the controller recreated every drive-backed app (the N100, 2026-11-14, +32.7 s). `GuestBinder.GuestBootKey`; `""` when the guest does not run or the boot_id is unreadable. Only a controller that knows the key (built there, not released) reads it. Tests `TestGuestBootKey_AClockStepDoesNotMoveIt`, `TestDisks_CarriesBothBootTokens`; 6 mutations seen red (felhom.eu `documentation/audits/night-build-2026-11-14/C/redproof-agent-boot-key.txt`; two mutants did not compile at first and were done again). - **A refused signed job and a watchdog give-up reach the hub (R-1383, D78, felhom.eu `09` §3 decision 299; `05` §16.6).** (1) `signedjobs.Runner` clears a job with `hub.Client.CompleteJobWithOutcome`: `outcome` (done · rejected · failed · malformed), a `reason` from the closed `signedjobs.ReasonCodes` (the two clock reasons `expired` and `not_yet_valid` first — a box with a wrong clock refuses every job) and the op's name, in the `DELETE`'s query. No signature, nonce or job parameter is sent. (2) The guest-power watchdog's give-up — one ERROR line in the journal until now — rides the host report as `guest_power_gave_up` (`localapi.Server.GuestPowerGaveUpStanza`, wired in `main.go`); the list empties when the guest runs. Tests `TestRunner_TellsTheHubHowEachJobEnded` (the real gate and verifier), `TestReasonCode_ClosedList`, `TestCompleteJob_OutcomeRidesTheQuery`, `TestGuestPower_GiveUpIsReportedUntilTheGuestRuns`, `TestMainWiresPVETrust`; 12 mutations seen red (felhom.eu `documentation/audits/night-build-2026-11-14/B/redproof-agent-{job-outcome,guest-power}.txt`). - **The report carries the box's clock at the moment of sending (R-1384, D79, felhom.eu `09` §3 decision 300).** `HostReport.SentAt` (`sent_at`, RFC3339 with nanoseconds) is stamped by `Client.Report` at every send, never by the collector. `reported_at` could not serve: it is stamped when the collect starts. The hub (its own unreleased part) compares it with its clock and alarms above one minute. Test `TestReport_StampsSentAtAtSending`; 3 mutations seen red (felhom.eu `documentation/audits/night-build-2026-11-14/B/redproof-agent-sent-at.txt`). ## Unreleased (2026-11-14 night) — release 2b, built and NOT released: trust in Proxmox by a copy of the box's own root CA (R-1380, D80, felhom.eu `09` §3 decision 303; design `03-host-agent.md` §12a) Commit only. No box ran any of this; the proof on a box (demo-hp, a forced new certificate, twice) is the next attended session's. `configs/` changes, so the release needs BOTH signed jobs. - **The root step that keeps the copy** (`configs/felhom-os-apply`, host-layer mode `pve-ca-copy`). It copies exactly `/etc/pve/pve-root-ca.pem` to exactly `/etc/felhom-agent/pve-root-ca.pem` (0644 root), only if the source is ONE certificate with `CA:TRUE` that has not ended; written whole or not at all; „same" when nothing changed. The plan may name nothing (T1); no source (T2); not one CA certificate (T3). No appliance check: the copy is the agent's own trust file and changes nothing of a BYO owner's host. Tests `PVECACopy` (9, one with the real openssl through the real runner); 7 mutations seen red (felhom.eu `audits/night-build-2026-11-14/A/redproof-wrapper.txt`). - **The move of a box that exists, in a safe order** (`internal/pvetrust`, new; wired in `cmd/felhom-agent/main.go` at the agent's start, after a config bundle install, on a timer — hourly until moved, daily after — and on every call to Proxmox that fails on trust). (1) the root step makes the copy; (2) ONE real call to Proxmox with the copy ALONE, while the pinned fingerprint is still the configured trust; (3) only then ONE write of `agent.json` sets `proxmox.tls.ca_file` and clears `fingerprint` (the agent's own file, rewritten in place after a recovery copy `agent.json.pre-pve-trust` was parked, read back and checked); (4) the RUNNING client takes the new trust (`proxmox.Client.SetTLS`, new) — no restart. A failed copy, proof or write changes nothing: the fingerprint stays. **`proxmox.tls` keeps its order (a set fingerprint wins over a CA file), so the fingerprint is cleared LAST.** An operator's own CA file, a box with checks switched off and a config that is not a file are never moved. - **A pinned box whose certificate changed heals by itself:** the failed call starts the same order, the box moves to the CA copy and the next call works. A refreshed copy (a renewed root CA) is proven and reloaded by the running client. - **The cause is named** (`proxmox.TrustProblem`, new): the pinned certificate changed; the CA copy did not sign the served certificate; a certificate ended; the copy is unreadable; the copy ends within 30 days; the root step gave no copy; the proof failed. An ERROR line in the journal, and the new `proxmox_trust` stanza of the host report (`mode`, `problem`, `detail`, `ca_not_after`, `ca_days_left`, `moved_at`). **The hub does not read the stanza yet** (felhom.eu R-1396), and a box that is fully blind sends no host report at all — that case still shows only as „host stale" on the hub. - Tests: `internal/pvetrust` (11, against a real TLS server that changes its certificate and renews its CA), the AST wiring test `TestMainWiresPVETrust`; 12 + 3 mutations seen red (felhom.eu `audits/night-build-2026-11-14/A/redproof-agent.txt`; one mutation is not a rule and says so). ## v0.174.0 (2026-11-14) — the box removes its old kernels (R-1368); security profiles are refreshed after a kernel change (R-1388, R-1301); Go 1.27.4 (R-1391) — RELEASED 2026-11-14 (release-agent.sh from the clean working clone at `cc63dd5`, CI run id 2641; agent sha256 6efbb97809135483bc446d71d1321f9e2f7fc3268fe9b20e7fe742a2eb13db92, bundle sha256 7d87d0584dee79eee766f01c27b9e89273d39c89a14b0725a6e5564a55b3ec5f) and VOUCHED 2026-11-14 15:21:45 CET Release 2a of the build order (felhom.eu `09` §3 decision 306). Design: felhom.eu `11-os-updates.md` §5.11.1 and §5.12. `configs/` changed (the wrapper), so a box needs BOTH signed jobs: `agent_update`, then `agent_config_update`. **Part A — old kernels (`09` §3 decision 286).** Nothing removed old kernels; a ring-0 box got about one a week, 1 GB each. Now: - **The wrapper** (`configs/felhom-os-apply`, layer `kernel`, new mode `kernel-cleanup`): the hand-proven rule of 2026-11-10 as a program. Keep the running kernel, the step's `from`, `to` and `default_before`, both defaults, the flag's kernel, an operator's boot pin and the newest installed; remove every other installed kernel image in ONE `apt-get purge` by exact name. Refusals P1, P2, P3, K1, N1, S1, S2 (the design's table), C1 (the plan names something — it may carry only `dry_run`), R9, R12. „Not run" (exit 0): no root-owned ring-0 mark, no kernel step yet, the newest step did not end `good`, the box does not run its newest kernel, a step within the last 20 hours. K1 reads the box a second time, by itself. Repair-first. After the purge it proves the default, the pinned file, the two generators and the state file unchanged; a left-over folder is reported, never deleted. **Rule R4 and every update plan are unchanged; `apt-get autoremove` is not used.** - **The agent** (`internal/osupdate/kernel.go`): the clean-up is the night leg's last call, after a healthy host step, never in a night with a kernel step, never in a debug pass, never on a BYO host. The hub hears `cleaned`, `refused` or `failed` (a kernel-layer report with NO kernel version — the hub would read that as a kernel step); „nothing to remove" and „not run" are log lines. `--selftest=kernel-cleanup-dry-run` prints what it would do and changes nothing. - **No new sudo line.** The wrapper is reached by the one `--plan` line (pinned by `test_cleanup_needs_no_new_sudo_line`). **Part B — security profiles (spike first: felhom.eu `audits/profile-refresh-spike-2026-11-14/`).** After a kernel went back and forth a box can load an old compiled AppArmor profile; on the N100 this stopped the time service. - **The wrapper**, new host-layer mode `profile-refresh` (an appliance only; no package change; the plan names nothing, F1): once per kernel the box starts on (a root-owned record), `apparmor_parser --replace --skip-read-cache --write-cache -- /etc/apparmor.d`; then ONE restart for a `failed` service whose own program's cached profile the rewrite changed (at most 3). Measured on demo-hp: 0.5 s, no guest or container touched. - **The agent** (`internal/osupdate/profilerefresh.go`) asks at every start and after a config bundle install, under the heavy-op gate; nothing is sent to the hub. **Part C — Go 1.27.4** (GO-2026-6859; hygiene: the agent's one `MaxBytesReader` place does not match the advisory). **Tests, each seen red** (felhom.eu `audits/release-2a-2026-11-14/A/redproof*.txt`, `B/redproof*.txt`): 31 wrapper cases for the clean-up, one per acceptance scenario and one per kept-kernel kind (14 mutations, all red); 11 for the profile refresh (10 mutations); 5 + 4 agent tests (3 + 3 mutations). Two existing agent tests list the night's calls exactly and gained the clean-up call (`TestRing0_OneCallPerLayer`, `TestRing1_NoReleaseIsInventory`). **Delivered 2026-11-14 to the three test boxes by signed `agent_update` + `agent_config_update` each** (Tester 1, demo-hp, the N100): update committed, bundle installed (written 1 of 31), capability probe 77/77, the running binary reads `go1.27.4`. **On boxes** (felhom.eu `audits/release-2a-2026-11-14/D/`): - **Profile refresh, by the product's own path, on all three:** refused by the OLD wrapper at the agent's start (R11, logged), then run right after the bundle install: 0.5 s each, nothing restarted on Tester 1 and demo-hp. **On the N100 it repaired the time service:** the cached chrony profile was rewritten, `chrony.service` (failed since 2026-10-16) was restarted and is active, the clock stepped +32.7 s; offset to DooPlex −32.55 s → +0.18 s. After a restart of Tester 1 the agent answered „not needed" (the same kernel). - **Clean-up:** dry runs through the released agent on all three (demo-hp: nothing to remove; the N100: 6 of 8 will go tonight; Tester 1: „not run", it has no ring-0 mark). Refusals P1, P2 and P3 by dry run on demo-hp against states set and put back. **A real removal on Tester 1**, with the ring-0 mark set for the test on the operator's yes and put back: `proxmox-kernel-7.0.14-20-pve-signed` removed, 1.12 GB gained, 8.5 s, the default, the pinned file, the generators and the state file unchanged, no left-over; the second call removed nothing; one orderly restart came back on the same kernel, healthy, a clean stop for the crash guard. **This real run was started by hand** (the agent user, the one sudo line, the night leg's own plan), not by the night leg. - **Not proven:** the step by the agent at night (the N100's and demo-hp's own night, 2026-11-15); a box whose running kernel is not its newest and a self-revert into the kept kernel (tests only); refusal K1 on a box (it guards a wrong computation; tests only); a restart by the profile refresh of any program but chrony. - **Found:** a clock step makes the CONTROLLER read a new boot (its boot identity is `/proc/stat` btime): on the N100 it recreated its three drive-backed apps 9 s after the step (felhom.eu R-1395). The crash guard (kernel `boot_id`) was not fooled. **Vouched by the operator 2026-11-14 15:21:45 CET** (the hub log: `Artifact manifest set: agent=0.174.0`, the bundle sha = the release; the hub's System page: „vouched agent: 0.174.0"). `pinned.day0_agent` = 0.174.0 (D50). ## Unreleased (2026-11-11) — docs only: a repository address holds no credential (R-925, Part C of the clock/end-dates run) - `scripts/release-agent.sh` header: the tag push needs `GITEA_GIT_TOKEN` from the secret store (`sec run`). The origin address of CC's clone on DooPlex held a login and a token until 2026-11-11; git now asks the helper `felhom.eu/scripts/git-credential-sec.sh`. No code change, no version change, no release. ## v0.173.0 (2026-11-10) — R-1368 part 2: the self-update folder keeps what it needs and no more — RELEASED and VOUCHED 2026-11-10 (release-agent.sh from a clean clone of `776025c`; agent sha256 752b2727fb6043417ae5f342acbda52330521b21bb799fda90760b960feef7ea, bundle sha256 51ebc1f4c66d87918c87c035b4297c0e40e10ed31e3250c3837dfeaa0002e1c1) - **The defect (measured 2026-11-10, space list place H12).** `Execute` downloads each new agent to `/selfupdate/felhom-agent-` and removed that file only when the download or its checksum failed. A good update left it for ever: 49 / 48 / 33 binaries = 740 / 724 / 509 MB on demo-hp, demo-felhom and Tester 1. - **What the folder is needed for (read first: `configs/felhom-selfupdate-guarded`).** The root wrapper reads the staged file once, copies it to its own root-owned folder and flips from that copy. The rollback needs `pending.json` (in the folder) and `/usr/local/bin/felhom-agent.prev` (outside it); the commit needs `pending.json`. No staged binary. - **The fix (`internal/selfupdate/prune.go`).** After the wrapper's `commit` returned success, `MaybeCommit` removes every staged binary but the running version's. Nothing is removed while a pending marker exists or cannot be read, at a plain start, after a rollback (the failed version's binary stays to be read), or when the running version is not a version this code can name a file for. Only a regular file with exactly the name `Execute` writes is removed, one `os.Remove` per name; a failed removal is logged and never fails the commit. A download and a prune take turns (`stagingMu`). - **Tests `TestPrune_*` (6).** After six updates the folder holds one binary and every bystander (unknown names, a folder and a symlink that carry a staged name); a pending or unreadable marker refuses; only a healthy commit prunes; the name rule follows `versionRe`; the wrapper's `commit` and `rollback` blocks name no staged binary; and the wrapper's REAL `commit` and `rollback` verbs, run from a sandboxed copy, still put the previous binary back after four pruned updates. **Red seen three times:** no prune call; no pending refusal; no regular-file check. - **Not reached.** `/usr/local/bin/felhom-agent.bak-*` (place H13): root-owned, in a folder the agent cannot write, made by a person. R-1368 part 4 stays open. - **Corrected 2026-11-10 (measured on three boxes).** This entry said: „A box gets the clean-up at its first good update AFTER the one that brings this code". That was wrong. The commit runs in the NEW binary, so the box cleans the folder at the commit of the update that brings the code. - **Delivered 2026-11-10 to the three test boxes by a signed `agent_update` each (no bundle job: `configs/` did not change since v0.172.1).** The folder before → after, read on each box: demo-hp 49 entries / 722,596 KB → 1 / 16,028 KB (commit 14:59:19 UTC, `removed=49`); demo-felhom 48 / 706,968 KB → 1 / 16,028 KB (commit 14:51:50 UTC, `removed=48`); Tester 1 33 / 497,244 KB → 1 / 16,028 KB (commit 15:00:19 UTC, `removed=33`). After each: `pending.json` absent, `/usr/local/bin/felhom-agent.prev` = 0.172.1, the unit active with no restart. - **A rollback still works after the clean-up (Tester 1, 2026-11-10 15:14 UTC).** A signed `agent_update` to 0.172.1 (the vouched version; it has no prune code) was installed with its pending marker. One `SEGV` in the 60-second dwell → the unit's `OnFailure` → the wrapper's `rollback` → 0.173.0 ran again 9 seconds after the install, `pending.json` gone, no failed unit. The failed version's staged file stays (by design, until the next good update): the folder holds 2 files, 32,040 KB. Evidence: felhom.eu `documentation/audits/release-space-fixes-2026-11-10/agent/`. - **Vouched by the operator 2026-11-10 16:29:16 CET** (read from the hub log: `Artifact manifest set: agent=0.173.0`, the bundle sha = the release). `pinned.day0_agent` = 0.173.0 (D50). ## v0.172.1 (2026-11-07) — proxmox-grub joins the boot-chain lane: the nightly pve step stops being refused R6 — RELEASED and VOUCHED 2026-11-07 (release-agent.sh from `39db030`; agent sha256 505a0594a2fb117f359e329a0b7ae2c7a6902b74c51f52c1883145f3c2ac72f8, bundle 3e7acaee4500893bfaf567b1d427790ef89bf41f08cb6a1419ee528aeb40ddc3) - **The defect (measured on demo-felhom, 2026-11-05, 11-06, 11-07).** Proxmox published `proxmox-grub` 2.12-9+pmx4. It depends on `grub-efi-amd64` of the same version. `HOST_SLOW_RE` matches `grub` only at the START of a name, so `proxmox-grub` landed in the ring-0 pve plan; the simulation then pulled `grub-efi-amd64`, which is not in the plan, and `felhom-os-apply` refused the WHOLE pve step (R6 „the plan would touch grub-efi-amd64") every night — 34 Proxmox userspace upgrades held with it, and an `os_update_failed` error mail each morning. The same class as the 2026-10-09 `proxmox-secure-boot-support` fix. Read-only proof on the box: `apt-get -s install --only-upgrade proxmox-grub` → `Inst grub-efi-amd64 …pmx4`. - **The fix.** `proxmox-grub` joins `HOST_SLOW_RE`: GRUB's meta-package now waits with the rest of the boot chain (the slow lane, the operator's signed step), and the pve step runs without it. - **Test** `test_ring0_pending_pve_leaves_proxmox_grub_out`; red seen before the fix (proxmox-grub entered the plan), green after; the wrapper suite 149 of 149. - **Delivered 2026-11-07 (operator, signed jobs):** demo-felhom, demo-hp, Tester 1 — `agent_update` committed (23:05/23:13/23:13 CET), then the bundle: `BUNDLE DONE agent=0.172.1 written=1 same=30 self-check=ok`, capability probe 77/77 on each. `pinned.day0_agent` = 0.172.1 (D50). - **Delivery.** `configs/` changed, so a box needs BOTH signed jobs: `agent_update` 0.172.1, then `agent_config_update` 0.172.1 with the bundle sha. Not vouched by CC. Bundle vs 0.170.0 (what the boxes run): no path added or removed, only `felhom-os-apply` changed — so no step bundle is needed (R-880). Both downloads re-checked by sha. ## v0.172.0 (2026-11-03) — R-1302: the first grant of a tier is no loss — RELEASED and VOUCHED 10:18:10 CET (release-agent.sh from a clean clone of `ecbb0cc`; agent sha256 2c0222cd01713f65c627d6d17c18b4a8b2b1e0c39881db0df7ccb17de48e24c7, config bundle cc34b0bc3346ffd6a4ba80fe9269852dced8c56c9503c8b14a50d9ded2961cce; configs/ unchanged since 0.171.0, so the signed delivery was agent_update alone) - **R-1302 — the first grant of a tier is no loss.** A fresh install arms the whole-guest tier before its storage exists, so the first probe found no grant, repaired it and logged ERROR „GRANT WAS MISSING … investigate the loss (R-190)" plus a DEGRADED capability, on every new box (VM 9375, 2026-10-26). The agent now remembers which tiers have EVER held their grant (`/store-grant-seen.json`, written by a healthy probe). A first grant logs INFO „granted" and reports healthy; a grant that existed and vanished keeps R-190's ERROR and its one degraded cycle. A doubt keeps the ERROR: an unreadable or corrupt file, or no file path. A box updated to this agent records its tiers at the first healthy probe, so a loss in the one probe before that would read as a first grant. Tests `TestR1302_*`; red seen with the first-grant branch removed and on three more mutants. ## v0.171.0 (2026-10-30) — R-1344 decided from the night's measurement: a controller that runs but does not answer is RESTARTED — RELEASED (release-agent.sh from a clean clone of `bd18347`; agent sha256 c888ca7ba22a1f689059081575814a725e2edcecbb6bcc56829e9d9de88202a5, config bundle 95f977f6dc827185f2b5a2aa9e1c829db41a9f425d49eb40f317286d13c83d2b; binary-only signed agent_update on Tester 1, demo-hp, demo-felhom; vouched by the operator 2026-10-30 10:47:36 CET; `pinned.day0_agent` = 0.171.0). Live on Tester 1: the controller frozen (SIGSTOP) 09:24:50 UTC → Docker „unhealthy" 09:26:36 → the alarm 09:29:21 → the restart 09:31:22 → healthy 09:31:24; the hub mailed `controller_unresponsive` and `controller_unresponsive_restart` (hub 0.178.0), not `controller_restarted_by_agent` - **The measurement (operator brief 2026-10-29 Part 2, read 2026-10-30).** Docker's own health record of the controller on Tester 1 and demo-hp, every probe from 2026-10-29 11:12 to 2026-10-30 07:00 UTC: 4 733 probes through the night chain (demo-hp's PBS vzdump, Tester 1's local vzdump, both DB dumps, tier-2, the off-site push, a restore test, two controller updates). No failed probe, no 5-s timeout; slowest 303 ms (demo-hp, during the PBS vzdump). The only „unhealthy" was the controller stopped by hand for a proof. A busy night never came near ONE failed probe. - **So the alarm becomes a restart.** At `controllerUnresponsiveConfirm` = 6 unhealthy sweeps the alarm is raised, as before. At `controllerUnresponsiveRestart` = 10 (2 minutes later; ~6.5 minutes after the first failed probe) the agent restarts the controller's bootstrap unit — unless the controller's busy file is fresh (a backup, a restore, an update, an export) or cannot be read, or the brake refuses (3 restarts in 15 minutes → the crash-loop pause, which the hub mails). A swap, a parked guest, a locked guest and a vzdump keep resetting the count, as before. A refusal keeps the alarm and is logged once per reason. After a restart the count starts again from zero. - **The stanza** carries `unresponsive_restart_after_s` (120: the hub's alarm text says when the restart comes) and `unresponsive_restart_at` (equals `last_restart_at` for that act; the hub mails `controller_unresponsive_restart`). - **Refactor, no behaviour change:** the not-running restart's brake and its record are now `supervisorBrakeAllows` and `recordSupervisorRestart`, shared by both restarts. The busy-file stale log line no longer says „operator reboot". - Tests `TestSupervisor_UnresponsiveIsAlarmedThenRestarted`, `…Guards` (now also: never restarted), `…RestartRefusals` (busy fresh, busy unreadable, the brake, a crash-loop pause), `…StaleBusyFileDoesNotShield` — red first (build failure, then 3 behaviour failures); 7 mutants, each killed (one survivor — the count reset after a restart — found and pinned). ## v0.170.0 (2026-10-29) — „Reboot guest" from the hub (D65 = A): `reboot_guest` joins the agent's closed list — RELEASED (release-agent.sh from a clean clone of `ca15a31`; agent sha256 da8d12a05ef607c338b9ba307e7a81b18253a3188721bbd8421ba36658a22404, config bundle 03f2ff6442fc3ca600b1f25f26767ce4da466a770a258f3e362cf95f6e76b543; signed agent_config_update + agent_update on Tester 1, demo-hp, demo-felhom; vouched by the operator 2026-10-29 18:57:48 CET; `pinned.day0_agent` = 0.170.0) - **D65 = A (operator ruling 2026-10-29 15:50; felhom.eu `04` §6.1, `03`).** The agent's closed operator-action list is now `restart_controller` and `reboot_guest` (arg: the vmid). No signature: a reboot destroys nothing. The act is the existing guest-reboot path (`pct reboot ` of THAT guest over the existing exact grant) — never the host. - **Guards** (each refuses with the reason; nothing is called, no record is written): a guest this agent did not provision, not running, locked, or parked; the host-wide heavy-operation gate held (a backup of any tier the agent runs, a restore test, an OS step — the reboot HOLDS the gate until the guest is up again); `jobBlocker` (an agent backup job, a controller swap, a drive or NAS job, an escrow ceremony, a vzdump of the guest); the CONTROLLER busy — its `data/ctl/busy` file (a backup, a restore, an off-site run, an app update), fresh within 90 s; a stale file is a controller that stopped writing, not a busy one; any read failure but „no such file" refuses (fail closed); at most ONE reboot per hour per guest (`//operator-reboot.json`, on disk — it survives an agent restart); the operator restart's brake (a refusal in force refuses a reboot; a reboot counts as one of the 3 restarts in 15 minutes). - **During the reboot** the guest-power watchdog and the controller supervisor leave the guest alone (`rebootInFlight`): a racing start would fail the reboot's own start half. - **The result** says both times from the press: „rebooted; the guest was up after 41 s, the controller answered after 63 s" — or `failed`, said plainly. The record is written BEFORE the reboot; the new self-scoped `GET /guest/operator-reboot` gives it to the controller for one day, for the household's line „Felhom support restarted your box at