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 None of the above is a stable interface — treat the module doc
comments as the source of truth for exactly which checks exist today. 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 ## Cross-references
- Milestone: "Agent privileges and sub-agents" (tracked internally) - 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 it checkpoints the session and observes wakes that only reach the
harness between turns. 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 ## Sub-pages
The rest lives alongside this page, in three topic files: 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 Any tool invocation that needs root then fails loudly with the standard
sudo rejection rather than silently succeeding — easier to audit. 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` is the unix user the harness and
`services.hyperhive.agent.user.name` are the companion options; see its co-process daemons run as inside the container (default `"agent"`
`docs/agent-lifecycle/agent-roster.md` — "Harness systemd unit shape" for the full for a standalone evaluation; the meta flake rebinds it to the agent's
`user.*` surface. 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 ## Dashboard links

View file

@ -455,7 +455,7 @@ in
# Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars, # Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars,
# RuntimeDirectory, User=, standalone-eval fallbacks): # 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 # auto-append behaviour: docs/process/gotchas.md::systemd.services.*.path
# appends /bin to every entry. # appends /bin to every entry.
systemd.services.hive-agent = systemd.services.hive-agent =