Watch
0
0
Fork
You've already forked hyperhive
0

docs: sort the top-level directories into five sections by what the reader wants

Every docs directory moves under 1-getting-started, 2-handbook,
4-implementation or 5-project; the options reference (section 3) comes from
the website and has no directory here. trust-boundary stays whole under the
handbook for now. web-ui, swarm and tools split between handbook and
implementation, and the command-line references move to
2-handbook/reference. Links, path references in code and nix, and the docs
index follow the moves; no page content changes.
This commit is contained in:
iris 2026-11-06 18:30:12 +01:00 • committed by mara
commit 6739bb903d
55 changed files with 736 additions and 701 deletions

View file

@ -189,7 +189,7 @@ in
not `state/` — `harness/` survives container rebuilds exactly like
`state/` does, but is never bind-mounted into another agent's
container (unlike `state/`, which a `ManageRootAgent` holder gets
read-write for recovery — see `docs/agent-lifecycle/persistence.md`'s "Cross-agent access to
read-write for recovery — see `docs/2-handbook/agent-lifecycle/persistence.md`'s "Cross-agent access to
state"), so this credential is reachable by nothing but this agent
and the host. Permissions should be `0600`, owned by the agent's
unix user. Whatever owner it was installed with, the file becomes
@ -462,8 +462,8 @@ in
# Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars,
# RuntimeDirectory, User=, standalone-eval fallbacks):
# docs/turn-loop/README.md::Harness systemd unit shape. PATH /bin
# auto-append behaviour: docs/process/gotchas.md::systemd.services.*.path
# docs/4-implementation/turn-loop/README.md::Harness systemd unit shape. PATH /bin
# auto-append behaviour: docs/5-project/process/gotchas.md::systemd.services.*.path
# appends /bin to every entry.
systemd.services.hive-agent =
let

View file

@ -46,7 +46,7 @@
# Write declared dashboardLinks to the state dir so hive-c0re can
# read them without accessing the container's /etc/ from the host.
# Best-effort oneshot (always exit 0):
# docs/process/conventions.md::Best-effort oneshot services.
# docs/5-project/process/conventions.md::Best-effort oneshot services.
systemd.services.hive-dashboard-links =
lib.mkIf (config.services.hyperhive.agent.dashboardLinks != [ ])
{

View file

@ -199,8 +199,8 @@ in
# `lib.mkForce` overrides nixpkgs's normal-priority `false` so
# in-container `nix build` invocations fall back to unsandboxed
# local builds rather than failing on the missing user-namespace.
# See `docs/process/gotchas.md::Containerized nix-daemon needs
# sandbox-fallback = true` + `docs/trust-boundary/security.md` for the rationale.
# See `docs/5-project/process/gotchas.md::Containerized nix-daemon needs
# sandbox-fallback = true` + `docs/2-handbook/trust-boundary/security.md` for the rationale.
#
# Note: with NIX_REMOTE=daemon below this becomes a no-op for the
# common case — daemon-routed builds run on the host where sandboxing
@ -264,7 +264,7 @@ in
# to be on PATH too. Only this one is actually looked up on PATH
# by claude/shell code inside the container:
# `hive-metric` (agent-emitted custom metrics CLI,
# docs/scheduler/observability.md).
# docs/4-implementation/scheduler/observability.md).
environment.systemPackages = [
config.services.hyperhive.agent.packages.hive-metric
]

View file

@ -186,7 +186,7 @@ in
# agent deployment the container can boot before the swarm has minted
# the token, so the service fires too early and exits with "no
# forge-token found". Without this path unit, nothing would ever
# re-run it. See docs/agent-lifecycle/persistence.md::forge-avatar-sync.
# re-run it. See docs/2-handbook/agent-lifecycle/persistence.md::forge-avatar-sync.
#
# PathChanged=, not PathExists=: a PathExists= condition that already
# holds re-activates the unit immediately every time the path unit
@ -213,7 +213,7 @@ in
};
# One-shot: services.hyperhive.agent.icon → Forgejo profile avatar. Shape contract:
# docs/process/conventions.md::Best-effort oneshot services.
# docs/5-project/process/conventions.md::Best-effort oneshot services.
# RemainAfterExit = false so the .path trigger above can re-fire
# this unit when the forge-token arrives after boot. The PNG is
# rasterized at build time (`iconPng`, shared shape with the matrix

View file

@ -208,7 +208,7 @@ in
loopback-only binding means no auth token is needed (same
`allowed_hosts` reasoning as `services.hyperhive.agent.mcp.httpPort`). Safe as a
single fixed default across all agents (private per-container
network namespace — see docs/networking/network.md).
network namespace — see docs/4-implementation/networking/network.md).
'';
};
@ -302,7 +302,7 @@ in
# Long-running matrix-sdk client + sync per agent. Serves the MCP
# tools directly over streamable-http + emits hyperhive wake
# signals on incoming room events via `/run/hive/mcp.sock`. See
# `docs/agent-lifecycle/persistence.md::Matrix per-agent daemon + token-arrival
# `docs/2-handbook/agent-lifecycle/persistence.md::Matrix per-agent daemon + token-arrival
# trigger` for the first-boot-ordering rationale.
systemd.services.hive-matrix-daemon = lib.mkIf matrixEnabled {
description = "long-running matrix-sdk Client + MCP daemon";
@ -434,7 +434,7 @@ in
# `tokenFile`, or a `main` from before the swarm minted it). Without this
# the daemon would exit 0 silently on first boot and the MCP
# would have no backend until next restart. See
# `docs/agent-lifecycle/persistence.md` (same section as above).
# `docs/2-handbook/agent-lifecycle/persistence.md` (same section as above).
systemd.paths.hive-matrix-daemon = lib.mkIf matrixEnabled {
description = "trigger hive-matrix-daemon when a matrix token appears";
wantedBy = [ "multi-user.target" ];

View file

@ -213,7 +213,7 @@ in
Safe as a single fixed default across all agents: each container
runs in its own private network namespace (isolation is always-on —
see docs/networking/network.md), so `127.0.0.1:<port>` is per-container-private
see docs/4-implementation/networking/network.md), so `127.0.0.1:<port>` is per-container-private
and cannot collide across agents. Override only if a container-local
service already occupies this port.
@ -235,7 +235,7 @@ in
self-healing, and loopback-only binding means no auth token is
needed (same `allowed_hosts` reasoning as `services.hyperhive.agent.mcp.httpPort`).
Safe as a single fixed default across all agents (private
per-container network namespace — see docs/networking/network.md).
per-container network namespace — see docs/4-implementation/networking/network.md).
'';
};

View file

@ -229,7 +229,7 @@ in
# home dir, chowns the bind-mounted state + `~/.claude/`, and
# (marker-guarded) moves any leftover `/root/.claude` content
# from the previous root-run shape. See
# `docs/agent-lifecycle/persistence.md::First-boot agent-user migration` for the
# `docs/2-handbook/agent-lifecycle/persistence.md::First-boot agent-user migration` for the
# step-by-step rationale; this script implements it.
system.activationScripts.hive-agent-user-migrate = lib.stringAfter [ "users" "specialfs" ] ''
homeDir=${lib.escapeShellArg homeDir}
@ -290,7 +290,7 @@ in
# The socket dir is bind-mounted from the host, where hive-priv creates
# it `0751 root`. The harness binds its sockets here as this user; the
# 0751 is what lets hive-c0re and nginx dial them without listing
# (docs/trust-boundary/boundary.md, "the per-agent socket dir"). Not
# (docs/2-handbook/trust-boundary/boundary.md, "the per-agent socket dir"). Not
# recursive: only the harness writes inside it. `test -d` and `chmod`
# follow symlinks, so a link here is refused rather than handed over.
socketDir="/run/hive-agent/$userName"

View file

@ -37,8 +37,8 @@ in
#
# Port allocation, weston bind-address quirk, PAM service name, the
# Type=simple choice, idle-time=0: all in
# docs/process/gotchas.md::Weston VNC compositor.
# Harness-side WebSocket relay shape: docs/web-ui/agent.md::Per-agent
# docs/5-project/process/gotchas.md::Weston VNC compositor.
# Harness-side WebSocket relay shape: docs/2-handbook/web-ui/agent.md::Per-agent
# endpoints (`/screen` + `/screen/ws`).
options.services.hyperhive.agent.gui.enable = lib.mkOption {

View file

@ -4,7 +4,7 @@
# builder evaluates `meta` alone and swarm-controller stores it as JSON.
# `meta` fields: `description` (required), `permissions`, `limits`,
# `inference`, in the shapes `POST /api/agents` takes them; see
# docs/swarm/archetypes.md.
# docs/2-handbook/swarm/archetypes.md.
{
reviewer = {
module = { };

View file

@ -473,7 +473,7 @@ in
};
# `hivectl` CLI reference freshness check. The committed
# markdown at `docs/tools/hivectl-cli.md` is the rendered
# markdown at `docs/2-handbook/reference/hivectl-cli.md` is the rendered
# output of the hidden `hivectl markdown-docs` subcommand
# (clap-markdown walks the binary's own command tree). This
# check regenerates it from the built binary and fails if the
@ -485,13 +485,13 @@ in
# makes this check unsatisfiable rather than merely stale.
# Regenerate locally with:
# nix build .#default
# ./result/bin/hivectl markdown-docs > docs/tools/hivectl-cli.md
# ./result/bin/hivectl markdown-docs > docs/2-handbook/reference/hivectl-cli.md
hivectl-docs = pkgs.runCommand "hivectl-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; } ''
${self.packages.${system}.default}/bin/hivectl markdown-docs > generated.md
if ! diff -u ${../docs/tools/hivectl-cli.md} generated.md; then
if ! diff -u ${../docs/2-handbook/reference/hivectl-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/hivectl-cli.md is out of date — regenerate it:" >&2
echo " nix build .#default && ./result/bin/hivectl markdown-docs > docs/tools/hivectl-cli.md" >&2
echo "ERROR: docs/2-handbook/reference/hivectl-cli.md is out of date — regenerate it:" >&2
echo " nix build .#default && ./result/bin/hivectl markdown-docs > docs/2-handbook/reference/hivectl-cli.md" >&2
exit 1
fi
touch "$out"
@ -504,10 +504,10 @@ in
# nix/packages/default.nix's comment on it).
swarmctl-docs = pkgs.runCommand "swarmctl-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; } ''
${self.packages.${system}.swarmctl}/bin/swarmctl markdown-docs > generated.md
if ! diff -u ${../docs/tools/swarmctl-cli.md} generated.md; then
if ! diff -u ${../docs/2-handbook/reference/swarmctl-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/swarmctl-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarmctl && ./result/bin/swarmctl markdown-docs > docs/tools/swarmctl-cli.md" >&2
echo "ERROR: docs/2-handbook/reference/swarmctl-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarmctl && ./result/bin/swarmctl markdown-docs > docs/2-handbook/reference/swarmctl-cli.md" >&2
exit 1
fi
touch "$out"
@ -521,10 +521,10 @@ in
pkgs.runCommand "swarm-logs-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; }
''
${self.packages.${system}.swarm-logs}/bin/swarm-logs markdown-docs > generated.md
if ! diff -u ${../docs/tools/swarm-logs-cli.md} generated.md; then
if ! diff -u ${../docs/2-handbook/reference/swarm-logs-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/swarm-logs-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarm-logs && ./result/bin/swarm-logs markdown-docs > docs/tools/swarm-logs-cli.md" >&2
echo "ERROR: docs/2-handbook/reference/swarm-logs-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarm-logs && ./result/bin/swarm-logs markdown-docs > docs/2-handbook/reference/swarm-logs-cli.md" >&2
exit 1
fi
touch "$out"
@ -536,10 +536,10 @@ in
pkgs.runCommand "swarm-metrics-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; }
''
${self.packages.${system}.swarm-metrics}/bin/swarm-metrics markdown-docs > generated.md
if ! diff -u ${../docs/tools/swarm-metrics-cli.md} generated.md; then
if ! diff -u ${../docs/2-handbook/reference/swarm-metrics-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/swarm-metrics-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarm-metrics && ./result/bin/swarm-metrics markdown-docs > docs/tools/swarm-metrics-cli.md" >&2
echo "ERROR: docs/2-handbook/reference/swarm-metrics-cli.md is out of date — regenerate it:" >&2
echo " nix build .#swarm-metrics && ./result/bin/swarm-metrics markdown-docs > docs/2-handbook/reference/swarm-metrics-cli.md" >&2
exit 1
fi
touch "$out"
@ -554,10 +554,10 @@ in
pkgs.runCommand "hive-forge-docs-fresh" { nativeBuildInputs = [ pkgs.diffutils ]; }
''
${self.packages.${system}.hive-forge}/bin/hive-forge markdown-docs > generated.md
if ! diff -u ${../docs/tools/forge-cli.md} generated.md; then
if ! diff -u ${../docs/2-handbook/reference/forge-cli.md} generated.md; then
echo "" >&2
echo "ERROR: docs/tools/forge-cli.md is out of date — regenerate it:" >&2
echo " nix build .#hive-forge && ./result/bin/hive-forge markdown-docs > docs/tools/forge-cli.md" >&2
echo "ERROR: docs/2-handbook/reference/forge-cli.md is out of date — regenerate it:" >&2
echo " nix build .#hive-forge && ./result/bin/hive-forge markdown-docs > docs/2-handbook/reference/forge-cli.md" >&2
exit 1
fi
touch "$out"

View file

@ -9,7 +9,7 @@
# `swarm.md` + `deploy.md`). The HTML + CSS for `/options/` is rendered
# downstream by the website repo, which owns the presentation and
# shares one stylesheet with the prose `/docs/` tree. Full pipeline +
# subtree-pick / output-tree rationale: docs/process/gotchas.md::Nix options
# subtree-pick / output-tree rationale: docs/5-project/process/gotchas.md::Nix options
# reference.
#
# `swarm.md`/`deploy.md` are separate pages from `host.md`:
@ -99,7 +99,7 @@ let
# Filter to a set of top-level subtree roots — keeps the rendered docs
# focused on hyperhive's surface instead of NixOS's 10k+ default
# options. Root choice matters: see docs/process/gotchas.md::Nix options
# options. Root choice matters: see docs/5-project/process/gotchas.md::Nix options
# reference for the services.hyperhive consolidation history.
pickSubtrees =
options: roots:

View file

@ -1,5 +1,5 @@
# The `bao-bootstrap` policy: what the 24h bootstrap token may do, and nothing
# else. ../../docs/getting-started/setup.md has the operator write it with the
# else. ../../docs/1-getting-started/setup.md has the operator write it with the
# root token, from the copy ./swarm-bao.nix ships at
# /etc/hyperhive/bao-bootstrap-policy.hcl; `swarm-bao-granter-role` then acts
# with it. Every other grant is written by the `bao-granter` principal this

View file

@ -397,7 +397,7 @@ in
No hive CA is generated any more: the gateway leaf is issued by the
secret store's pki mount, whose per-hive role sets its lifetime
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
definition. See docs/swarm/ca.md.
definition. See docs/4-implementation/swarm/ca.md.
''
)
(lib.mkRemovedOptionModule
@ -406,7 +406,7 @@ in
No hive CA is generated any more: the gateway leaf is issued by the
secret store's pki mount, whose per-hive role sets its lifetime
(services.hyperhive.deploy.bao.servicesPkiLeafTtlHours). Remove this
definition. See docs/swarm/ca.md.
definition. See docs/4-implementation/swarm/ca.md.
''
)
@ -728,7 +728,7 @@ in
machine-addressable DNS name. Exposed to agents as
`HYPERHIVE_HIVE_NAME`; surfaced in the dashboard chrome and per-agent
system prompt. The identity surfaces are listed in
`docs/process/conventions.md`, § Hive identity.
`docs/5-project/process/conventions.md`, § Hive identity.
'';
};

View file

@ -41,7 +41,7 @@ in
(lib.mkIf (onHiveVhost && hyperhiveCfg.deploy.matrix.enable) {
# `<hive>/matrix/*` → 301 → `gatewayHost`'s vhost/$1 (`chat.<swarm-domain>`
# by default; legacy deep-link shim for the fluffychat sub-domain).
# See `docs/networking/gateway.md`.
# See `docs/4-implementation/networking/gateway.md`.
services.hyperhive.gateway.vhosts.${hyperhiveCfg.domain}.locations."/matrix/" = {
extraConfig = ''
rewrite ^/matrix/(.*)$ ${homeserverUrl}/$1 permanent;
@ -53,7 +53,7 @@ in
services.hyperhive.gateway.vhosts.${hyperhiveCfg.domain}.locations = {
# `.well-known/matrix/{client,server}` discovery JSON, pointing
# clients and peers at `gatewayHost`: the homeserver has no other
# address. CORS `*` per matrix spec. See `docs/networking/gateway.md`.
# address. CORS `*` per matrix spec. See `docs/4-implementation/networking/gateway.md`.
"= /.well-known/matrix/client" = {
extraConfig = ''
default_type application/json;
@ -69,7 +69,7 @@ in
# explicit even when it's the HTTPS default, unlike the client
# base_url above. Without it, peers federate to <gatewayHost>:8448
# (closed) while the endpoint actually lives on the gateway's 443
# vhost. See docs/networking/gateway.md discovery flow.
# vhost. See docs/4-implementation/networking/gateway.md discovery flow.
"= /.well-known/matrix/server" = {
extraConfig = ''
default_type application/json;

View file

@ -181,7 +181,7 @@ in
# Operators granted sudoless `hivectl`. Members of `hive-admin` can
# connect to the host admin socket (group-owned by hive-admin via the
# socket unit's `SocketGroup` below) without root. That socket is *full*
# hive control (spawn/kill/destroy/deploy, docs/trust-boundary/boundary.md), so this is
# hive control (spawn/kill/destroy/deploy, docs/2-handbook/trust-boundary/boundary.md), so this is
# an explicit opt-in allowlist — empty by default (root-only).
users.groups.hive-admin = {
members = controllerCfg.adminUsers;
@ -210,7 +210,7 @@ in
# The gateway nginx is always the sole external entry point (it runs
# alongside hyperhive), so the per-agent web-port range stays closed on
# the host firewall. See `docs/networking/gateway.md::Firewall posture (host-level)`.
# the host firewall. See `docs/4-implementation/networking/gateway.md::Firewall posture (host-level)`.
# NB: `services.hyperhive.domain` is required when hyperhive is
# enabled — the canonical assertion lives in `hive-network.nix` (the
@ -356,7 +356,7 @@ in
# `0660 root:hive-admin` — group-owned by `hive-admin` so operators in
# that group (services.hyperhive.deploy.hive-controller.adminUsers) drive `hivectl`
# without root; an empty adminUsers list leaves the group memberless,
# so it stays effectively root-only. See docs/trust-boundary/boundary.md.
# so it stays effectively root-only. See docs/2-handbook/trust-boundary/boundary.md.
SocketMode = "0660";
SocketGroup = "hive-admin";
# Only consulted when the directory is absent at socket-unit

View file

@ -67,7 +67,7 @@ in
// {
# Identity env vars threaded into c0re's own service env and
# forwarded by meta.rs into every sub-agent's harness env —
# full chain in docs/process/conventions.md::Hive identity. `domain` is
# full chain in docs/5-project/process/conventions.md::Hive identity. `domain` is
# required (asserted in hive-network.nix), so it's always set.
HYPERHIVE_HIVE_DOMAIN = config.services.hyperhive.domain;
}
@ -122,7 +122,7 @@ in
# hive-c0re (forge/mod.rs `forge_http_base()`) and forwarded to
# agents via meta.rs for their forge-notify client. The forge is
# mandatory, so this is unconditional (the whole env block is already
# gated on hyperhive being enabled). See `docs/networking/gateway.md::HIVE_FORGE_URL`.
# gated on hyperhive being enabled). See `docs/4-implementation/networking/gateway.md::HIVE_FORGE_URL`.
HIVE_FORGE_URL = "http://${config.services.hyperhive.swarm.forge.domain}";
# The one blacklist of names an agent may not take — see

View file

@ -234,7 +234,7 @@ in
`hivectl` (and thus the whole hive) without `sudo`.
This is a real privilege grant: the admin socket is *full* hive
control — spawn / kill / destroy / deploy — see `docs/trust-boundary/boundary.md`.
control — spawn / kill / destroy / deploy — see `docs/2-handbook/trust-boundary/boundary.md`.
Keep the list to trusted operators. Empty (the default) keeps the
socket root-only, as before.
'';

View file

@ -4,7 +4,7 @@
# dashboard re-themes with no operator action and no npm/esbuild
# rebuild (a pure file-copy over the prebuilt dist). `colors.css` is
# the entire swap contract — `theme.css` derives every semantic var
# from the 16 base16 slots (see docs/web-ui/css-vars.md). The guarded
# from the 16 base16 slots (see docs/4-implementation/web-ui/css-vars.md). The guarded
# access makes this a clean no-op when stylix isn't imported into the
# host config. Exposed as the read-only `deploy.hive-controller.servedFrontend` option.
#

View file

@ -161,7 +161,7 @@ in
# netns, on the one host with `deploy.forgejo.enable`. Agents reach it
# at `forge.<domain>` via the gateway. State at
# `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/` survives
# restart. See `docs/networking/gateway.md::hive-forge container shape`.
# restart. See `docs/4-implementation/networking/gateway.md::hive-forge container shape`.
imports = [
./service.nix
@ -319,7 +319,7 @@ in
# `server_name = forge.domain`, proxies all `/` → forgejo. Tuned for
# git: `client_max_body_size 1G`, `proxy_read_timeout 1h` (multi-GB
# clones). SSH stays direct on `forge.sshPort`. See
# `docs/networking/gateway.md`.
# `docs/4-implementation/networking/gateway.md`.
services.hyperhive.gateway.vhosts = {
"${cfg.domain}" = {
locations = {
@ -963,7 +963,7 @@ in
# container, where forgejo already stores it at rest in the
# login-source row — root and forgejo are the only
# principals here, and root can read the resting copy
# anyway. Accepted deliberately (see docs/swarm/), not
# anyway. Accepted deliberately (see docs/2-handbook/swarm/), not
# overlooked; upstream gap filed.
# An ARRAY, not a string. A string of arguments has to be
# word-split at the call site to become argv, and splitting

View file

@ -55,7 +55,7 @@ in
Public hostname for the forge. Doubles as both the forgejo
`DOMAIN` setting (clone URLs forgejo advertises) AND the
gateway vhost server-name (sub-domain routing — see
`docs/networking/gateway.md`).
`docs/4-implementation/networking/gateway.md`).
Defaults to `forge.''${services.hyperhive.swarm.domain}` — the
swarm's domain, not this hive's, because a swarm runs **one**
@ -85,7 +85,7 @@ in
Defaults to `https://''${cfg.domain}`, the gateway vhost. When
`null`, the dashboard **hides** forge links rather than
guessing one — see `docs/web-ui/dashboard.md::Container row` for
guessing one — see `docs/2-handbook/web-ui/dashboard.md::Container row` for
the rationale (a link built from the operator's own browser
hostname + a container port is only an accident away from
wrong on any deployment that isn't plain localhost).

Some files were not shown because too many files have changed in this diff Show more