docs: repoint agent-tier option paths to services.hyperhive.agent.*

Refs #4479
This commit is contained in:
atlas 2026-09-18 00:55:51 +02:00 • committed by mara
commit 7570597190
18 changed files with 78 additions and 78 deletions

View file

@ -53,7 +53,7 @@ declarations.
- **How does the matrix-tuwunel container work? Multiple accounts per - **How does the matrix-tuwunel container work? Multiple accounts per
agent?** → [`integrations/matrix.md`](integrations/matrix.md) (the homeserver); agent?** → [`integrations/matrix.md`](integrations/matrix.md) (the homeserver);
[`tools/matrix.md`](tools/matrix.md) for the MCP tool surface and [`tools/matrix.md`](tools/matrix.md) for the MCP tool surface and
`hyperhive.matrixAccounts`. `services.hyperhive.agent.matrixAccounts`.
- **How do I give an agent a GitHub account (`gh` + `git push`)? how's - **How do I give an agent a GitHub account (`gh` + `git push`)? how's
the PAT injected?** → [`integrations/github.md`](integrations/github.md) the PAT injected?** → [`integrations/github.md`](integrations/github.md)
(operator content up top; the `gh`/git-push + notification-poller (operator content up top; the `gh`/git-push + notification-poller

View file

@ -194,7 +194,7 @@ direct mentions, reviews, and assignments.
session dir. session dir.
- `HIVE_STATIC_DIR = <mergedDist>` — `tower_http::ServeDir` root for - `HIVE_STATIC_DIR = <mergedDist>` — `tower_http::ServeDir` root for
the per-agent web UI; merged dist = agent default + every the per-agent web UI; merged dist = agent default + every
`hyperhive.frontend.extraFiles` overlay. `services.hyperhive.agent.frontend.extraFiles` overlay.
- `HIVE_ASSETS_DIR = pkgs.hyperhive-assets/share/hyperhive` — set - `HIVE_ASSETS_DIR = pkgs.hyperhive-assets/share/hyperhive` — set
directly on the unit, **not** via `environment.variables`, because directly on the unit, **not** via `environment.variables`, because
the latter only populates `/etc/profile` which systemd services the latter only populates `/etc/profile` which systemd services
@ -213,7 +213,7 @@ wrappers — notably `sudo` — resolve before bare nix-store binaries; see
general. It's load-bearing here because the harness runs as the general. It's load-bearing here because the harness runs as the
per-agent user: without the wrapper dir on `PATH`, `sudo` resolves to per-agent user: without the wrapper dir on `PATH`, `sudo` resolves to
the non-setuid nix-store binary and every the non-setuid nix-store binary and every
`hyperhive.user.passwordlessSudo` grant fails with "must be owned by `services.hyperhive.agent.user.passwordlessSudo` grant fails with "must be owned by
uid 0 and have the setuid bit set." uid 0 and have the setuid bit set."
### `serviceConfig` highlights ### `serviceConfig` highlights
@ -229,7 +229,7 @@ uid 0 and have the setuid bit set."
the host bind-mounts in root-owned and which holds hive-c0re's the host bind-mounts in root-owned and which holds hive-c0re's
`mcp.sock`. `mcp.sock`.
- `User = Group = userName` — drops root inside the container; sudo is - `User = Group = userName` — drops root inside the container; sudo is
the explicit escalation surface (`hyperhive.user.passwordlessSudo`). the explicit escalation surface (`services.hyperhive.agent.user.passwordlessSudo`).
## Cross-references ## Cross-references

View file

@ -505,7 +505,7 @@ reinstall.
## First-boot agent-user migration ## First-boot agent-user migration
The harness runs as a per-agent unix user inside the container The harness runs as a per-agent unix user inside the container
(`hyperhive.user.name`, defaults to the agent's logical label so each (`services.hyperhive.agent.user.name`, defaults to the agent's logical label so each
container has a uniquely named user). Operators with legacy root-owned container has a uniquely named user). Operators with legacy root-owned
state dirs need a one-time data shuffle so they don't lose their claude state dirs need a one-time data shuffle so they don't lose their claude
session. session.
@ -544,10 +544,10 @@ the body + marker check at that point.
`hive-matrix-daemon` is a long-running matrix-sdk Client + sync `hive-matrix-daemon` is a long-running matrix-sdk Client + sync
process per agent. Serves its MCP tools directly over process per agent. Serves its MCP tools directly over
streamable-http (`hyperhive.mcp.matrixHttpPort`, no stdio bridge — streamable-http (`services.hyperhive.agent.mcp.matrixHttpPort`, no stdio bridge —
same shape as `hive-bash-daemon`), emits hyperhive wake signals same shape as `hive-bash-daemon`), emits hyperhive wake signals
on incoming room events via `/run/hive/mcp.sock`. Conditional on on incoming room events via `/run/hive/mcp.sock`. Conditional on
`hyperhive.matrix.enable` (which both the daemon AND the `services.hyperhive.agent.matrix.enable` (which both the daemon AND the
autoinjected `extraMcpServers.matrix` entry read). autoinjected `extraMcpServers.matrix` entry read).
**First-boot ordering**: hive-c0re provisions the matrix token AFTER **First-boot ordering**: hive-c0re provisions the matrix token AFTER
@ -565,7 +565,7 @@ avatar (see below).
### matrix avatar (set by the daemon over the live Client) ### matrix avatar (set by the daemon over the live Client)
`hive-matrix-daemon` itself publishes the agent icon (`hyperhive.icon`, an SVG) as each matrix `hive-matrix-daemon` itself publishes the agent icon (`services.hyperhive.agent.icon`, an SVG) as each matrix
account's profile avatar account's profile avatar
(`hive-matrix-mcp::client::sync_avatar`), not a separate oneshot. After (`hive-matrix-mcp::client::sync_avatar`), not a separate oneshot. After
the daemon builds + restores an account's `Client` (authenticated, the daemon builds + restores an account's `Client` (authenticated,
@ -578,7 +578,7 @@ bring-up loop, so it sets the avatar for **every** account.
Nix rasterizes the SVG to a 512x512 PNG at build time (`iconPng`, via Nix rasterizes the SVG to a 512x512 PNG at build time (`iconPng`, via
librsvg) and forwards its store path as `HIVE_ICON_PNG` on the daemon librsvg) and forwards its store path as `HIVE_ICON_PNG` on the daemon
unit, gated on `hyperhive.icon != null`. No icon configured → the env is unit, gated on `services.hyperhive.agent.icon != null`. No icon configured → the env is
unset → `sync_avatar` returns early and no avatar is set. unset → `sync_avatar` returns early and no avatar is set.
Idempotency is **per-account**: an `avatar-icon-hash` file in each Idempotency is **per-account**: an `avatar-icon-hash` file in each

View file

@ -12,7 +12,7 @@ injects the token into the agent's state dir out of band.
## Enabling ## Enabling
The integration is **on by default** for every agent (`hyperhive.github.enable The integration is **on by default** for every agent (`services.hyperhive.agent.github.enable
= true`), inert until the operator provisions a PAT. No per-agent declaration is = true`), inert until the operator provisions a PAT. No per-agent declaration is
needed — an agent gains GitHub by having a PAT written to its token needed — an agent gains GitHub by having a PAT written to its token
file. file.
@ -23,9 +23,9 @@ To turn it off for the whole hive, set the host option:
services.hyperhive.github.enable = false; services.hyperhive.github.enable = false;
``` ```
hive-c0re's meta-flake renderer then injects `hyperhive.github.enable = false` hive-c0re's meta-flake renderer then injects `services.hyperhive.agent.github.enable = false`
into every agent, so no agent ships the `gh` wrapper or credential helper. into every agent, so no agent ships the `gh` wrapper or credential helper.
(`hyperhive.github.enable` also exists per-agent for completeness, but the (`services.hyperhive.agent.github.enable` also exists per-agent for completeness, but the
hive-wide host switch is the intended control.) hive-wide host switch is the intended control.)
github.com only. The token **value** never touches nix — it's written to github.com only. The token **value** never touches nix — it's written to
@ -92,7 +92,7 @@ notification thread into a todo. it's a **separate binary and a
separate systemd unit** from the internal forge's poller separate systemd unit** from the internal forge's poller
(`hive-forge-notify`, see [forge.md](forge.md#notification-poller-hive-forge-notifysrcnotifyrs)), (`hive-forge-notify`, see [forge.md](forge.md#notification-poller-hive-forge-notifysrcnotifyrs)),
installed by `nix/agent-modules/github.nix` under installed by `nix/agent-modules/github.nix` under
`hyperhive.github.enable`. Both binaries ship from the one `services.hyperhive.agent.github.enable`. Both binaries ship from the one
`hive-forge-notify` derivation, so the unit is a second `ExecStart` `hive-forge-notify` derivation, so the unit is a second `ExecStart`
path, not a new package. path, not a new package.

View file

@ -461,7 +461,7 @@ unset (for example a build that predates `services.hyperhive.c0re.frontend`), ea
legacy single-proxy block and nginx forwards all traffic to the agent legacy single-proxy block and nginx forwards all traffic to the agent
daemon as before. daemon as before.
**`extraFiles`**: per-agent `hyperhive.frontend.extraFiles` are in **`extraFiles`**: per-agent `services.hyperhive.agent.frontend.extraFiles` are in
`mergedDist`, not in the base `services.hyperhive.c0re.frontend` dist. They're not under `mergedDist`, not in the base `services.hyperhive.c0re.frontend` dist. They're not under
the nix-store `alias` path, so requests for them fall through the nix-store `alias` path, so requests for them fall through
`try_files` to `@<name>_dynamic`, and the agent daemon serves them `try_files` to `@<name>_dynamic`, and the agent daemon serves them

View file

@ -523,7 +523,7 @@ the state dir at provisioning time:
`forge-token` written by `hive-c0re::forge::ensure_user_for`, `forge-token` written by `hive-c0re::forge::ensure_user_for`,
so `tea repos create` / `tea pulls create` work without so `tea repos create` / `tea pulls create` work without
interactive prompts. interactive prompts.
- `forge-avatar-sync` — uploads `hyperhive.icon` SVG to the - `forge-avatar-sync` — uploads `services.hyperhive.agent.icon` SVG to the
agent's Forgejo profile, so the icon shows up on commits / PRs / agent's Forgejo profile, so the icon shows up on commits / PRs /
issue comments. issue comments.

View file

@ -135,7 +135,7 @@ agent's flake as a string literal and the agent module symlinks
`path:/nix/store/<pkg>` flake input is re-copied into the store as a `path:/nix/store/<pkg>` flake input is re-copied into the store as a
reference-less `-source` (so the runtime closure never arrives), and reference-less `-source` (so the runtime closure never arrives), and
`lib.types.package` fed a bare path runs `builtins.storePath`, which `lib.types.package` fed a bare path runs `builtins.storePath`, which
pure evaluation rejects. `hyperhive.docs.source` gets away with being pure evaluation rejects. `services.hyperhive.agent.docs.source` gets away with being
an input only because a docs tree has no runtime dependencies. an input only because a docs tree has no runtime dependencies.
The `storePath` trap is worth spelling out, because it's not confined The `storePath` trap is worth spelling out, because it's not confined
@ -366,15 +366,15 @@ Never use raw `curl` for forge access.
## GUI (weston/VNC) ## GUI (weston/VNC)
### Weston VNC compositor (per-agent `hyperhive.gui.enable`) ### Weston VNC compositor (per-agent `services.hyperhive.agent.gui.enable`)
`nix/agent-modules/weston-vnc.nix` adds an optional Weston Wayland `nix/agent-modules/weston-vnc.nix` adds an optional Weston Wayland
compositor with the `vnc-backend` plugin, surfaced as compositor with the `vnc-backend` plugin, surfaced as
`hyperhive.gui.enable = true` per-agent. The harness's `services.hyperhive.agent.gui.enable = true` per-agent. The harness's
`/screen/ws` WebSocket relay (`docs/web-ui/agent.md::Per-agent endpoints`) `/screen/ws` WebSocket relay (`docs/web-ui/agent.md::Per-agent endpoints`)
connects to the compositor at `127.0.0.1:<vnc_port>`. connects to the compositor at `127.0.0.1:<vnc_port>`.
- **Port allocation**: a **fixed** port (`hyperhive.gui.vncPort`, - **Port allocation**: a **fixed** port (`services.hyperhive.agent.gui.vncPort`,
default 5900). No per-agent hashing: network isolation is default 5900). No per-agent hashing: network isolation is
unconditional (each agent has its own netns — see unconditional (each agent has its own netns — see
`docs/networking/network.md#container-isolation`), so the VNC port is `docs/networking/network.md#container-isolation`), so the VNC port is
@ -384,7 +384,7 @@ connects to the compositor at `127.0.0.1:<vnc_port>`.
the agent **web-UI** port, which is still an FNV-1a hash because those the agent **web-UI** port, which is still an FNV-1a hash because those
listen on the shared host stack — see `Web UI ports collide on hash`.) listen on the shared host stack — see `Web UI ports collide on hash`.)
- **Non-root, shared user session**: weston runs as the agent's own - **Non-root, shared user session**: weston runs as the agent's own
user (`hyperhive.user.name`, the same user hive-ag3nt runs as), not user (`services.hyperhive.agent.user.name`, the same user hive-ag3nt runs as), not
root, so the GUI and the agent share one session. The runtime dir is a root, so the GUI and the agent share one session. The runtime dir is a
fixed `/run/gui` (systemd `RuntimeDirectory=gui`, `0700`, fixed `/run/gui` (systemd `RuntimeDirectory=gui`, `0700`,
`RuntimeDirectoryPreserve=yes` so it survives weston restarts for the `RuntimeDirectoryPreserve=yes` so it survives weston restarts for the
@ -407,7 +407,7 @@ connects to the compositor at `127.0.0.1:<vnc_port>`.
launched with `--socket=wayland-0` so the socket path is launched with `--socket=wayland-0` so the socket path is
deterministic. `nix/agent-modules/weston-vnc.nix` exports `WAYLAND_DISPLAY=wayland-0` deterministic. `nix/agent-modules/weston-vnc.nix` exports `WAYLAND_DISPLAY=wayland-0`
and `XDG_RUNTIME_DIR=/run/gui` as global system environment and `XDG_RUNTIME_DIR=/run/gui` as global system environment
variables (gated on `hyperhive.gui.enable`) so every systemd service variables (gated on `services.hyperhive.agent.gui.enable`) so every systemd service
in the container inherits them. Without this, services starting in the container inherits them. Without this, services starting
Wayland clients couldn't find the compositor — libwayland falls Wayland clients couldn't find the compositor — libwayland falls
back to a headless display or errors out, the app "works" on a back to a headless display or errors out, the app "works" on a
@ -505,7 +505,7 @@ The fix (`nix/docs/default.nix`):
context. The resulting store path is content-addressed from the nix/ context. The resulting store path is content-addressed from the nix/
file contents only. Docs drvs only change when a `.nix` file changes. file contents only. Docs drvs only change when a `.nix` file changes.
2. The package options the modules consume (`hyperhive.packages.*`, 2. The package options the modules consume (`services.hyperhive.agent.packages.*`,
`services.hyperhive.c0re.*`) carry no in-module defaults and every `services.hyperhive.c0re.*`) carry no in-module defaults and every
default that references them has a `defaultText`, so the doc walk default that references them has a `defaultText`, so the doc walk
never forces a package — no stubs needed, and the Rust/frontend never forces a package — no stubs needed, and the Rust/frontend

View file

@ -97,7 +97,7 @@ opening a popover of links to other swarm-wide services — authelia,
matrix, forge, this UI's own swagger docs. Backed by `GET /api/links` matrix, forge, this UI's own swagger docs. Backed by `GET /api/links`
(swarm-controller), which serves `services.hyperhive.swarm.controller.links` (swarm-controller), which serves `services.hyperhive.swarm.controller.links`
(a `listOf { label, icon, url }`, same shape as the per-agent (a `listOf { label, icon, url }`, same shape as the per-agent
`hyperhive.dashboardLinks`). `services.hyperhive.agent.dashboardLinks`).
Rather than one central hardcoded list, each service's own module Rather than one central hardcoded list, each service's own module
contributes its own entry when it's actually enabled on the controller's contributes its own entry when it's actually enabled on the controller's

View file

@ -2,7 +2,7 @@
Background shell execution via `hive-bash-daemon`. Tools land as Background shell execution via `hive-bash-daemon`. Tools land as
`mcp__bash__<tool>` (the MCP server name is `bash`, not `hyperhive`). `mcp__bash__<tool>` (the MCP server name is `bash`, not `hyperhive`).
`nix/agent-modules/mcp.nix` registers bash into `hyperhive.extraMcpServers` `nix/agent-modules/mcp.nix` registers bash into `services.hyperhive.agent.extraMcpServers`
by default (`lib.mkDefault`, with `allowedTools = ["*"]`) — an `agent.nix` by default (`lib.mkDefault`, with `allowedTools = ["*"]`) — an `agent.nix`
can override or drop the entry, so this is a default, not an can override or drop the entry, so this is a default, not an
unconditional grant. Even where the entry is present, the server is only unconditional grant. Even where the entry is present, the server is only
@ -101,8 +101,8 @@ this structured path so tasks get task-id tracking and structured output.
container, systemd service in `nix/agent-modules/mcp.nix`) — no stdio container, systemd service in `nix/agent-modules/mcp.nix`) — no stdio
bridge. It owns subprocess management, output file writing, todo delivery bridge. It owns subprocess management, output file writing, todo delivery
on the harness's in-agent socket, **and** serves the `run`/`status`/`kill` on the harness's in-agent socket, **and** serves the `run`/`status`/`kill`
MCP tools directly over streamable-http on `hyperhive.mcp.bashHttpPort` MCP tools directly over streamable-http on `services.hyperhive.agent.mcp.bashHttpPort`
(declared in `hyperhive.extraMcpServers.bash` as `{ type = "http"; url = (declared in `services.hyperhive.agent.extraMcpServers.bash` as `{ type = "http"; url =
...; }`). Same shape as the built-in `hyperhive` surface (`hive-mcp-http`) ...; }`). Same shape as the built-in `hyperhive` surface (`hive-mcp-http`)
— claude reconnects to the stable URL every turn instead of respawning a — claude reconnects to the stable URL every turn instead of respawning a
stdio child, so there's no per-turn MCP re-registration race and no stdio child, so there's no per-turn MCP re-registration race and no

View file

@ -97,7 +97,7 @@ agent's token file so its `gh` wrapper + git credential helper can act as
the bot account. Unlike forge/matrix there is no account creation — the PAT the bot account. Unlike forge/matrix there is no account creation — the PAT
is for an existing GitHub account. A CLI alternative to the dashboard is for an existing GitHub account. A CLI alternative to the dashboard
credentials tab; the [GitHub integration](../integrations/github.md) is on by default credentials tab; the [GitHub integration](../integrations/github.md) is on by default
(`hyperhive.github.enable`), so no per-agent config is needed. (`services.hyperhive.agent.github.enable`), so no per-agent config is needed.
```bash ```bash
hivectl github set-token damocles --token-stdin # paste the PAT on stdin (preferred) hivectl github set-token damocles --token-stdin # paste the PAT on stdin (preferred)

View file

@ -2,7 +2,7 @@
## Built-in matrix MCP (`mcp__matrix__*`) ## Built-in matrix MCP (`mcp__matrix__*`)
When `hyperhive.matrix.enable = true` and the host-level matrix When `services.hyperhive.agent.matrix.enable = true` and the host-level matrix
tuwunel is configured, the harness autoinjects `hive-matrix-daemon`'s tuwunel is configured, the harness autoinjects `hive-matrix-daemon`'s
streamable-http endpoint as a second MCP server (no stdio bridge — streamable-http endpoint as a second MCP server (no stdio bridge —
see Architecture below). Tools land as `mcp__matrix__<name>`: see Architecture below). Tools land as `mcp__matrix__<name>`:
@ -69,17 +69,17 @@ room you haven't read yet.
## Multiple accounts ## Multiple accounts
`hyperhive.matrixAccounts` (declared in `agent.nix`) gives an agent `services.hyperhive.agent.matrixAccounts` (declared in `agent.nix`) gives an agent
*additional* matrix identities beyond the hive-internal one — for example an *additional* matrix identities beyond the hive-internal one — for example an
external-facing account alongside the internal one. Each entry is external-facing account alongside the internal one. Each entry is
keyed by account name and specifies `tokenFile` (bearer token, keyed by account name and specifies `tokenFile` (bearer token,
provisioned out-of-band; basename must start with `matrix-token`), provisioned out-of-band; basename must start with `matrix-token`),
`sessionDir` (per-account matrix-sdk sqlite state — crypto keys + `sessionDir` (per-account matrix-sdk sqlite state — crypto keys +
cache), and an optional `homeserver` (defaults to cache), and an optional `homeserver` (defaults to
`hyperhive.matrix.url`). The hive-internal account is always named `services.hyperhive.agent.matrix.url`). The hive-internal account is always named
`main`, synthesized from `hyperhive.matrix.url` + agent state — this `main`, synthesized from `services.hyperhive.agent.matrix.url` + agent state — this
option only declares extras, and `main` is a reserved key here. option only declares extras, and `main` is a reserved key here.
Requires `hyperhive.matrix.enable = true`. Requires `services.hyperhive.agent.matrix.enable = true`.
Every matrix tool above takes an optional `account` parameter (a name Every matrix tool above takes an optional `account` parameter (a name
from this map) to act as that identity instead of the primary one. from this map) to act as that identity instead of the primary one.
@ -94,8 +94,8 @@ bodies and invite todos gain an `[acct:<name>]` prefix — see
container, systemd service in `nix/agent-modules/matrix.nix`) — no container, systemd service in `nix/agent-modules/matrix.nix`) — no
stdio bridge, no separate bin. It owns the matrix-sdk `Client` + sync stdio bridge, no separate bin. It owns the matrix-sdk `Client` + sync
loop per configured account **and** serves the matrix tool surface loop per configured account **and** serves the matrix tool surface
directly over streamable-http on `hyperhive.mcp.matrixHttpPort` directly over streamable-http on `services.hyperhive.agent.mcp.matrixHttpPort`
(declared in `hyperhive.extraMcpServers.matrix` as (declared in `services.hyperhive.agent.extraMcpServers.matrix` as
`{ type = "http"; url = ...; }`). Same shape as `hive-bash-daemon` and `{ type = "http"; url = ...; }`). Same shape as `hive-bash-daemon` and
the built-in `hyperhive` surface (`hive-mcp-http`) — claude reconnects the built-in `hyperhive` surface (`hive-mcp-http`) — claude reconnects
to the stable URL every turn instead of respawning a stdio child. to the stable URL every turn instead of respawning a stdio child.
@ -115,7 +115,7 @@ Incoming room events wake the agent via `AgentRequest::Wake` with
per room) per room)
**Multi-account prefix**: when the daemon serves more than one account **Multi-account prefix**: when the daemon serves more than one account
(`hyperhive.matrixAccounts`), it prefixes every wake body and every invite todo (`services.hyperhive.agent.matrixAccounts`), it prefixes every wake body and every invite todo
below with `[acct:<name>] `, so a wake arrives as below with `[acct:<name>] `, so a wake arrives as
`[acct:ccc] [matrix] <sender> in <room>: …`. A single-account agent gets the `[acct:ccc] [matrix] <sender> in <room>: …`. A single-account agent gets the
formats exactly as written — the daemon adds nothing — which is why matching formats exactly as written — the daemon adds nothing — which is why matching
@ -147,7 +147,7 @@ provisioning flow, and federation config.
## Extra MCP servers (per-agent) ## Extra MCP servers (per-agent)
Each agent's NixOS config can declare additional MCP servers via Each agent's NixOS config can declare additional MCP servers via
`hyperhive.extraMcpServers.<key> = { type, command, args, env, url, `services.hyperhive.agent.extraMcpServers.<key> = { type, command, args, env, url,
allowedTools }` — `type = "stdio"` (the default, uses `command`/`args`/ allowedTools }` — `type = "stdio"` (the default, uses `command`/`args`/
`env`) or `type = "http"` (uses `url`, a long-lived streamable-http `env`) or `type = "http"` (uses `url`, a long-lived streamable-http
endpoint — see `hive-bash-daemon` and `hive-matrix-daemon` above for endpoint — see `hive-bash-daemon` and `hive-matrix-daemon` above for

View file

@ -8,7 +8,7 @@ capability than a background shell command, so it gets its own
deployable/restartable unit rather than living inside the bash daemon. deployable/restartable unit rather than living inside the bash daemon.
Shipped default-on for every agent — `nix/agent-modules/mcp.nix` injects Shipped default-on for every agent — `nix/agent-modules/mcp.nix` injects
`subagent` into `hyperhive.extraMcpServers` via `lib.mkDefault` `subagent` into `services.hyperhive.agent.extraMcpServers` via `lib.mkDefault`
(`allowedTools = ["*"]`), same as `bash`. Default-on rather than (`allowedTools = ["*"]`), same as `bash`. Default-on rather than
unconditional: an `agent.nix` can override or drop the entry, which is unconditional: an `agent.nix` can override or drop the entry, which is
what `mkDefault` is there for. The operator's own framing: default-on for what `mkDefault` is there for. The operator's own framing: default-on for
@ -210,7 +210,7 @@ assumption stops holding.
## Configuration ## Configuration
`hyperhive.mcp.subagentHttpPort` — the daemon's streamable-http listen `services.hyperhive.agent.mcp.subagentHttpPort` — the daemon's streamable-http listen
port. Same pattern as `bashHttpPort`/`matrixHttpPort`: a per-agent default port. Same pattern as `bashHttpPort`/`matrixHttpPort`: a per-agent default
assigned by `nix/agent-modules/mcp.nix`, only worth overriding for an assigned by `nix/agent-modules/mcp.nix`, only worth overriding for an
agent that needs a stable or non-default port. agent that needs a stable or non-default port.
@ -284,7 +284,7 @@ all, and the automatically injected `bash`/`subagent` entries default to exclude
too (a subagent can't spawn hive-bash tasks or its own nested subagents too (a subagent can't spawn hive-bash tasks or its own nested subagents
unless an operator opts them in explicitly, same as anything else). unless an operator opts them in explicitly, same as anything else).
Set `hyperhive.extraMcpServers.<name>.availableToSubagents = true` on a Set `services.hyperhive.agent.extraMcpServers.<name>.availableToSubagents = true` on a
specific entry to hand that one server to subagents as well — useful for, specific entry to hand that one server to subagents as well — useful for,
say, a read-only lookup or scraper MCP a subagent's bounded, single-batch say, a read-only lookup or scraper MCP a subagent's bounded, single-batch
task might need. `hive-subagent-mcp`'s `mcp_config` module renders the task might need. `hive-subagent-mcp`'s `mcp_config` module renders the

View file

@ -74,7 +74,7 @@ regardless of who sends them):
claude-pump + ack/requeue cycle described above). claude-pump + ack/requeue cycle described above).
- `hive-agent-mcp` — MCP server for the built-in `hyperhive` surface. - `hive-agent-mcp` — MCP server for the built-in `hyperhive` surface.
Run with `--http <addr>` as a persistent streamable-HTTP daemon (the Run with `--http <addr>` as a persistent streamable-HTTP daemon (the
`hive-mcp-http` systemd unit, on `hyperhive.mcp.httpPort`, default `hive-mcp-http` systemd unit, on `services.hyperhive.agent.mcp.httpPort`, default
8790); claude connects to its URL via `--mcp-config`. HTTP is the sole 8790); claude connects to its URL via `--mcp-config`. HTTP is the sole
transport — no per-turn stdio child (eliminates the re-registration transport — no per-turn stdio child (eliminates the re-registration
race). race).

View file

@ -42,7 +42,7 @@ deliberately not in that file — the `--effort` flag
live, which managed scope would otherwise lock. live, which managed scope would otherwise lock.
The harness reads `<name>` from `Bus::model()` on each turn. The harness reads `<name>` from `Bus::model()` on each turn.
`hyperhive.model` in the agent's `agent.nix` sets the initial `services.hyperhive.agent.model` in the agent's `agent.nix` sets the initial
default (NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls default (NixOS option; propagates via `HIVE_DEFAULT_MODEL` env var; falls
back to `"haiku"` if unset). The operator can flip it at runtime back to `"haiku"` if unset). The operator can flip it at runtime
with `/model <name>` in the web terminal — the next turn picks it with `/model <name>` in the web terminal — the next turn picks it
@ -146,7 +146,7 @@ deliberately _not_ the primary mechanism — it runs with no checkpoint and
none of the `CHECKPOINT_PROMPT` notes-flush machinery above, so a session none of the `CHECKPOINT_PROMPT` notes-flush machinery above, so a session
saved by it still loses whatever wasn't already durable — but it can act saved by it still loses whatever wasn't already durable — but it can act
_mid-turn_, underneath `InfiniteSession::run`, which the two triggers above _mid-turn_, underneath `InfiniteSession::run`, which the two triggers above
structurally can't. Don't confuse this with the `hyperhive.autoCompact` structurally can't. Don't confuse this with the `services.hyperhive.agent.autoCompact`
nix option below: that one is a per-agent on/off for hyperhive's _own_ nix option below: that one is a per-agent on/off for hyperhive's _own_
proactive watermark and has no effect on claude's native mechanism, which proactive watermark and has no effect on claude's native mechanism, which
is fleet-wide and set once in `hive-agent/prompts/claude-settings.json`. is fleet-wide and set once in `hive-agent/prompts/claude-settings.json`.
@ -171,7 +171,7 @@ a live claude process.
To disable proactive compaction for a specific agent, use the nix option: To disable proactive compaction for a specific agent, use the nix option:
```nix ```nix
hyperhive.autoCompact = false; # default true services.hyperhive.agent.autoCompact = false; # default true
``` ```
Setting `autoCompact = false` sets `HIVE_COMPACT_WATERMARK_TOKENS=0`, which Setting `autoCompact = false` sets `HIVE_COMPACT_WATERMARK_TOKENS=0`, which
@ -228,7 +228,7 @@ needs to `chown` a bind mount), once at startup:
- `claude-mcp-config.json` — points claude at the persistent - `claude-mcp-config.json` — points claude at the persistent
`hive-mcp-http` daemon (`http://127.0.0.1:{port}/mcp`, port from `hive-mcp-http` daemon (`http://127.0.0.1:{port}/mcp`, port from
`hyperhive.mcp.httpPort`, default 8790) for the built-in `hyperhive` `services.hyperhive.agent.mcp.httpPort`, default 8790) for the built-in `hyperhive`
surface. HTTP is the sole transport for it — no per-turn stdio child, surface. HTTP is the sole transport for it — no per-turn stdio child,
so the URL survives the per-turn claude re-spawn (no re-registration so the URL survives the per-turn claude re-spawn (no re-registration
race), trading that for a hard dependency on the daemon's uptime race), trading that for a hard dependency on the daemon's uptime
@ -241,11 +241,11 @@ needs to `chown` a bind mount), once at startup:
substituted: `{label}` (short agent name), `{qualified_label}` substituted: `{label}` (short agent name), `{qualified_label}`
(hive-qualified `name@domain` form), `{operator_pronouns}`, (hive-qualified `name@domain` form), `{operator_pronouns}`,
`{hive_identity}` (for example `` on hive `pr1ma` ``; empty when `{hive_identity}` (for example `` on hive `pr1ma` ``; empty when
`hyperhive.hiveName` is unset), and `{swarm_identity}` (same `services.hyperhive.agent.hiveName` is unset), and `{swarm_identity}` (same
shape for the swarm). Pronouns come from `HIVE_OPERATOR_PRONOUNS` shape for the swarm). Pronouns come from `HIVE_OPERATOR_PRONOUNS`
env (set by the meta flake from env (set by the meta flake from
`services.hyperhive.c0re.operatorPronouns`, default `she/her`). `services.hyperhive.c0re.operatorPronouns`, default `she/her`).
When `hyperhive.docs.enable` is set, `HIVE_DOCS_DIR` is present When `services.hyperhive.agent.docs.enable` is set, `HIVE_DOCS_DIR` is present
in the environment and `render()` appends a one-sentence pointer in the environment and `render()` appends a one-sentence pointer
telling the agent the docs are mounted at that path. telling the agent the docs are mounted at that path.
Passed via `--system-prompt-file`. Passed via `--system-prompt-file`.

View file

@ -5,10 +5,10 @@ Optional per-agent knobs, set under `hyperhive.*` in the agent's own
Absent means the default. (The claude spawn + compaction themselves live in Absent means the default. (The claude spawn + compaction themselves live in
[claude-invocation](claude-invocation.md).) [claude-invocation](claude-invocation.md).)
## Reference docs (`hyperhive.docs.enable`) ## Reference docs (`services.hyperhive.agent.docs.enable`)
```nix ```nix
hyperhive.docs.enable = true; # default: false (true for the manager agent) services.hyperhive.agent.docs.enable = true; # default: false (true for the manager agent)
``` ```
Makes the hyperhive `docs/` tree available inside the container at a nix Makes the hyperhive `docs/` tree available inside the container at a nix
@ -27,7 +27,7 @@ that input — not every agent's container gets rebuilt on a doc-only change.
## Agent icon ## Agent icon
```nix ```nix
hyperhive.icon = ./icon.svg; # default: null (falls back to shared hyperhive logo) services.hyperhive.agent.icon = ./icon.svg; # default: null (falls back to shared hyperhive logo)
``` ```
Path to an SVG file used as this agent's visual identity — shown in Path to an SVG file used as this agent's visual identity — shown in
@ -44,7 +44,7 @@ at `GET /icon` on the per-agent web port.
## `user.passwordlessSudo` ## `user.passwordlessSudo`
```nix ```nix
hyperhive.user.passwordlessSudo = true; # default services.hyperhive.agent.user.passwordlessSudo = true; # default
``` ```
Grants the per-agent unix user passwordless `sudo` (`NOPASSWD: ALL`). Grants the per-agent unix user passwordless `sudo` (`NOPASSWD: ALL`).
@ -57,15 +57,15 @@ 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.
`hyperhive.user.uid`, `hyperhive.user.gid`, and `services.hyperhive.agent.user.uid`, `services.hyperhive.agent.user.gid`, and
`hyperhive.user.name` are the companion options; see `services.hyperhive.agent.user.name` are the companion options; see
`docs/agent-lifecycle/agent-hierarchy.md` — "Harness systemd unit shape" for the full `docs/agent-lifecycle/agent-hierarchy.md` — "Harness systemd unit shape" for the full
`user.*` surface. `user.*` surface.
## Dashboard links ## Dashboard links
```nix ```nix
hyperhive.dashboardLinks = [ services.hyperhive.agent.dashboardLinks = [
{ label = "Stats"; icon = "📊"; url = "http://localhost:9001/stats"; } { label = "Stats"; icon = "📊"; url = "http://localhost:9001/stats"; }
{ label = "Scratchpad"; url = "http://localhost:8080"; } { label = "Scratchpad"; url = "http://localhost:8080"; }
]; ];
@ -92,7 +92,7 @@ Omitting the option (default empty) produces no extra links.
## Custom static files ## Custom static files
```nix ```nix
hyperhive.frontend.extraFiles = { services.hyperhive.agent.frontend.extraFiles = {
"games/bitburner" = { "games/bitburner" = {
source = ./bitburner-dist; # path relative to agent.nix source = ./bitburner-dist; # path relative to agent.nix
# target defaults to attribute name: "games/bitburner" # target defaults to attribute name: "games/bitburner"
@ -121,23 +121,23 @@ rejects `..` segments. The merge step refuses to overwrite
files already present in the default dist — pick a target name that files already present in the default dist — pick a target name that
doesn't collide with existing paths (`static/`, `index.html`, etc.). doesn't collide with existing paths (`static/`, `index.html`, etc.).
The default dist ships at `hyperhive.frontend.dist` (the The default dist ships at `services.hyperhive.agent.frontend.dist` (the
`hyperhive-frontend` package output, read-only). To replace the `hyperhive-frontend` package output, read-only). To replace the
entire UI rather than layer on top, override `frontend.dist` directly. entire UI rather than layer on top, override `frontend.dist` directly.
## Connectivity overrides ## Connectivity overrides
Two `hyperhive.forge.*` / `hyperhive.matrix.*` options override where Two `services.hyperhive.agent.forge.*` / `services.hyperhive.agent.matrix.*` options override where
the per-agent daemons connect. Both rarely need changing on a standard the per-agent daemons connect. Both rarely need changing on a standard
single-host deploy, but are useful for multi-hive or custom-network single-host deploy, but are useful for multi-hive or custom-network
setups. setups.
```nix ```nix
hyperhive.forge.url = "http://forge.example:3000"; # default: null services.hyperhive.agent.forge.url = "http://forge.example:3000"; # default: null
hyperhive.matrix.url = "https://matrix.example"; # default: null services.hyperhive.agent.matrix.url = "https://matrix.example"; # default: null
``` ```
**`hyperhive.forge.url`** — base URL of the Forgejo instance. Used by **`services.hyperhive.agent.forge.url`** — base URL of the Forgejo instance. Used by
a one-shot boot unit (`tea-login`) that writes `~/.config/tea/config.yml` a one-shot boot unit (`tea-login`) that writes `~/.config/tea/config.yml`
directly from the agent's `forge-token`, so `tea` and `hive-forge` directly from the agent's `forge-token`, so `tea` and `hive-forge`
work without an interactive auth step. The unit is a no-op when work without an interactive auth step. The unit is a no-op when
@ -156,7 +156,7 @@ host's real forge URL into every agent, and refuses to write a meta
flake without one, so `null` only survives where nix evaluates the flake without one, so `null` only survives where nix evaluates the
agent modules outside a hive. agent modules outside a hive.
**`hyperhive.matrix.url`** — homeserver URL used by **`services.hyperhive.agent.matrix.url`** — homeserver URL used by
`hive-matrix-daemon` when connecting via the matrix-sdk. hive-c0re `hive-matrix-daemon` when connecting via the matrix-sdk. hive-c0re
writes it into every agent at deploy time as the gateway-routed writes it into every agent at deploy time as the gateway-routed
`chat.<swarm-domain>` URL, so isolated agents can reach the homeserver. `chat.<swarm-domain>` URL, so isolated agents can reach the homeserver.
@ -179,15 +179,15 @@ The harness installs Claude Code plugins before the serve loop opens.
Three per-agent `agent.nix` options control this: Three per-agent `agent.nix` options control this:
```nix ```nix
hyperhive.claudeMarketplaces = [ # default services.hyperhive.agent.claudeMarketplaces = [ # default
"anthropics/claude-plugins-official" "anthropics/claude-plugins-official"
"${hyperhive.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive` "${services.hyperhive.agent.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive`
]; ];
hyperhive.claudePlugins = [ # default services.hyperhive.agent.claudePlugins = [ # default
"skill-creator@claude-plugins-official" "skill-creator@claude-plugins-official"
"base@hyperhive" "base@hyperhive"
]; ];
hyperhive.claudePluginsAutoUpdate = false; # default services.hyperhive.agent.claudePluginsAutoUpdate = false; # default
``` ```
- **`claudeMarketplaces`** — list of marketplace sources passed to - **`claudeMarketplaces`** — list of marketplace sources passed to
@ -222,7 +222,7 @@ hyperhive.claudePluginsAutoUpdate = false; # default
## `cargo.shortMessages` ## `cargo.shortMessages`
```nix ```nix
hyperhive.cargo.shortMessages = true; # default services.hyperhive.agent.cargo.shortMessages = true; # default
``` ```
When enabled (the default), the harness injects a `cargo` shell When enabled (the default), the harness injects a `cargo` shell
@ -243,10 +243,10 @@ programmatically and don't pass `--message-format json` themselves.
## API-key provider (`useApiKey` / `backendEnvironmentFile`) ## API-key provider (`useApiKey` / `backendEnvironmentFile`)
```nix ```nix
hyperhive.useApiKey = true; # default: false services.hyperhive.agent.useApiKey = true; # default: false
hyperhive.backendEnvironmentFile = services.hyperhive.agent.backendEnvironmentFile =
"/agents/myagent/harness/openrouter.env"; # default: null "/agents/myagent/harness/openrouter.env"; # default: null
hyperhive.model = "anthropic/claude-3.5-sonnet"; # provider-specific model string services.hyperhive.agent.model = "anthropic/claude-3.5-sonnet"; # provider-specific model string
``` ```
Runs this agent's `claude` against an API-key provider (for example OpenRouter) Runs this agent's `claude` against an API-key provider (for example OpenRouter)

View file

@ -2,13 +2,13 @@
The harness ships an embedded MCP server (rmcp 2). A persistent The harness ships an embedded MCP server (rmcp 2). A persistent
`hive-mcp-http` daemon serves the built-in `hyperhive` surface over `hive-mcp-http` daemon serves the built-in `hyperhive` surface over
streamable HTTP (loopback, `127.0.0.1:<hyperhive.mcp.httpPort>`, streamable HTTP (loopback, `127.0.0.1:<services.hyperhive.agent.mcp.httpPort>`,
per-container private netns). Claude connects to its stable URL via per-container private netns). Claude connects to its stable URL via
`--mcp-config` rather than respawning a stdio child each turn, so the `--mcp-config` rather than respawning a stdio child each turn, so the
URL survives the per-turn claude re-spawn (and a host-side hive-c0re URL survives the per-turn claude re-spawn (and a host-side hive-c0re
restart) — there is no per-turn MCP re-registration race for the restart) — there is no per-turn MCP re-registration race for the
built-in surface. HTTP is the sole transport for it (no stdio fallback). built-in surface. HTTP is the sole transport for it (no stdio fallback).
Extra servers (`hyperhive.extraMcpServers`) pick their own transport per Extra servers (`services.hyperhive.agent.extraMcpServers`) pick their own transport per
entry (`type = "stdio" | "http"`, default `"stdio"`): `matrix` stays a entry (`type = "stdio" | "http"`, default `"stdio"`): `matrix` stays a
stdio bridge, `bash` runs its own persistent streamable-http listener stdio bridge, `bash` runs its own persistent streamable-http listener
(`hive-bash-daemon`) — same reasoning as the built-in surface. The (`hive-bash-daemon`) — same reasoning as the built-in surface. The
@ -33,7 +33,7 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
parent without hardcoding the label; the broker resolves the parent without hardcoding the label; the broker resolves the
sentinel at delivery time. Optional `in_reply_to: i64` links the sentinel at delivery time. Optional `in_reply_to: i64` links the
message to a prior id for thread rendering. Per-agent message to a prior id for thread rendering. Per-agent
`hyperhive.allowedRecipients` (default: empty = unrestricted) limits `services.hyperhive.agent.allowedRecipients` (default: empty = unrestricted) limits
which names `send` accepts — useful for sandboxing: set which names `send` accepts — useful for sandboxing: set
`[ "operator" ]` to restrict a sub-agent to operator messages only `[ "operator" ]` to restrict a sub-agent to operator messages only
(the topology parent is always reachable regardless of this list — (the topology parent is always reachable regardless of this list —

View file

@ -33,7 +33,7 @@ structurally rather than for one specific trigger. Two columns:
the parent flex container open via `align-items: stretch`-driven the parent flex container open via `align-items: stretch`-driven
height feedback. Falls back to the dimmed hyperhive mark height feedback. Falls back to the dimmed hyperhive mark
(`/favicon.svg`) on load error (`/icon` 404s when the agent has no (`/favicon.svg`) on load error (`/icon` 404s when the agent has no
`hyperhive.icon` override — there is no server-side default image). `services.hyperhive.agent.icon` override — there is no server-side default image).
- **Main column** (`.agent-header-main`, `Header.tsx`): two rows — - **Main column** (`.agent-header-main`, `Header.tsx`): two rows —
title (`◆ <label> ◆`) and, when set, the "swarm / hive" identity title (`◆ <label> ◆`) and, when set, the "swarm / hive" identity
line. Nothing else lives here; both rows are short, fixed-shape text line. Nothing else lives here; both rows are short, fixed-shape text
@ -73,7 +73,7 @@ structurally rather than for one specific trigger. Two columns:
(back-link to the host dashboard, `${dashboardBase}dashboard.html`) (back-link to the host dashboard, `${dashboardBase}dashboard.html`)
followed by this agent's `agent_links()`-sourced navigation (stats, followed by this agent's `agent_links()`-sourced navigation (stats,
screen when VNC is enabled, forge profile + config-repo mirror when screen when VNC is enabled, forge profile + config-repo mirror when
the agent has a forge account, any `hyperhive.dashboardLinks` the agent has a forge account, any `services.hyperhive.agent.dashboardLinks`
extras) — all as real `<a>` elements, not a `Dropdown`-style extras) — all as real `<a>` elements, not a `Dropdown`-style
command list, so ctrl/middle-click and "copy link address" keep command list, so ctrl/middle-click and "copy link address" keep
working. Each `AgentLink.kind` resolves differently: `container` → working. Each `AgentLink.kind` resolves differently: `container` →
@ -348,7 +348,7 @@ shaped).
`Snapshot` consumed by the `/stats` page. `all` ranges from the `Snapshot` consumed by the `/stats` page. `all` ranges from the
earliest recorded turn with an adaptive bucket width. earliest recorded turn with an adaptive bucket width.
- `GET /icon` — agent's icon as `image/svg+xml`. Returns - `GET /icon` — agent's icon as `image/svg+xml`. Returns
`/etc/hyperhive/icon.svg` (set via `hyperhive.icon` in `agent.nix`) `/etc/hyperhive/icon.svg` (set via `services.hyperhive.agent.icon` in `agent.nix`)
when present, otherwise **404** — there is no server-side default. when present, otherwise **404** — there is no server-side default.
Consumers (dashboard container row, this page's own header icon) Consumers (dashboard container row, this page's own header icon)
hit `/icon` optimistically and fall back client-side on load failure hit `/icon` optimistically and fall back client-side on load failure
@ -358,7 +358,7 @@ shaped).
renderer — deliberately thin, just enough to display the renderer — deliberately thin, just enough to display the
desktop + forward pointer + keyboard. A production-grade viewer desktop + forward pointer + keyboard. A production-grade viewer
would vendor noVNC; this file ships the minimal in-tree variant). would vendor noVNC; this file ships the minimal in-tree variant).
Only accessible when `hyperhive.gui.enable = true` in the agent's Only accessible when `services.hyperhive.agent.gui.enable = true` in the agent's
`agent.nix`; the harness shows a 🖥 screen link in the state row `agent.nix`; the harness shows a 🖥 screen link in the state row
when `gui_vnc_port` is present. A `← agent` back-link in the page when `gui_vnc_port` is present. A `← agent` back-link in the page
header returns to the main per-agent page. Controls: `⤢ fit` CSS-downscales header returns to the main per-agent page. Controls: `⤢ fit` CSS-downscales
@ -380,9 +380,9 @@ shaped).
frames to the weston VNC server at `127.0.0.1:<vnc_port>`. frames to the weston VNC server at `127.0.0.1:<vnc_port>`.
Transparent to any RFB variant. VNC port comes from the Transparent to any RFB variant. VNC port comes from the
`HIVE_GUI_VNC_PORT` env var (a fixed port set on the harness `HIVE_GUI_VNC_PORT` env var (a fixed port set on the harness
service when `hyperhive.gui.enable`; see `weston-vnc.nix`). service when `services.hyperhive.agent.gui.enable`; see `weston-vnc.nix`).
- `GET|POST /extra/<name>/…` — **extra web proxies** declared via - `GET|POST /extra/<name>/…` — **extra web proxies** declared via
`hyperhive.extraWebProxies` in `agent.nix` (serialised to the `services.hyperhive.agent.extraWebProxies` in `agent.nix` (serialised to the
`HIVE_EXTRA_WEB_PROXIES` env var as a JSON object `HIVE_EXTRA_WEB_PROXIES` env var as a JSON object
`{"<name>": "<upstream_url>"}`). Each entry mounts a transparent `{"<name>": "<upstream_url>"}`). Each entry mounts a transparent
reverse-proxy at `/extra/<name>/` that forwards every request (method, reverse-proxy at `/extra/<name>/` that forwards every request (method,

View file

@ -11,7 +11,7 @@
pointed at `HIVE_STATIC_DIR` — that fallback serves anything not pointed at `HIVE_STATIC_DIR` — that fallback serves anything not
matched by an API or action route from the dist. Dashboard dist lives at matched by an API or action route from the dist. Dashboard dist lives at
`${frontend}/dashboard`; per-agent dist is the merged `${frontend}/dashboard`; per-agent dist is the merged
`hyperhive.frontend.mergedDist` (default agent dist + per-agent `services.hyperhive.agent.frontend.mergedDist` (default agent dist + per-agent
`extraFiles` overlay). `extraFiles` overlay).
- `GET /static/*` → bundled CSS + JS produced by esbuild - `GET /static/*` → bundled CSS + JS produced by esbuild
(`frontend/packages/{dashboard,agent}/build.mjs`). Both pages (`frontend/packages/{dashboard,agent}/build.mjs`). Both pages