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:
parent
46f1f3cbdb
commit
2b2608a491
4 changed files with 75 additions and 68 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue