Watch
0
0
Fork
You've already forked hyperhive
0

docs(turn-loop): move harness systemd unit shape out of agent-roster.md

Moves the "Harness systemd unit shape" section from
docs/agent-lifecycle/agent-roster.md into docs/turn-loop/README.md: it
describes the per-agent harness systemd unit (env vars, PATH wiring,
serviceConfig), which is turn-loop material, not roster material.

Fixes two facts while moving: the ExecStart package is `hive-agent`,
not `hyperhive` (no package by that name exists); and `ruth.nix`
doesn't set any forge subscription default — it only defaults
`services.hyperhive.agent.docs.enable`.

Updates the inbound pointers in docs/turn-loop/config.md and the
module comment at nix/agent-modules/agent-service.nix.

Refs #3902
This commit is contained in:
atlas 2026-10-02 12:59:40 +02:00 • committed by mara
commit 2b2608a491
4 changed files with 75 additions and 68 deletions

View file

@ -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/<userName>` — 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 = <mergedDist>` — `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" ];
```
<!-- vale write-good.Passive = NO -->
`/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."
<!-- vale write-good.Passive = YES -->
### `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)

View file

@ -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/<userName>` — 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 = <mergedDist>` — `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" ];
```
<!-- vale write-good.Passive = NO -->
`/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."
<!-- vale write-good.Passive = YES -->
### `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.<system>.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:

View file

@ -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

View file

@ -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 =