fix(#702): spare agent creds from migration chown; give nix a writable HOME
Two privsep follow-ups, both in the hive-c0re service:
1. The migration 'chown -R hive-core /var/lib/hyperhive' (which only
started running once ExecStartPre stopped failing 203/EXEC) stomped every
agent's bind-mounted creds — agents/<name>/{claude,state,harness,config}
are owned by the per-agent/manager users — logging all agents out with no
way back in. Scope it to everything *except* agents/, plus the agents/
dir node itself so c0re can still create new per-agent subdirs. Each
container's hive-agent-user-migrate activation chowns the contents back.
2. nix (prebuild 'nix build', flake-check, meta eval in c0re; nixos-container
update->nix in priv) writes its cache under $HOME/.cache. Both services
run as users with no home -> HOME=/var/empty (unwritable) -> Lix cache
init fails, rebuilds error out. Set HOME to each service's StateDirectory
(adding one for hive-priv).
This commit is contained in:
parent
e7b6896e4c
commit
d272803d58
1 changed files with 36 additions and 9 deletions
|
|
@ -346,6 +346,12 @@ in
|
|||
"/run/current-system/sw"
|
||||
];
|
||||
environment = {
|
||||
# nix (the prebuild `nix build`, flake-check, and meta eval) writes
|
||||
# its fetcher/eval cache under $HOME/.cache. As a system user
|
||||
# hive-core has no home, so HOME defaults to the unwritable
|
||||
# /var/empty and Lix fails to initialise its cache. Point HOME at
|
||||
# the writable StateDirectory.
|
||||
HOME = "/var/lib/hyperhive";
|
||||
HYPERHIVE_GIT = "${pkgs.git}/bin/git";
|
||||
# Path to the dashboard static dist. The hive-c0re axum router
|
||||
# serves this via `tower_http::ServeDir` for any path it doesn't
|
||||
|
|
@ -423,15 +429,26 @@ in
|
|||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --nixpkgs-flake ${cfg.nixpkgsFlake} --nixpkgs-unstable-flake ${cfg.nixpkgsUnstableFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}";
|
||||
# One-time migration: chown existing state tree to the service
|
||||
# user after upgrading from a root-run install. The `+` prefix
|
||||
# runs this step as root even though User = hive-core; the `-`
|
||||
# prefix tolerates failure (e.g. an empty tree) without blocking
|
||||
# startup. coreutils ships `chown` but no `sh`, so invoke chown
|
||||
# directly rather than through a shell. systemd's StateDirectory
|
||||
# chowns the top-level dir at every start, but pre-existing files
|
||||
# inside may still be root-owned.
|
||||
ExecStartPre = "+-${pkgs.coreutils}/bin/chown -R hive-core:hive-core /var/lib/hyperhive";
|
||||
# Migrate hive-c0re's *own* state to the service user after an
|
||||
# upgrade from a root-run install (systemd's StateDirectory only
|
||||
# chowns the top-level dir, not pre-existing files inside it). The
|
||||
# `+` prefix runs as root despite User = hive-core; `-` tolerates
|
||||
# failure. coreutils ships `chown` but no `sh`, so invoke the
|
||||
# binaries directly rather than through a shell.
|
||||
#
|
||||
# CRITICAL: exclude the per-agent `agents/` subtree. Its contents
|
||||
# (each agent's `claude/` OAuth creds, `state/`, `harness/`,
|
||||
# `config/`) are owned by the per-agent / manager users, and each
|
||||
# container's `hive-agent-user-migrate` activation script chowns
|
||||
# them back to that user on boot. Blanket-chowning them to hive-core
|
||||
# makes every agent's `~/.claude` unreadable — logging them all out
|
||||
# with no way to log back in. So chown everything *except* agents/,
|
||||
# plus the `agents/` dir node itself (not its contents) so c0re can
|
||||
# still create new per-agent subdirs.
|
||||
ExecStartPre = [
|
||||
"+-${pkgs.findutils}/bin/find /var/lib/hyperhive -mindepth 1 -maxdepth 1 -not -name agents -exec ${pkgs.coreutils}/bin/chown -R hive-core:hive-core {} +"
|
||||
"+-${pkgs.coreutils}/bin/chown hive-core:hive-core /var/lib/hyperhive/agents"
|
||||
];
|
||||
Restart = "on-failure";
|
||||
RestartSec = 2;
|
||||
User = "hive-core";
|
||||
|
|
@ -521,6 +538,14 @@ in
|
|||
pkgs.util-linux # umount (nsenter is hardcoded in the script)
|
||||
pkgs.e2fsprogs # chattr
|
||||
];
|
||||
environment = {
|
||||
# `nixos-container update/create` runs `nix`, which writes its
|
||||
# fetcher/eval cache under $HOME/.cache. With ProtectHome and no
|
||||
# explicit HOME this lands on the unwritable /var/empty and Lix
|
||||
# errors out. Point HOME at the StateDirectory below (persistent,
|
||||
# so the cache survives across rebuilds).
|
||||
HOME = "/var/lib/hive-priv";
|
||||
};
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/hive-priv";
|
||||
Type = "simple";
|
||||
|
|
@ -530,6 +555,8 @@ in
|
|||
# hive-priv needs to write to /etc/nixos-containers/ and
|
||||
# /run/systemd/system/ — "strict" would block both.
|
||||
ProtectSystem = "false";
|
||||
# Writable HOME for nix's caches (see environment.HOME above).
|
||||
StateDirectory = "hive-priv";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue