feat(host-install)+docs: D1 — install self-update artifacts on day-0 + architecture §11 self-update (implemented)
felhom-host-install.sh installs felhom-selfupdate-guarded (sh -n), the rollback unit + the [Unit] start-limit drop-in (daemon-reload) so day-0 boxes get operator-signed self-update from birth; non-fatal on pre-D1 agent repos. 03-host-agent.md §11 updated to the shipped mechanism (signed op, A/B wrapper, OnFailure rollback, tuned start-limit backstop). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -446,13 +446,23 @@ buildable until then; recorded here so the front-half built in slice 7 lands rea
|
||||
|
||||
## 11. Self-update
|
||||
|
||||
- **Agent (the hard case — a host service, no snapshot-rollback).** **A/B layout:** download →
|
||||
verify signature → stage as the inactive slot → flip a `current → good|new` symlink → restart.
|
||||
**Revert authority lives outside the swapped binary** — `Restart=always` alone just
|
||||
crash-loops a bad binary — so a **separate health-gate** (a systemd oneshot `ExecStartPost`
|
||||
probe, or a tiny supervisor unit) flips `current` back to last-good and restarts on a failed
|
||||
health window. The new version is **committed as "good" only after a clean health window**.
|
||||
Triggered by a hub signed job within the update window; manual always allowed. Journaled (§10).
|
||||
- **Agent (IMPLEMENTED — v0.70.0, TASK D1; provenance `audits/SPIKE-agent-selfupdate-2026-07-05.md`).**
|
||||
The hard case (a host service, no snapshot-rollback). Delivered as an **operator-signed
|
||||
`agent_update` op** through the signed-jobs pipeline (same LOCKED gate as every destructive op) —
|
||||
the signed params pin **version + sha256**, so the pinned sha is the ONLY integrity root: a
|
||||
compromised hub (transport) or Gitea (storage) can never substitute a binary. **A/B flip** by a
|
||||
root-owned guarded wrapper (`felhom-selfupdate-guarded`): the agent downloads + verifies the sha,
|
||||
the wrapper re-verifies as root → `.prev` snapshot → atomic same-fs `mv` → `pending.json` marker →
|
||||
detached restart (`systemd-run` transient timer, outside the agent cgroup). The new binary
|
||||
**commits** (clears the marker) only after a clean dwell + core init. **Revert authority lives
|
||||
outside the swapped binary:** systemd's `OnFailure=felhom-agent-rollback.service` runs the wrapper's
|
||||
pending-guarded `rollback` — on systemd 257 OnFailure fires on the FIRST crash, so a bad binary is
|
||||
reverted within seconds; the tuned `[Unit]` start-limit (`StartLimitIntervalSec=120`,
|
||||
`StartLimitBurst=4`) is the terminal backstop that also lets the hub's `host_staleness` dead-man's
|
||||
switch alert on an environmental crash loop. The wrapper + systemd units are what change almost
|
||||
never — the binary is what flips. Report field `selfupdate_pending` surfaces a runs-but-never-commits
|
||||
binary. v1 scope-outs: no hub-floor auto-update, no failed-update auto-retry (the operator re-signs),
|
||||
no pending-timeout auto-rollback.
|
||||
- **Controller (the easy case — it's a guest).** The agent owns the controller's lifecycle,
|
||||
so the **agent updates the controller**: snapshot-before-update (free rollback, because the
|
||||
controller *is* a snapshottable guest) → pull new image → redeploy → health-check → rollback
|
||||
|
||||
Reference in New Issue
Block a user