diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba902b..c4c5551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## v0.145.0 — the OS update repairs itself after a power cut; a short-session box gets restore-tested; "sent late" (R-876, R-874, R-875) (2026-10-05) + +Released by `scripts/release-agent.sh`: binary sha256 `894da35c7b9e1ac78885690b78352b634e6831e7d99b321573c75d878db8886e`, +config bundle sha256 `78c00adce662d2d966b2ac50ebde46cde1ae225f0107c6a7c02b70ec8ce80c4f`. The wrapper changed: a box +needs the signed `agent_update` AND the signed `agent_config_update`. + +- **R-876.** After a crash during an install, `dpkg --audit` can read clean while dpkg's update journal + (`/var/lib/dpkg/updates/`) is not — and apt refuses every install until `dpkg --configure -a` (measured on demo-hp + 2026-10-05: every later pass failed until a person typed it). The wrapper now reads `--audit` and the journal in ONE + `sh -c` call (`DPKG_STATE_SCRIPT`) — a clean pass still costs one call (R-845's speed, pinned) — and repairs when + either shows something; as a belt, when apt itself says "dpkg was interrupted", it repairs and retries the install + ONCE. `REPAIR` now logs `journal=N`; a journal still not empty after the repair refuses (R13). Tests + `CrashLeftTheJournal` (the measured shape, the speed, the belt); 3 red-proofs. +- **R-874.** The restore-test's first due-check runs 30 minutes after the agent starts (`DefaultFirstEval`), then every + interval; a box whose power-on sessions are shorter than the 6 h interval never evaluated. A crash-looping agent + restarting faster than 30 minutes still never evaluates (the earned restraint, pinned). +- **R-875.** A kept report's reason is neutral — "sent late — kept on the box until the hub could take it" — the copy + cannot tell a killed agent from an absent hub. +- Red-proofs: `felhom.eu/documentation/audits/catchup-2026-10-05/part{C,D}/`. + ## v0.144.1 — a killed pass really keeps its report: the wrapper survives a dead reader; the agent looks again every 5 minutes (R-868, measured live) (2026-10-05) Released by `scripts/release-agent.sh`: binary sha256 `6ccd521d47e64999017e8eb5bc613d724543cfdc5ef9b13bcae9e3ea8c53b8f3`, diff --git a/REUSE.md b/REUSE.md index dc2bb41..b5dedf8 100644 --- a/REUSE.md +++ b/REUSE.md @@ -19,6 +19,7 @@ | `osupdate.ConfigUpdateExecutor` | internal/osupdate/bundle.go | signed op `agent_config_update` {agent_version, bundle_sha256} | delivering the bundle to an installed box | a courier only: the root wrapper re-verifies signature, host, nonce and sha itself | | `osupdate.Leg.SendUnsent` / `lockPass` (v0.144.0, R-868) | internal/osupdate/unsent.go | `(ctx) int` | an OS-pass report the agent never sent (killed mid-pass): the wrapper keeps `report---apply.json` beside the plan; the agent deletes it once the hub has it | any new caller that runs an apply pass must hold `lockPass` (flock, across processes) — the sender must never take a running pass's copy | | `osupdate.LoadSavedBlock` (v0.144.0, R-866) | internal/osupdate/leg.go | `(planDir) (block, savedAt, ok)` | the hub's newest os_update block as the daemon last received it (`os-update-block.json`) | the debug pass uses it ONLY when the hub cannot be reached, and says so in its header; no saved block → no pass | +| `dpkg_state()` / `DPKG_STATE_SCRIPT` (v0.145.0, R-876) | configs/felhom-os-apply | `audit, journal = self.dpkg_state()` | dpkg's state in ONE call: `--audit` AND the update journal | never gate a repair on `--audit` alone — a crash leaves only the journal (measured); keep it one call (R-845) | | `guesthook.InstallSnippet` / `Register` | internal/guesthook/install.go | `InstallSnippet(ctx, runner) error` | pre-start self-heal hook install (C1 net) | Same random-temp+install pattern; snippet delegates to the agent binary (no shell logic). Issues `mkdir -p /var/lib/vz/snippets` FIRST (v0.63.0, B2 — fresh boxes lack the dir; sudoers grants exactly that argv) | ### Disk / format safety (role gates, durable IDs, format guards)