run agent containers on an ephemeral root, salvaging the old one first
hive-priv copies a stopped container's root to the agent's work dir and renames it aside before passing nspawn --ephemeral; the agent's nix state moves to the persistent profiles bind. Adds the agent-ephemeral-rootfs vm test and a module-eval case for the nix state redirect.
This commit is contained in:
parent
22522b386a
commit
81439fa710
1 changed files with 1238 additions and 39 deletions
|
|
@ -341,6 +341,53 @@ Under `/var/lib/hyperhive/agents/<name>/`:
|
|||
in the per-agent disk metric (`measure_agent_disk`) together with
|
||||
`state/` and the container rootfs. The dashboard doesn't serve it.
|
||||
|
||||
### The container root (per agent)
|
||||
|
||||
An agent container starts with `systemd-nspawn --ephemeral`
|
||||
(`hive-priv`'s `WriteNspawnFlags` passes the flag). nspawn runs it on a
|
||||
snapshot of `/var/lib/nixos-containers/h-<name>`, and the snapshot vanishes
|
||||
at stop. The container loses anything the agent writes outside a bind: `/home/<name>`
|
||||
apart from `.claude/`, `/etc`, `/var`, `/root`, `/tmp`. What persists is
|
||||
`state/`, `work/`, `harness/`, `claude/` (at `~/.claude`), the per-container
|
||||
`/nix/var/nix/profiles` and `/nix/var/nix/gcroots` binds, and the sockets dir.
|
||||
`/home/<name>` holds caches and nothing the agent should keep: worktrees,
|
||||
clones and builds go in `work/`.
|
||||
|
||||
The nix profile needs one extra step. nix keeps a non-root user's profile in
|
||||
`~/.local/state/nix`, so the agent's tmpfiles rules
|
||||
(`nix/agent-modules/user.nix`) point that directory into
|
||||
`/nix/var/nix/profiles/per-user/<name>` (the persistent bind) and recreate
|
||||
`~/.nix-profile` at every boot. Packages the agent installs with `nix-env` or
|
||||
`nix profile` survive a restart, and go with the container on `destroy`.
|
||||
|
||||
**The one-time salvage.** The first start of an agent whose root has content
|
||||
runs `hive-priv/src/rootfs.rs` before hive-priv passes `--ephemeral`, from the
|
||||
`WriteNspawnFlags` that precedes every start, while the container is down:
|
||||
|
||||
1. Everything in the root except the cache paths (`tmp`, `var/cache`,
|
||||
`var/log/journal`, `var/lib/weston`, `var/lib/opentelemetry-collector`) is
|
||||
copied to `work/salvaged-rootfs-<YYYYMMDD>/`, owned by the agent, with
|
||||
modes and mtimes kept. The agent finds its old `/home/<name>` there.
|
||||
2. hive-priv copies the agent's nix state in the old home to the persistent profile bind, following no link in the path (the container can write there).
|
||||
3. hive-priv renames the old root to `h-<name>.pre-ephemeral-<YYYYMMDD>` and
|
||||
logs the path. Nothing deletes it; the operator removes it when ready.
|
||||
4. A fresh template root takes its place, carrying `etc/machine-id` (the old
|
||||
id, so the journal directory and DHCP identity hold) and the marker
|
||||
`etc/hyperhive-ephemeral-root`.
|
||||
|
||||
The salvage runs again only if the template holds content outside the cache
|
||||
paths, which happens when a hive without this change started the container
|
||||
persistently in between; the new copy gets a numbered directory beside the
|
||||
first. A failed salvage leaves the root where it was and the container starts
|
||||
without the flag (hive-priv logs the error), so a failure costs the switch,
|
||||
never data.
|
||||
|
||||
The journal still ships. The agent's collector (`nix/agent-modules/otel.nix`)
|
||||
reads the guest's own `/var/log/journal` and pushes it over OTLP, so no
|
||||
journal link to the host takes part; nspawn skips `--link-journal=try-guest`
|
||||
in ephemeral mode without failing. The journal vanishes at stop, and with it
|
||||
the lines written after the last push.
|
||||
|
||||
### Cross-agent access to state
|
||||
|
||||
An agent holding the `manage_root_agent` permission gets every other
|
||||
|
|
@ -562,7 +609,9 @@ container lifetime:
|
|||
`${homeDir}/.claude`.** Marker
|
||||
(`/var/lib/hive-agent-user-migrated`) guards single-shot; it's
|
||||
only written once there's nothing left to migrate, so a `cp`
|
||||
failure leaves it absent and the next boot retries.
|
||||
failure leaves it absent and the next boot retries. The root is
|
||||
ephemeral, so the marker vanishes at every stop and the step
|
||||
re-runs; with no `/root/.claude` it only writes the marker again.
|
||||
`cp -an` (no-clobber) so any pre-existing files at the new
|
||||
location win — never blow over data already there, and so a
|
||||
retry after a partial copy is as safe as the first attempt.
|
||||
|
|
|
|||
Loading…
Reference in a new issue