diff --git a/docs/agent-lifecycle/agent-roster.md b/docs/agent-lifecycle/agent-roster.md index 953b04b4..a015eac4 100644 --- a/docs/agent-lifecycle/agent-roster.md +++ b/docs/agent-lifecycle/agent-roster.md @@ -136,69 +136,6 @@ through it the same way everywhere. None of the above is a stable interface — treat the module doc comments as the source of truth for exactly which checks exist today. -## Harness systemd unit shape - -One harness serve binary (`hive-agent`, with its `hive-agent-mcp` -sibling), one shared `nix/agent-modules/` tree, one service unit -(`systemd.services.hive-agent`) for all agents. No separate manager -service name or role distinction exists in the harness — privilege -differences live server-side in the broker socket (which tool groups -and manager-surface calls each agent receives). - -`agent.nix` and `ruth.nix` both import the shared `nix/agent-modules/`. -`ruth.nix` additionally sets forge defaults to suppress the -subscription/participation firehose so ruth's inbox stays focused on -direct mentions, reviews, and assignments. - -### Environment variables set on the unit - -- `HOME = /home/` — systemd defaults `HOME` to `/` for - services without `User=` set; with the per-agent user the harness - needs the right home so claude finds its bind-mounted `~/.claude/` - session dir. -- `HIVE_STATIC_DIR = ` — `tower_http::ServeDir` root for - the per-agent web UI; merged dist = agent default + every - `services.hyperhive.agent.frontend.extraFiles` overlay. -- `HIVE_ASSETS_DIR = pkgs.hyperhive-assets/share/hyperhive` — set - directly on the unit, **not** via `environment.variables`, because - the latter only populates `/etc/profile` which systemd services - don't inherit. - -### `PATH` setup (the wrapper-dir trick) - -```nix -path = [ "/run/wrappers" "/run/current-system/sw" ]; -``` - - - -`/run/wrappers` (not `/run/wrappers/bin`) comes first so setuid -wrappers — notably `sudo` — resolve before bare nix-store binaries; see -[`docs/process/gotchas.md`](../process/gotchas.md) ("`systemd.services.*.path` appends -`/bin` to every entry") for why the trailing `/bin` matters in -general. It's load-bearing here because the harness runs as the -per-agent user: without the wrapper dir on `PATH`, `sudo` resolves to -the non-setuid nix-store binary and every -`services.hyperhive.agent.user.passwordlessSudo` grant fails with "must be owned by -uid 0 and have the setuid bit set." - - - -### `serviceConfig` highlights - -- `ExecStart = pkgs.hyperhive/bin/hive-agent` — same binary for every - agent. -- `Restart = on-failure`, `RestartSec = 2` — keeps the harness - resilient across transient crashes without thundering retries. -- `RuntimeDirectory = "hive-config"` → `/run/hive-config/` owned by - `User=`, autocleared on stop. The harness writes regenerated - `claude-{mcp-config,settings,system-prompt}` files there - (`paths::config_dir`). Deliberately separate from `/run/hive`, which - the host bind-mounts in root-owned and which holds hive-c0re's - `mcp.sock`. -- `User = Group = userName` — drops root inside the container; sudo is - the explicit escalation surface (`services.hyperhive.agent.user.passwordlessSudo`). - ## Cross-references - Milestone: "Agent privileges and sub-agents" (tracked internally) diff --git a/docs/turn-loop/README.md b/docs/turn-loop/README.md index 60823ca5..fe763aa4 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop/README.md @@ -170,6 +170,73 @@ those drive the next one is strictly better than parking in-process: it checkpoints the session and observes wakes that only reach the harness between turns. +## Harness systemd unit shape + +One harness serve binary (`hive-agent`, with its `hive-agent-mcp` +sibling), one shared `nix/agent-modules/` tree, one service unit +(`systemd.services.hive-agent`) for all agents. Privilege +differences live server-side in the broker socket (which tool groups +and manager-surface calls each agent receives). + +`agent.nix` and `ruth.nix` both import the shared `nix/agent-modules/`. +`ruth.nix` additionally defaults +`services.hyperhive.agent.docs.enable` to `true`, so the root/manager +gets the hyperhive reference docs readable at `$HIVE_DOCS_DIR` by +default; other agents opt in per `agent.nix` (see +[config.md](config.md), "Reference docs"). + +### Environment variables set on the unit + +- `HOME = /home/` — systemd defaults `HOME` to `/` for + services without `User=` set; with the per-agent user the harness + needs the right home so claude finds its bind-mounted `~/.claude/` + session dir. +- `HIVE_STATIC_DIR = ` — `tower_http::ServeDir` root for + the per-agent web UI; merged dist = agent default + every + `services.hyperhive.agent.frontend.extraFiles` overlay. +- `HIVE_ASSETS_DIR = "${config.services.hyperhive.agent.packages.assets}/share/hyperhive"` + (`nix/agent-modules/agent-service.nix:480`; `packages.assets` resolves + to the `hyperhive-assets` derivation) — set directly on the unit, + **not** via `environment.variables`, because the latter only + populates `/etc/profile` which systemd services don't inherit. + +### `PATH` setup (the wrapper-dir trick) + +```nix +path = [ "/run/wrappers" "/run/current-system/sw" ]; +``` + + + +`/run/wrappers` (not `/run/wrappers/bin`) comes first so setuid +wrappers — notably `sudo` — resolve before bare nix-store binaries; see +[`docs/process/gotchas.md`](../process/gotchas.md) ("`systemd.services.*.path` appends +`/bin` to every entry") for why the trailing `/bin` matters in +general. It's load-bearing here because the harness runs as the +per-agent user: without the wrapper dir on `PATH`, `sudo` resolves to +the non-setuid nix-store binary and every +`services.hyperhive.agent.user.passwordlessSudo` grant fails with "must be owned by +uid 0 and have the setuid bit set." + + + +### `serviceConfig` highlights + +- `ExecStart = "${config.services.hyperhive.agent.packages.hive-agent}/bin/${binary}"` + (`nix/agent-modules/agent-service.nix:535`); `packages.hive-agent` is + wired to the flake's `hyperhive.packages..hive-agent` output + (`nix/agent-modules/packages.nix:8-22`) — same binary for every agent. +- `Restart = on-failure`, `RestartSec = 2` — keeps the harness + resilient across transient crashes without thundering retries. +- `RuntimeDirectory = "hive-config"` → `/run/hive-config/` owned by + `User=`, autocleared on stop. The harness writes regenerated + `claude-{mcp-config,settings,system-prompt}` files there + (`paths::config_dir`). Deliberately separate from `/run/hive`, which + the host bind-mounts in root-owned and which holds hive-c0re's + `mcp.sock`. +- `User = Group = userName` — drops root inside the container; sudo is + the explicit escalation surface (`services.hyperhive.agent.user.passwordlessSudo`). + ## Sub-pages The rest lives alongside this page, in three topic files: diff --git a/docs/turn-loop/config.md b/docs/turn-loop/config.md index 3dc01a58..36d3cf60 100644 --- a/docs/turn-loop/config.md +++ b/docs/turn-loop/config.md @@ -57,10 +57,13 @@ Set to `false` for agents that should be strictly unprivileged. Any tool invocation that needs root then fails loudly with the standard sudo rejection rather than silently succeeding — easier to audit. -`services.hyperhive.agent.user.uid`, `services.hyperhive.agent.user.gid`, and -`services.hyperhive.agent.user.name` are the companion options; see -`docs/agent-lifecycle/agent-roster.md` — "Harness systemd unit shape" for the full -`user.*` surface. +`services.hyperhive.agent.user.name` is the unix user the harness and +its co-process daemons run as inside the container (default `"agent"` +for a standalone evaluation; the meta flake rebinds it to the agent's +own name). `services.hyperhive.agent.user.uid` / `.gid` are optional +fixed UID/GID (`null` default lets NixOS assign one automatically from +the normal-user range) — set them only when something outside the +container needs a stable numeric id across a full destroy + recreate. ## Dashboard links diff --git a/nix/agent-modules/agent-service.nix b/nix/agent-modules/agent-service.nix index ef2cfb03..9609e2b2 100644 --- a/nix/agent-modules/agent-service.nix +++ b/nix/agent-modules/agent-service.nix @@ -455,7 +455,7 @@ in # Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars, # RuntimeDirectory, User=, standalone-eval fallbacks): - # docs/agent-lifecycle/agent-roster.md::Harness systemd unit shape. PATH /bin + # docs/turn-loop/README.md::Harness systemd unit shape. PATH /bin # auto-append behaviour: docs/process/gotchas.md::systemd.services.*.path # appends /bin to every entry. systemd.services.hive-agent =