diff --git a/CLAUDE.md b/CLAUDE.md index 77862443..0cb89726 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,12 +10,12 @@ when you need depth on a subsystem. This file is the index. the env var — inside an agent container `localhost` is the *agent*, not the forge, so a hardcoded loopback address fails to connect. - Operator/agent trust-boundary design: - **[docs/boundary.md](docs/boundary.md)** (`area/ops` issues + **[docs/trust-boundary/boundary.md](docs/trust-boundary/boundary.md)** (`area/ops` issues for the deployment/gateway/privsep work — the separator is a **slash**, and `--label area:ops` now fails with `did you mean "area/ops"?`). - Agent trust model (trust boundary, prompt-injection threat model, capability = accepted risk), credential isolation + sandbox threat model: - **[docs/security.md](docs/security.md)**. + **[docs/trust-boundary/security.md](docs/trust-boundary/security.md)**. ## Repo map @@ -38,7 +38,7 @@ hand-maintained per-file tree drifts out of sync with the code. - **`hivectl/`** — standalone operator CLI (`hivectl` binary). Talks to the `hive-c0re` daemon over the host admin socket (`hive-host-sock` wire types) — does NOT link `hive-c0re`. Full, always-current verb - reference (CI-enforced against the clap tree, see `docs/conventions.md`): + reference (CI-enforced against the clap tree, see `docs/process/conventions.md`): [`docs/tools/hivectl-cli.md`](docs/tools/hivectl-cli.md). - **`hive-agent/`**, **`hive-agent-mcp/`** — in-container harness, two sibling crates for every agent (not a @@ -80,7 +80,7 @@ hand-maintained per-file tree drifts out of sync with the code. - **`hive-priv/`** — minimal root privileged-helper, socket-activated at `/run/hive/priv.sock`; performs the few root operations (bind-mount edits, nsenter) the unprivileged `hive-c0re` delegates to it. See - `docs/boundary.md`. + `docs/trust-boundary/boundary.md`. - **`hive-forge/`** — `hive-forge` Forgejo CLI wrapper; one module per verb under `src/verbs/`. - **`hive-forge-notify/`** — per-agent notification poller daemons; turns @@ -156,7 +156,7 @@ hand-maintained per-file tree drifts out of sync with the code. written in place** — one file, shared with `swarm-authelia-bridge`; see that crate's README for what both writers must uphold. Full, always-current verb reference (CI-enforced against the clap tree, same pattern as - `hivectl`'s — see `docs/conventions.md`): + `hivectl`'s — see `docs/process/conventions.md`): [`docs/tools/swarmctl-cli.md`](docs/tools/swarmctl-cli.md). ### External dependencies with no directory here @@ -191,91 +191,17 @@ hand-maintained per-file tree drifts out of sync with the code. ## Reading paths -Pick the doc that matches your task. None depend on the others — -read them à la carte. - -- **"How do I bring a fresh hive online (first-run hivectl - bootstrap)?"** → [`docs/setup.md`](docs/setup.md). -- **"What does the dashboard look like?"** → - [`docs/web-ui.md`](docs/web-ui.md) (index; sub-pages: - [`shape`](docs/web-ui/shape.md), - [`dashboard`](docs/web-ui/dashboard.md), - [`agent`](docs/web-ui/agent.md)). -- **"How does the per-agent terminal classify + colour - events?"** → [`docs/terminal-rendering.md`](docs/terminal-rendering.md). -- **"How does claude get its prompt and what tools does it have?"** → - [`docs/turn-loop/`](docs/turn-loop/README.md) (index: the loop, binary shape, - turn outcomes; sub-pages: - [`claude-invocation`](docs/turn-loop/claude-invocation.md), - [`config`](docs/turn-loop/config.md), [`mcp`](docs/turn-loop/mcp.md)). -- **"How do config changes flow from manager to operator to - container?"** → [`docs/approvals.md`](docs/approvals.md). -- **"What state survives destroy / purge / restart?"** → - [`docs/persistence.md`](docs/persistence.md). -- **"Naming, commit style, wire protocol, the `data-async` - pattern."** → [`docs/conventions.md`](docs/conventions.md). -- **"Why does the nspawn flag look like that?"** → - [`docs/gotchas.md`](docs/gotchas.md). -- **"What nginx vhosts does the gateway serve? How does matrix - discovery work?"** → [`docs/gateway.md`](docs/gateway.md). -- **"How do per-agent forge accounts work? What does forge_notify - poll + how does it format wake messages?"** → - [`docs/forge.md`](docs/forge.md). -- **"What verbs does `hive-forge` support? How do I post a comment, - upload an attachment, manage subscriptions?"** → - [`docs/tools/forge.md`](docs/tools/forge.md). -- **"What does `hivectl` do? How do I provision a forge/matrix account, - manage gateway users, restart containers, or drop into an agent shell?"** → - [`docs/tools/hivectl.md`](docs/tools/hivectl.md). -- **"How does the matrix-tuwunel container work? What about - fluffychat-web and per-agent matrix accounts?"** → - [`docs/matrix.md`](docs/matrix.md). -- **"How do I give an agent a GitHub account (`gh` + `git push`)? - How is the PAT injected?"** → [`docs/github.md`](docs/github.md). -- **"How does DNS resolution work in agent containers? What's the - bridge network for?"** → [`docs/network.md`](docs/network.md). -- **"How do I connect two hives into a swarm? How do I declare peer - hives and configure TLS trust?"** → - [`docs/swarm/`](docs/swarm/README.md). -- **"Which deployment shapes are supported — all-local, services on the - controller host, fully spread out?"** → - [`docs/swarm/services.md`](docs/swarm/services.md#deployment-shapes). -- **"Where do agent snapshots go? How does the swarm's `btrfs receive` - endpoint authenticate a pushing hive, and what does a snapshot - actually contain?"** → - [`docs/snapshot-store.md`](docs/snapshot-store.md). -- **"What is the job queue, as a general idea?"** → - [`docs/jobq.md`](docs/jobq.md) (operator-facing, no hive-c0re specifics). -- **"How does the rebuild queue work? What are the concrete step kinds, - queue sources, scheduler internals?"** → - [`docs/coordinator.md`](docs/coordinator.md). -- **"How does the CI runner work? What's the auto-registration flow?"** → - [`docs/ci.md`](docs/ci.md). -- **"What is `/knowledge`? How does the hive-wide knowledge repo sync, - and how do I contribute a document?"** → - [`docs/knowledge.md`](docs/knowledge.md). -- **"How do I export Claude Code metrics (tokens, cost, tool calls) to - a Prometheus/Grafana collector? What OTEL options are available?"** → - [`docs/observability.md`](docs/observability.md). -- **"What does a PR review verdict actually gate? Should a reviewer - wait for CI before approving? What does arming auto-merge commit an - operator to?"** → [`docs/pr-review-gate.md`](docs/pr-review-gate.md). +**[`docs/README.md`](docs/README.md) is the single index** — grouped by topic +(getting started, agent lifecycle, trust boundary, integrations, networking, +scheduler, process). Read it instead of a second copy here; this file used to +carry its own parallel "Reading paths" list, and the two drifted out of sync +with each other. ## Conventions & process -The docs below own the details — this section just points at them. - -- **Commit style, naming, identity, reconcile verb:** → - [`docs/conventions.md`](docs/conventions.md). - **Never add `#[allow(clippy::…)]`** — fix the lint instead (extract a helper, add backticks, etc.). Details + worked examples: → - [`docs/conventions.md`](docs/conventions.md#building--local-checks). -- **NixOS / nspawn quirks** (bind mounts, conf flags, etc.): → - [`docs/gotchas.md`](docs/gotchas.md). -- **Turn loop, sentinels (rate-limit, auth-failed), context - window:** → [`docs/turn-loop/`](docs/turn-loop/README.md). -- **Two-step spawn, approval flow, flake.lock validation:** → - [`docs/approvals.md`](docs/approvals.md). + [`docs/process/conventions.md`](docs/process/conventions.md#building--local-checks). - **Pre-push lint hook** (catches tracker-tag and comment-block failures before CI does — install once per clone): `ln -sf ../../scripts/pre-push .git/hooks/pre-push` diff --git a/docs/README.md b/docs/README.md index 5a8c37eb..78950a78 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,55 +11,53 @@ declarations. ## Getting started -- **Bringing a fresh hive online?** → [`setup.md`](setup.md) (first-run - `hivectl` bootstrap). +- **Bringing a fresh hive online?** → [`getting-started/setup.md`](getting-started/setup.md) + (first-run `hivectl` bootstrap). - **What does the dashboard look like, and how do I use it?** → [`web-ui/`](web-ui/README.md) — the operator-facing starting point; its own sub-pages ([`shape`](web-ui/shape.md), [`dashboard`](web-ui/dashboard.md), [`agent`](web-ui/agent.md), - [`css-vars`](web-ui/css-vars.md)) go deeper into implementation. + [`css-vars`](web-ui/css-vars.md), [`terminal-rendering`](web-ui/terminal-rendering.md)) + go deeper into implementation. - **What tools does an agent (or the operator) have available?** → [`tools/`](tools/README.md) — `hivectl` (yours) plus every agent's MCP tool surface (bash, forge, lifecycle, matrix, scheduling). -## Dashboard & agent UI internals - -- **How does the per-agent terminal classify + colour events?** → - [`terminal-rendering.md`](terminal-rendering.md). - -## Turn loop, config, approvals +## Agent lifecycle +- **How do config changes flow from manager to operator to container?** → + [`agent-lifecycle/approvals.md`](agent-lifecycle/approvals.md) (two-step spawn, approval + state machine, `flake.lock` validation). +- **What state survives destroy / purge / restart?** → + [`agent-lifecycle/persistence.md`](agent-lifecycle/persistence.md). +- **Who can do what to whom — agent hierarchy and privilege?** → + [`agent-lifecycle/agent-hierarchy.md`](agent-lifecycle/agent-hierarchy.md). - **How does claude get its prompt, and what tools does it have?** → [`turn-loop/`](turn-loop/README.md) — the loop, binary shape, turn outcomes; sub-pages: [`claude-invocation`](turn-loop/claude-invocation.md), [`config`](turn-loop/config.md), [`mcp`](turn-loop/mcp.md). -- **How do config changes flow from manager to operator to container?** → - [`approvals.md`](approvals.md) (two-step spawn, approval state machine, - `flake.lock` validation). -- **What state survives destroy / purge / restart?** → - [`persistence.md`](persistence.md). ## Trust boundary & security - **What's the operator/agent trust boundary? What's a capability?** → - [`boundary.md`](boundary.md). + [`trust-boundary/boundary.md`](trust-boundary/boundary.md). - **Agent trust model, prompt-injection threat model, credential - isolation?** → [`security.md`](security.md). -- **Who can do what to whom — agent hierarchy and privilege?** → - [`agent-hierarchy.md`](agent-hierarchy.md). + isolation?** → [`trust-boundary/security.md`](trust-boundary/security.md). ## Accounts & integrations - **How do per-agent forge accounts work? What does `forge_notify` poll, - and how does it format wake messages?** → [`forge.md`](forge.md) (the - hive's own Forgejo); [`tools/forge.md`](tools/forge.md) for the + and how does it format wake messages?** → [`integrations/forge.md`](integrations/forge.md) + (the hive's own Forgejo); [`tools/forge.md`](tools/forge.md) for the `hive-forge` CLI verbs agents actually call. - **How does the matrix-tuwunel container work? Multiple accounts per - agent?** → [`matrix.md`](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 `hyperhive.matrixAccounts`. - **How do I give an agent a GitHub account (`gh` + `git push`)? How is - the PAT injected?** → [`github.md`](github.md). + the PAT injected?** → [`integrations/github.md`](integrations/github.md). +- **What is `/knowledge`? How does the hive-wide knowledge repo sync, + and how do I contribute a document?** → [`integrations/knowledge.md`](integrations/knowledge.md). - **What does `hivectl` do? Provisioning, gateway users, container shells?** → [`tools/hivectl.md`](tools/hivectl.md) (the curated guide); [`tools/hivectl-cli.md`](tools/hivectl-cli.md) for the exhaustive, @@ -68,25 +66,25 @@ declarations. ## Networking & swarms - **What nginx vhosts does the gateway serve? How does matrix - discovery work?** → [`gateway.md`](gateway.md). + discovery work?** → [`networking/gateway.md`](networking/gateway.md). - **How does DNS resolution work in agent containers? What's the - bridge network for?** → [`network.md`](network.md). + bridge network for?** → [`networking/network.md`](networking/network.md). - **How do I connect two hives into a swarm?** → [`swarm/`](swarm/README.md) (peer hives, TLS trust). - **Where do agent snapshots go? How does the swarm's `btrfs receive` endpoint authenticate a pushing hive?** → - [`snapshot-store.md`](snapshot-store.md). + [`networking/snapshot-store.md`](networking/snapshot-store.md). ## Scheduler, CI, observability - **What is the job queue, as a general idea (not hive-c0re specifics)?** → - [`jobq.md`](jobq.md) — operator-facing, no implementation detail. + [`scheduler/jobq.md`](scheduler/jobq.md) — operator-facing, no implementation detail. - **How does the rebuild queue work? What are the concrete step kinds, - queue sources, scheduler internals?** → [`coordinator.md`](coordinator.md). + queue sources, scheduler internals?** → [`scheduler/coordinator.md`](scheduler/coordinator.md). - **How does the CI runner work? What's the auto-registration flow?** → - [`ci.md`](ci.md). + [`scheduler/ci.md`](scheduler/ci.md). - **How do I export Claude Code metrics (tokens, cost, tool calls) to - Prometheus/Grafana?** → [`observability.md`](observability.md). + Prometheus/Grafana?** → [`scheduler/observability.md`](scheduler/observability.md). ## Crate reference @@ -98,8 +96,8 @@ declarations. ## Process & conventions - **Naming, commit style, wire protocol, the `data-async` pattern?** → - [`conventions.md`](conventions.md). -- **Why does the nspawn flag look like that?** → [`gotchas.md`](gotchas.md) + [`process/conventions.md`](process/conventions.md). +- **Why does the nspawn flag look like that?** → [`process/gotchas.md`](process/gotchas.md) (bind mounts, conf flags, other NixOS/nspawn quirks). -- **What is `/knowledge`? How does the hive-wide knowledge repo sync, - and how do I contribute a document?** → [`knowledge.md`](knowledge.md). +- **What does a PR review verdict actually gate?** → + [`process/pr-review-gate.md`](process/pr-review-gate.md). diff --git a/docs/agent-hierarchy.md b/docs/agent-lifecycle/agent-hierarchy.md similarity index 99% rename from docs/agent-hierarchy.md rename to docs/agent-lifecycle/agent-hierarchy.md index ff08bcf2..a949eb28 100644 --- a/docs/agent-hierarchy.md +++ b/docs/agent-lifecycle/agent-hierarchy.md @@ -212,7 +212,7 @@ path = [ "/run/wrappers" "/run/current-system/sw" ]; `/run/wrappers` (not `/run/wrappers/bin`) comes first so setuid wrappers — notably `sudo` — resolve before bare nix-store binaries; see -[`docs/gotchas.md`](gotchas.md) ("`systemd.services.*.path` appends +[`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 @@ -243,4 +243,4 @@ uid 0 and have the setuid bit set." (`$HIVE_FORGE_URL/hyperhive/hyperhive/issues/363`) - Audit table source: milestone comment (`$HIVE_FORGE_URL/hyperhive/hyperhive/issues/361#issuecomment-3335`) -- Operator/agent trust boundary (orthogonal axis): [`boundary.md`](boundary.md) +- Operator/agent trust boundary (orthogonal axis): [`boundary.md`](../trust-boundary/boundary.md) diff --git a/docs/approvals.md b/docs/agent-lifecycle/approvals.md similarity index 100% rename from docs/approvals.md rename to docs/agent-lifecycle/approvals.md diff --git a/docs/persistence.md b/docs/agent-lifecycle/persistence.md similarity index 98% rename from docs/persistence.md rename to docs/agent-lifecycle/persistence.md index b95291d9..04bf7672 100644 --- a/docs/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -77,8 +77,8 @@ power-intent registry: This is the durable power *intent* the job queue reconciles the observed container state against; intent survives hive-c0re restarts even though in-flight queue work doesn't. See - [`docs/coordinator.md`'s Desired-state - section](coordinator.md#desired-state-spec-vs-status) for who + [`docs/scheduler/coordinator.md`'s Desired-state + section](../scheduler/coordinator.md#desired-state-spec-vs-status) for who writes and reads it and how reconciliation works. Retention: @@ -126,7 +126,7 @@ history sooner than a quiet one. The trade-off (accepted): a misbehaving harness could now skip its own cleanup, which the old host-side sweep was meant to prevent — but a compromised harness is already inside the container trust boundary -([`docs/security.md`](security.md)), and these are ephemeral local +([`docs/trust-boundary/security.md`](../trust-boundary/security.md)), and these are ephemeral local artifacts, so cleaning them up where they live is the honest fix. Path overridable via `HYPERHIVE_EVENTS_DB` (for dev / no-`/harness` @@ -236,7 +236,7 @@ Survives destroy/recreate, gone on `--purge`. Empty marker file. Its presence parks the agent's turn loop: the harness keeps serving its web UI and MCP daemons but drives no turns, and inbox messages queue unacked until it's removed (see -[turn loop](turn-loop/README.md#the-loop)). +[turn loop](../turn-loop/README.md#the-loop)). Unusually, it's read and written from **both** sides of the harness bind-mount, and that's the whole design: the harness stats it @@ -383,7 +383,7 @@ Contents: (`{ "alice": "root", "bob": "alice", "root": null }`). Written by `topology::apply_set_parent` (the pure move-validating transform) via `meta::bulk_commit_topology` (the committer — see the - `Reparent` node in [`docs/coordinator.md`](coordinator.md)); read by + `Reparent` node in [`docs/scheduler/coordinator.md`](../scheduler/coordinator.md)); read by the dashboard, the renderer, and `` / `` recipient resolution. - `tool-groups.json` — per-agent MCP tool group grants @@ -466,7 +466,7 @@ subvolume creation/deletion above. This is the same subvolume `hivectl agent subvol snapshot push` sends to the swarm's snapshot store — see -[`docs/snapshot-store.md`](snapshot-store.md) for what a pushed +[`docs/snapshot-store.md`](../networking/snapshot-store.md) for what a pushed snapshot contains and how the store authenticates a sender. ## `/var/lib/swarm-controller/` (swarm-controller host only) diff --git a/docs/setup.md b/docs/getting-started/setup.md similarity index 94% rename from docs/setup.md rename to docs/getting-started/setup.md index 78de01bf..49911e8b 100644 --- a/docs/setup.md +++ b/docs/getting-started/setup.md @@ -11,7 +11,7 @@ inside an agent container); the `request_*` steps run from ruth's own turn via the MCP tools. **Bringing up a hive that does not host its own swarm services?** Read -[`swarm/secrets.md`](swarm/secrets.md) first. Everything below assumes +[`swarm/secrets.md`](../swarm/secrets.md) first. Everything below assumes each credential is generated where it is read, which is true on an all-local deploy and not otherwise — that page says which files an operator has to place, and where. @@ -105,7 +105,7 @@ than amend — adding the group afterwards is `swarmctl user update mara --add-group admins`. Detail, including what the password is and why this stays manual: -[`swarm/sso.md`](swarm/sso.md). +[`swarm/sso.md`](../swarm/sso.md). ### 5 · Swarm UI (only when `deploy.swarm-ui`, on by default with the controller) @@ -121,7 +121,7 @@ decide whether you can actually open it: anywhere else it needs a real DNS record like any other public name. Detail, including why reachability is deliberately not the access -control: [`swarm/ui.md`](swarm/ui.md). +control: [`swarm/ui.md`](../swarm/ui.md). ### 6 · Matrix @@ -163,7 +163,7 @@ request_init_config(name: "iris") # the operator reviews + approves it — no MCP tool call. ``` -See [`approvals.md`](approvals.md) for the full flow. +See [`approvals.md`](../agent-lifecycle/approvals.md) for the full flow. ### 8 · Useful host commands @@ -183,7 +183,7 @@ hivectl open forge # Forgejo hivectl open matrix # Matrix GUI (fluffychat) ``` -See [`tools/hivectl.md`](tools/hivectl.md) for every `hivectl` verb. +See [`tools/hivectl.md`](../tools/hivectl.md) for every `hivectl` verb. ## Security notes @@ -192,12 +192,12 @@ See [`tools/hivectl.md`](tools/hivectl.md) for every `hivectl` verb. creds (the core token, the matrix admin token) live on the host. - All config changes (forge PRs on `agent-configs/`) go through operator approval — agents can't unilaterally rebuild containers, by design. - See [`boundary.md`](boundary.md) and [`security.md`](security.md). + See [`boundary.md`](../trust-boundary/boundary.md) and [`security.md`](../trust-boundary/security.md). - **Telemetry ingest is authenticated per hive**, and the `hive` label comes from which hive authenticated rather than from the payload — so no hive can report metrics as another. A first-run all-local hive gets this with nothing to configure; joining a swarm you don't host needs one secret copied across. - See [`observability.md`](observability.md#authenticated-ingest). + See [`observability.md`](../scheduler/observability.md#authenticated-ingest). Once the hive is running, ruth records anything it needs to remember across restarts in `/agents/ruth/state/notes.md`. diff --git a/docs/forge.md b/docs/integrations/forge.md similarity index 98% rename from docs/forge.md rename to docs/integrations/forge.md index 40445952..43fd8488 100644 --- a/docs/forge.md +++ b/docs/integrations/forge.md @@ -4,7 +4,7 @@ Private Forgejo instance running in a nixos-container, used as the swarm's persistent code-collaboration surface (issues, PRs, reviews, attachments). Configured via `services.hyperhive.swarm.forge.*`. Container shape, ROOT_URL / sub-domain routing, and operator-vs-in-cluster URL -handling live in [`docs/gateway.md`](gateway.md); this file owns the +handling live in [`docs/networking/gateway.md`](../networking/gateway.md); this file owns the per-agent integration story and the notification pump that wakes each agent on relevant activity. @@ -69,7 +69,7 @@ Two things live in the `agent-configs` Forgejo organization: agent's config. (Agents remain read-only collaborators on `core/meta`.) hive-c0re also references this repo as the agent's **persistent meta flake input** (`agent-.url = git+http:///agent-configs/.git`; - see [approvals.md § Meta flake](approvals.md)), fetching it as the `core` + see [approvals.md § Meta flake](../agent-lifecycle/approvals.md)), fetching it as the `core` user via a git credential helper that reads the live forge-core token — so the config lives on the forge, not a hand-synced local checkout. - The dashboard links each container's "config" anchor to this @@ -332,4 +332,4 @@ delivers every unread notification it's handed. Bounding the firehose (dropping broad repo watches an agent doesn't need) is done explicitly via a hive-forge CLI subscription verb, not by the poller guessing which watches to drop. See the `subscription` verb in -[`docs/tools/forge.md`](tools/forge.md). +[`docs/tools/forge.md`](../tools/forge.md). diff --git a/docs/github.md b/docs/integrations/github.md similarity index 98% rename from docs/github.md rename to docs/integrations/github.md index 9dfecf4a..45a79996 100644 --- a/docs/github.md +++ b/docs/integrations/github.md @@ -46,7 +46,7 @@ hivectl github set-token --token # inline (visible in shell his Either path has hive-c0re delegate the write to hive-priv, which stores the file `0600` owned by the agent (so the container can read it) — the same credential-injection path as forge/matrix tokens. See -[hivectl → GitHub](tools/hivectl.md#github). +[hivectl → GitHub](../tools/hivectl.md#github). ## Security @@ -55,7 +55,7 @@ credential-injection path as forge/matrix tokens. See workflow needs. Agents have passwordless sudo, so a compromised or hallucinating agent can act as the account within the token's scopes; scope is the real blast-radius limiter, and the container boundary is - the enforcement. See [security.md](security.md). + the enforcement. See [security.md](../trust-boundary/security.md). Everything below this point is implementation detail (how the agent actually uses the token, and the notification poller's internals). diff --git a/docs/knowledge.md b/docs/integrations/knowledge.md similarity index 100% rename from docs/knowledge.md rename to docs/integrations/knowledge.md diff --git a/docs/matrix.md b/docs/integrations/matrix.md similarity index 98% rename from docs/matrix.md rename to docs/integrations/matrix.md index 17f59636..fdad90f3 100644 --- a/docs/matrix.md +++ b/docs/integrations/matrix.md @@ -4,11 +4,11 @@ Private Matrix homeserver (matrix-tuwunel — the conduwuit successor) wrapped in a nixos-container, plus optional fluffychat-web client at `chat./` (the `gatewayHost` vhost). Configured via `services.hyperhive.swarm.matrix.*`; vhost routing lives in -[`gateway.md`](gateway.md). +[`gateway.md`](../networking/gateway.md). ## Container shape -Same shape as [`gateway.md::hive-forge container shape`](gateway.md): +Same shape as [`gateway.md::hive-forge container shape`](../networking/gateway.md): - Container name `hive-matrix` (not `h-*`) so c0re's lifecycle scanner ignores it; operator manages via the standard @@ -202,7 +202,7 @@ at the protocol level from first boot (`allow_federation = true`) but no remote homeserver is trusted until listed here. For a closed single-hive deployment the default empty list is correct — add peer hive domains here when connecting hives into a swarm (see -[`docs/swarm/`](swarm/README.md)). +[`docs/swarm/`](../swarm/README.md)). **`maxRequestSize`** (default `20_000_000` bytes = 20 MB) — maximum size of a single matrix client request body. Matches the matrix-spec @@ -237,7 +237,7 @@ homeserver at all rather than of a setting: `server_name`'s own bogus-value guard lives in `hive-network.nix` (`services.hyperhive.domain != null`), not here — see -[`docs/network.md`](network.md). +[`docs/networking/network.md`](../networking/network.md). ## fluffychat-web build fixes diff --git a/docs/gateway.md b/docs/networking/gateway.md similarity index 98% rename from docs/gateway.md rename to docs/networking/gateway.md index 91c4861e..b3128d83 100644 --- a/docs/gateway.md +++ b/docs/networking/gateway.md @@ -19,7 +19,7 @@ Single nginx in front of every hyperhive web surface. Runs on the **host**, next The authelia vhost is declared only by the host that **runs** authelia, not by every hive that uses it — a client hive knows the swarm's `authelia.url` but must not answer for a name it doesn't serve. Its server name is exactly `swarm.authelia.domain`: authelia validates `authelia_url ⊂ session cookie domain` at startup, so a near-miss is a container that refuses to boot. It carries no `auth_basic` — the login page must not sit behind the login mechanism it replaces — and sets the four `X-Forwarded-{Proto,Host,Uri,For}` headers, since authelia decides by the *original* request rather than the hop it sees. -⚠️ **A `502` from this vhost usually means authelia has no users yet, not that the proxy is misconfigured.** Authelia treats an empty user store as a fatal startup error, so an enabled-but-unbootstrapped swarm crash-loops the container while the vhost in front of it works perfectly. Check `journalctl -M swarm-authelia -u authelia-swarm` before suspecting anything here; the bootstrap step is in [`swarm/sso.md`](swarm/sso.md). +⚠️ **A `502` from this vhost usually means authelia has no users yet, not that the proxy is misconfigured.** Authelia treats an empty user store as a fatal startup error, so an enabled-but-unbootstrapped swarm crash-loops the container while the vhost in front of it works perfectly. Check `journalctl -M swarm-authelia -u authelia-swarm` before suspecting anything here; the bootstrap step is in [`swarm/sso.md`](../swarm/sso.md). Per-agent UIs stay sub-path, forge and matrix get sub-domains — see [Sub-domain shape (rationale)](#sub-domain-shape-rationale) below for why. @@ -108,7 +108,7 @@ now set unconditionally for every agent. The mechanism: nginx reaches `web.sock` through `o=--x` (traverse) and the socket's own `0666`. The gateway is one of three principals sharing that dir and does not own its ownership rules — see - [`docs/boundary.md`](boundary.md#the-per-agent-socket-dir). + [`docs/trust-boundary/boundary.md`](../trust-boundary/boundary.md#the-per-agent-socket-dir). 3. **Marker gate**. After successful `bind_unix`, the harness drops `/hyperhive-socket-bound` next to the socket. c0re's `agent_sockets::write` filters its JSON map by marker presence — @@ -216,11 +216,11 @@ swarm-services fallback below is for. **Why a CA, not a bare leaf**: a bare self-signed leaf is its own trust anchor, so every regeneration is a new anchor every consumer must re-trust — and a runtime-generated leaf can't be wired into an agent's build-time trust store at all. With a stable CA, agents and federation peers trust it *once*; leaf rotation never re-breaks them. -**What consumers trust**: `trust-bundle.pem` in the same state dir, not `ca.pem`. The hive CA is itself issued under the swarm root ([`swarm/ca.md`](swarm/ca.md) has the hierarchy), and an intermediate is not a chain a verifier can terminate at — so the bundle carries the hive CA plus whatever it is rooted at. nginx is handed the leaf with the hive CA appended for the same reason. Everything that trusts the hive's TLS reads the bundle: agents (via `security.pki.certificateFiles`), the CI and forge containers, and a federating peer. +**What consumers trust**: `trust-bundle.pem` in the same state dir, not `ca.pem`. The hive CA is itself issued under the swarm root ([`swarm/ca.md`](../swarm/ca.md) has the hierarchy), and an intermediate is not a chain a verifier can terminate at — so the bundle carries the hive CA plus whatever it is rooted at. nginx is handed the leaf with the hive CA appended for the same reason. Everything that trusts the hive's TLS reads the bundle: agents (via `security.pki.certificateFiles`), the CI and forge containers, and a federating peer. **Why on by default**: matrix-dart-sdk (FluffyChat's SDK) hardcodes `https:///.well-known/matrix/client` for homeserver discovery and refuses to fall back to plain http. Without TLS the browser client cannot bootstrap. -**Cert shape**: leaf subject CN = bare hive domain; subjectAltName is `` plus wildcard `*.`, so all current and future sub-domain vhosts validate under the same leaf + the hive CA. A swarm service whose name is *not* under this hive's domain cannot be added here — the hive CA is name-constrained to ``, and a violating SAN invalidates the whole leaf, not just that name. Those names get the swarm-services leaf instead ([`swarm/ca.md`](swarm/ca.md)). +**Cert shape**: leaf subject CN = bare hive domain; subjectAltName is `` plus wildcard `*.`, so all current and future sub-domain vhosts validate under the same leaf + the hive CA. A swarm service whose name is *not* under this hive's domain cannot be added here — the hive CA is name-constrained to ``, and a violating SAN invalidates the whole leaf, not just that name. Those names get the swarm-services leaf instead ([`swarm/ca.md`](../swarm/ca.md)). **Rotation**: `hive-tls-ca.service` is idempotent — it re-signs the leaf when it is missing or within 30 days of expiry, always under the same CA (so consumer trust is undisturbed). The CA itself is regenerated only if missing or already expired. To force a leaf rotation, delete `gateway.pem` under the state dir and restart the unit, then reload `nginx`. diff --git a/docs/network.md b/docs/networking/network.md similarity index 99% rename from docs/network.md rename to docs/networking/network.md index 4a9aa079..c6b34978 100644 --- a/docs/network.md +++ b/docs/networking/network.md @@ -70,7 +70,7 @@ The flows, end to end: the host forwards + masquerades out its uplink. - **Swarm** — peer hives connect over the `wg-hive` WireGuard mesh and reach each other's gateway/forge across it - ([`docs/swarm/`](swarm/README.md)). + ([`docs/swarm/`](../swarm/README.md)). - **Control plane (no network)** — per-agent broker socket `/run/hive/mcp.sock`, privileged helper `/run/hive/priv.sock`, operator admin `/run/hyperhive/host.sock`, and the per-agent UI diff --git a/docs/snapshot-store.md b/docs/networking/snapshot-store.md similarity index 98% rename from docs/snapshot-store.md rename to docs/networking/snapshot-store.md index 7cfedaa2..d3c3e2a6 100644 --- a/docs/snapshot-store.md +++ b/docs/networking/snapshot-store.md @@ -35,7 +35,7 @@ services.hyperhive.swarm.wireguard = { The store host is a swarm member like any other: it gets an entry in `services.hyperhive.swarm.hives`, the same directory every host holds. See -[swarm/](swarm/README.md) for the mesh itself. +[swarm/](../swarm/README.md) for the mesh itself. Note that the mesh is gated on `swarm.wireguard.enable`, **not** on `c0re.enable` --- a store host runs no hive and would otherwise get no @@ -185,7 +185,7 @@ the access-review list. The snapshot covers an agent's **state subvolume**, which is the parent of `state/`, `claude/` and `harness/` (see [`docs/persistence.md`'s btrfs subvolume -section](persistence.md#btrfs-subvolumes-for-varlibhyperhiveagentsname) +section](../agent-lifecycle/persistence.md#btrfs-subvolumes-for-varlibhyperhiveagentsname) for how and when that subvolume is created). Consequences: - The Claude session (`claude/`) travels, so a restored agent keeps its diff --git a/docs/conventions.md b/docs/process/conventions.md similarity index 100% rename from docs/conventions.md rename to docs/process/conventions.md diff --git a/docs/gotchas.md b/docs/process/gotchas.md similarity index 99% rename from docs/gotchas.md rename to docs/process/gotchas.md index 693e1844..50638da7 100644 --- a/docs/gotchas.md +++ b/docs/process/gotchas.md @@ -178,7 +178,7 @@ durable knowledge here (`notes.md`, anything else) — the harness's own internal files (`hyperhive-events.sqlite`, `hyperhive-turn-stats.sqlite`, `hyperhive-model`) live in the separate `harness` dir instead, so they don't clutter what claude sees as "my notes dir" (see -[`docs/persistence.md`](persistence.md)). Survives `destroy`/recreate +[`docs/agent-lifecycle/persistence.md`](../agent-lifecycle/persistence.md)). Survives `destroy`/recreate alongside the claude dir. ## Networking & ports @@ -361,7 +361,7 @@ own self-consistent object store and is immune to the issue. ### `hive-forge`: prefer over raw curl pipelines -Full CLI reference: [`docs/tools/forge.md`](tools/forge.md). +Full CLI reference: [`docs/tools/forge.md`](../tools/forge.md). Never use raw `curl` for forge access. ## GUI (weston/VNC) diff --git a/docs/pr-review-gate.md b/docs/process/pr-review-gate.md similarity index 100% rename from docs/pr-review-gate.md rename to docs/process/pr-review-gate.md diff --git a/docs/ci.md b/docs/scheduler/ci.md similarity index 100% rename from docs/ci.md rename to docs/scheduler/ci.md diff --git a/docs/coordinator.md b/docs/scheduler/coordinator.md similarity index 100% rename from docs/coordinator.md rename to docs/scheduler/coordinator.md diff --git a/docs/jobq.md b/docs/scheduler/jobq.md similarity index 97% rename from docs/jobq.md rename to docs/scheduler/jobq.md index 79a83e81..aa167fbe 100644 --- a/docs/jobq.md +++ b/docs/scheduler/jobq.md @@ -32,7 +32,7 @@ using the same engine for its own unrelated queue. ## Watching it happen Each **row** you see in a queue view (the **BU1LDS** page's R3BU1LD QU3U3 -— see [`web-ui/dashboard.md`](web-ui/dashboard.md) — and swarm-ui's +— see [`web-ui/dashboard.md`](../web-ui/dashboard.md) — and swarm-ui's `/jobs` page both render the same underlying graph) is one job; the rows nested under it are that job's steps, in order (occasionally a couple run side by side). A step shows one of: diff --git a/docs/observability.md b/docs/scheduler/observability.md similarity index 99% rename from docs/observability.md rename to docs/scheduler/observability.md index 18d71712..d2e60d23 100644 --- a/docs/observability.md +++ b/docs/scheduler/observability.md @@ -20,13 +20,13 @@ services.hyperhive.otel = { leaves the swarm — optional, because the swarm's own metrics store (`deploy.victoriametrics`) is a destination in its own right. With both, telemetry goes to both. See -[`swarm/services.md`](swarm/services.md#metrics-victoriametrics--grafana). +[`swarm/services.md`](../swarm/services.md#metrics-victoriametrics--grafana). **There is exactly one way telemetry leaves a hive: through the collector that `enable` starts on the host.** Agents never talk to `endpoint` themselves — they export unauthenticated to a bridge address only their own containers can reach. That collector forwards to the swarm's -([`swarm/services.md`](swarm/services.md#telemetry-collector-otel)), which is +([`swarm/services.md`](../swarm/services.md#telemetry-collector-otel)), which is the single process holding the upstream credential and the only writer to the swarm's store. No agent holds a copy, and neither does this hive. diff --git a/docs/swarm/README.md b/docs/swarm/README.md index 7e5976f4..0a7bb4f2 100644 --- a/docs/swarm/README.md +++ b/docs/swarm/README.md @@ -75,7 +75,7 @@ domains can share one. `hiveName` surfaces in the same places but is distinction — one names this hive, the other names the group it belongs to. -See `docs/conventions.md` § Hive identity for the env-var chain +See `docs/process/conventions.md` § Hive identity for the env-var chain and `qualify()` / `qualified_label()` semantics. ## Swarm CA @@ -166,7 +166,7 @@ evaluates cleanly points at a real machine that isn't the one you meant. key must never enter the store), so there is no build-time name for it. Bridging that needs a runtime mechanism and is tracked as its own issue. Until then, federation needs CA-issued certs (ACME). See - `docs/matrix.md` for federation firewall + TLS requirements. + `docs/integrations/matrix.md` for federation firewall + TLS requirements. 3. **WireGuard mesh** (optional) — `swarm.wireguard.enable` reads each entry's `wireguardPublicKey`/`wireguardEndpoint`/`wireguardAddress` @@ -279,7 +279,7 @@ port}` tells this hive where the swarm's `btrfs receive` endpoint is, so It is genuinely swarm-scoped rather than per-peer — a swarm has exactly one store, because the receiver keys destinations by *agent* so a migrating agent keeps one unbroken incremental chain. See -[snapshot-store.md](../snapshot-store.md). +[snapshot-store.md](../networking/snapshot-store.md). ## Swarm controller @@ -385,7 +385,7 @@ Nothing to configure. The hooks are registered only when this host also serves the swarm UI vhost — that is what publishes the endpoint, and a hook the forge cannot reach would collect failed deliveries while looking healthy. The HMAC secret is generated on first start and kept -(see [`docs/persistence.md`](../persistence.md)). +(see [`docs/agent-lifecycle/persistence.md`](../agent-lifecycle/persistence.md)). To check it is working, push to `internal/knowledge` and look for `webhook: verified delivery` in `journalctl -u swarm-controller`. A @@ -393,13 +393,13 @@ refused delivery logs `webhook: refused delivery` with the reason. ## Cross-references -- `docs/snapshot-store.md` — the swarm's `btrfs receive` endpoint, and +- `docs/networking/snapshot-store.md` — the swarm's `btrfs receive` endpoint, and the `swarm.snapshotStore` option that points a hive at it -- `docs/conventions.md` § Hive identity — env vars, qualified labels -- `docs/matrix.md` — matrix federation, TLS cert auto-generation, +- `docs/process/conventions.md` § Hive identity — env vars, qualified labels +- `docs/integrations/matrix.md` — matrix federation, TLS cert auto-generation, firewall posture - `docs/swarm/ui.md` — the swarm-wide hive roster, now the operator surface for "what hives exist" (superseded the per-hive dashboard's old "peer hives" display) -- `docs/gateway.md` — nginx vhosts and the `.well-known/matrix/` +- `docs/networking/gateway.md` — nginx vhosts and the `.well-known/matrix/` auto-discovery scheme diff --git a/docs/swarm/services.md b/docs/swarm/services.md index e16aa6eb..29c7f61c 100644 --- a/docs/swarm/services.md +++ b/docs/swarm/services.md @@ -183,5 +183,5 @@ refused at eval — a tier that receives samples and drops them looks healthy while losing data. Agent-side configuration, and what a hive's own collector does, are in -[`../observability.md`](../observability.md). +[`../scheduler/observability.md`](../scheduler/observability.md). diff --git a/docs/swarm/sso.md b/docs/swarm/sso.md index 26d5c7ea..b8df3bf8 100644 --- a/docs/swarm/sso.md +++ b/docs/swarm/sso.md @@ -16,7 +16,7 @@ authelia binds loopback only. The **gateway** on the host running it publishes it as `auth.` — vhost, dnsmasq record and TLS name all follow `deploy.authelia`, so there is nothing to turn on separately. (Details, including why a client hive must not declare that -vhost: [`../gateway.md`](../gateway.md).) +vhost: [`../networking/gateway.md`](../networking/gateway.md).) **Authelia does not start until at least one user exists.** The user store is generated empty — deliberately, since seeding a default account diff --git a/docs/swarm/ui.md b/docs/swarm/ui.md index f7c02846..bfc2dcae 100644 --- a/docs/swarm/ui.md +++ b/docs/swarm/ui.md @@ -38,7 +38,7 @@ fine and still gets bounced. swarmctl user add --group admins ``` -`admins` deliberately, not a new word: [`../setup.md`](../setup.md) has +`admins` deliberately, not a new word: [`../getting-started/setup.md`](../getting-started/setup.md) has told every operator to create exactly that group since the bootstrap step existed, so an account made by following the guide already passes. This is the first rule that *consumes* a group name — inventing a second one @@ -111,4 +111,4 @@ popover. ## Cross-references - [`sso.md`](sso.md) — the authelia instance itself, and the user store. -- [`../gateway.md`](../gateway.md) — the full vhost map and TLS modes. +- [`../networking/gateway.md`](../networking/gateway.md) — the full vhost map and TLS modes. diff --git a/docs/tools/hivectl.md b/docs/tools/hivectl.md index 7f7b5d76..be47a76f 100644 --- a/docs/tools/hivectl.md +++ b/docs/tools/hivectl.md @@ -6,7 +6,7 @@ admin socket `/run/hyperhive/host.sock`, which is root-only by default — so it needs `sudo` unless you grant sudoless access by listing your login user in `services.hyperhive.c0re.adminUsers` (adds you to the `hive-admin` group that owns the socket; see -[`docs/boundary.md`](../boundary.md#host-admin-socket-access-hivectl)). +[`docs/trust-boundary/boundary.md`](../trust-boundary/boundary.md#host-admin-socket-access-hivectl)). Available via the `hive-c0re` package in the host NixOS config. Unlike the `hive-c0re` daemon subcommands (which go through the broker), @@ -96,7 +96,7 @@ Write an operator-supplied GitHub personal access token (PAT) into an 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 is for an existing GitHub account. A CLI alternative to the dashboard -credentials tab; the [GitHub integration](../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. ```bash @@ -108,7 +108,7 @@ hivectl github set-token damocles --token # inline (visible in shell hi hive-priv — the same credential-injection path as forge/matrix tokens. The `gh` wrapper / git credential helper read it live, so a freshly-set or rotated PAT takes effect with no rebuild or restart. Refuses an empty - token. See [github.md](../github.md) for the full flow + security notes. + token. See [github.md](../integrations/github.md) for the full flow + security notes. ## Gateway @@ -193,7 +193,7 @@ hivectl agent sock set-limits --reset # drop all overrides Overrides the hive-wide `services.hyperhive.agentCpuQuota` / `agentMemoryMax` for one agent, persisted to `meta/resource-limits.json` (see -[`persistence.md`](../persistence.md)). Values are systemd's +[`persistence.md`](../agent-lifecycle/persistence.md)). Values are systemd's `CPUQuota=` / `MemoryMax=` syntax: a percentage (`400%` = four full cores) for CPU; a size (`8G`), a percentage of physical RAM, or `infinity` for memory. Both are validated before they're persisted — @@ -276,7 +276,7 @@ hivectl agent iris watch # tail iris's live events; Ctrl-C to stop Dials the same unix socket the gateway's nginx `proxy_pass`es through (`/run/hive-agent//web.sock` — see [Per-agent unix-socket -upstream](../gateway.md#per-agent-unix-socket-upstream)) directly and +upstream](../networking/gateway.md#per-agent-unix-socket-upstream)) directly and speaks a bare HTTP/1.1 request for the agent's existing `/events/stream` SSE endpoint over it. No gateway hop, no daemon round-trip for the stream itself — the daemon socket is only used for the "does this agent @@ -289,7 +289,7 @@ Prints one compact line per event — reuses the `_icon`/`_summary` fields the harness already stamps onto stream-json events for the web UI (`stream_enrich.rs`), so tool calls and turn markers read as short glyph-prefixed lines instead of raw JSON. Not an attempt at the web -UI's full collapsible-details rendering (`docs/terminal-rendering.md`) +UI's full collapsible-details rendering (`docs/web-ui/terminal-rendering.md`) — that's presentation for a browser, this is a `tail -f`. ## Open diff --git a/docs/tools/lifecycle.md b/docs/tools/lifecycle.md index 4e0e2681..ab854417 100644 --- a/docs/tools/lifecycle.md +++ b/docs/tools/lifecycle.md @@ -52,7 +52,7 @@ which creates the container from that config. Subsequent config changes go through a **forge PR** on the agent's `agent-configs/` repo (queues a `MergeConfigPr` approval on open/update — no MCP tool involved), not a tool call. See -`docs/approvals.md`. +`docs/agent-lifecycle/approvals.md`. Fails if a proposed config repo for `name` already exists. `name` is ≤ 9 characters. @@ -78,6 +78,6 @@ agents after the approval resolves. ## See also -- [`docs/approvals.md`](../approvals.md) — full approval flow, kinds, +- [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md) — full approval flow, kinds, helper events (`config_ready`, `approval_resolved`), flake.lock validation. diff --git a/docs/tools/matrix.md b/docs/tools/matrix.md index 2a4a91f2..d2634d7c 100644 --- a/docs/tools/matrix.md +++ b/docs/tools/matrix.md @@ -130,7 +130,7 @@ pending invites, resolve_invite to accept or reject`. The keyed todo is cleared when a `resolve_invite` (or `join_room`) call resolves the invite. -See [`docs/matrix.md`](../matrix.md) for the homeserver setup, +See [`docs/integrations/matrix.md`](../integrations/matrix.md) for the homeserver setup, provisioning flow, and federation config. ## Extra MCP servers (per-agent) diff --git a/docs/tools/scheduling.md b/docs/tools/scheduling.md index 4a4a90ac..4bab6bcd 100644 --- a/docs/tools/scheduling.md +++ b/docs/tools/scheduling.md @@ -92,5 +92,5 @@ lifecycle events, or another container's boot log. - `remind` (no-approval self-wake path) — documented in [`docs/turn-loop/`](../turn-loop/README.md). -- [`docs/approvals.md`](../approvals.md) — approval flow for +- [`docs/agent-lifecycle/approvals.md`](../agent-lifecycle/approvals.md) — approval flow for `request_schedule_prompt`. diff --git a/docs/boundary.md b/docs/trust-boundary/boundary.md similarity index 97% rename from docs/boundary.md rename to docs/trust-boundary/boundary.md index 27ba8c01..f4ec9fce 100644 --- a/docs/boundary.md +++ b/docs/trust-boundary/boundary.md @@ -77,7 +77,7 @@ The boundary rests on three layers: 3. **Privsep** — defence in depth on the core process; `hive-c0re` runs as the unprivileged `hive-core` user and delegates root operations to `hive-priv`, a narrow socket-activated helper. See - [`docs/security.md`](security.md) for the privilege boundary table. + [`docs/trust-boundary/security.md`](security.md) for the privilege boundary table. ### hive-priv socket activation @@ -130,7 +130,7 @@ them and the rest of the host. That costs no network isolation: nginx binds the host's `:80`/`:443` and reaches `localhost` upstreams, which a netns would have to be opened up for anyway. 🔑 It does mean nothing *implicitly* scopes the privileged reload verb — -see [`docs/security.md`](security.md#hive-c0re-privilege-separation) for +see [`docs/trust-boundary/security.md`](security.md#hive-c0re-privilege-separation) for how `PrivRequest::ReloadGatewayNginx`'s containment works. ⚠️ Contrast `/shared`, which *is* sticky world-writable (`1777`): it has diff --git a/docs/security.md b/docs/trust-boundary/security.md similarity index 98% rename from docs/security.md rename to docs/trust-boundary/security.md index 3a142cc0..b1ee3946 100644 --- a/docs/security.md +++ b/docs/trust-boundary/security.md @@ -71,7 +71,7 @@ checkpoints**, not about sandboxing the agent from its own tools: technical control. - **Approvals** — config changes, schedule additions, and other blast-radius-y operations route through the operator approval queue - (see [`approvals.md`](approvals.md)). + (see [`approvals.md`](../agent-lifecycle/approvals.md)). ### Capability = accepted risk @@ -158,7 +158,7 @@ The blast radius also has a **network** dimension. hive-ci runs its unsandboxed builds of untrusted PR code in its own private netns behind the hive bridge: a build reaches the forge only through the gateway and cannot reach host-loopback services — including the core dashboard at `127.0.0.1:`, which -has no application-layer auth of its own (see [`docs/ci.md`](ci.md)). The `0600` +has no application-layer auth of its own (see [`docs/scheduler/ci.md`](../scheduler/ci.md)). The `0600` token policy bounds file reads; network isolation bounds network reach. **What is NOT exposed**: diff --git a/docs/turn-loop/README.md b/docs/turn-loop/README.md index 48c0f413..d1a8a66c 100644 --- a/docs/turn-loop/README.md +++ b/docs/turn-loop/README.md @@ -14,7 +14,7 @@ agents) runs: queued and unacked, so a resume drains the backlog instead of losing it; reminders and todo wakes buffer in their channels. Set it with `hivectl agent pause` or the dashboard toggle; see - [persistence](../persistence.md#-harnesspaused-per-agent). + [persistence](../agent-lifecycle/persistence.md#-harnesspaused-per-agent). 1. Long-poll `Recv` on its socket. The host-side broker (`broker.rs::recv_blocking_batch`) returns immediately if there's a pending message, otherwise waits up to 30 s for a broker `Sent` @@ -91,21 +91,21 @@ doc for the trait shape. `serve_main` reads `HIVE_PORT` (default `DEFAULT_WEB_PORT`) + `HIVE_LABEL` (default `"hive"` for standalone runs; the meta flake sets it unconditionally for any container-deployed agent; -see `docs/conventions.md::Hive identity` for the env stack), +see `docs/process/conventions.md::Hive identity` for the env stack), opens turn-stats sqlite, prepares the on-boot files (see [claude-invocation](claude-invocation.md#on-boot-files)), installs claude plugins, spawns `web_ui::serve` + `vacuum::run`, and either drops into `serve_loop` directly (`Online`) or parks on the login flow first (`NeedsLogin`). Forge notifications are polled by their own process, not this loop — see `hive-forge-notify` in -[`forge.md`](../forge.md). +[`forge.md`](../integrations/forge.md). Boot also opens the todos store and the socket in-container producers dial. Matrix / bash / forge-notify daemons and the in-process `disk_watch` todo producer (low state-disk space) are the built-in producers, but the socket accepts any `subsystem` marker — a user-configured MCP server can push its own todos the same way. See -[`docs/persistence.md`](../persistence.md#state-dirs-per-agent) for +[`docs/agent-lifecycle/persistence.md`](../agent-lifecycle/persistence.md#state-dirs-per-agent) for what each built-in todo producer watches and how the store + `get_loose_ends` merge work. diff --git a/docs/turn-loop/claude-invocation.md b/docs/turn-loop/claude-invocation.md index 681050d5..16217d23 100644 --- a/docs/turn-loop/claude-invocation.md +++ b/docs/turn-loop/claude-invocation.md @@ -31,7 +31,7 @@ this package moves fast, the operator can pin one hive-wide with into each agent's flake, and `claude` on PATH becomes a symlink to it instead of the container's own `claude-code` — so there's only ever one `claude` in the container. Agents pick up a new build on their -next rebuild, not live. See docs/gotchas.md::`claude-code` is unfree. +next rebuild, not live. See docs/process/gotchas.md::`claude-code` is unfree. Hive-enforced settings ship at `/etc/claude-code/managed-settings.json` (claude-code's canonical managed-settings path — precedence #1, diff --git a/docs/turn-loop/config.md b/docs/turn-loop/config.md index a6765679..ef3b7343 100644 --- a/docs/turn-loop/config.md +++ b/docs/turn-loop/config.md @@ -59,7 +59,7 @@ sudo rejection rather than silently succeeding — easier to audit. `hyperhive.user.uid`, `hyperhive.user.gid`, and `hyperhive.user.name` are the companion options; see -`docs/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. ## Dashboard links @@ -284,7 +284,7 @@ KEYS The file lives in the agent's bind-mounted **harness** dir, not `state/` — `harness/` survives container rebuilds exactly like `state/` does, but (unlike `state/`) is never bind-mounted into a parent agent's container -(see `docs/persistence.md`'s "Parent access to child state"), so an API +(see `docs/agent-lifecycle/persistence.md`'s "Parent access to child state"), so an API key placed here is reachable by nothing but this agent and the host. ⚠️ Verified end-to-end against OpenRouter has not happened as of this diff --git a/docs/turn-loop/mcp.md b/docs/turn-loop/mcp.md index 42026548..60414fc0 100644 --- a/docs/turn-loop/mcp.md +++ b/docs/turn-loop/mcp.md @@ -70,7 +70,7 @@ which still wakes a turn (the todo-wake path — see [Turn outcomes](README.md#turn-outcomes)) but via a generic "call `get_loose_ends`" prompt rather than the event body itself. Full payload shapes and routing logic in -[`docs/approvals.md` § Helper events](../approvals.md#helper-events-to-the-submitting-agent). +[`docs/agent-lifecycle/approvals.md` § Helper events](../agent-lifecycle/approvals.md#helper-events-to-the-submitting-agent). **Inbox** (`inbox` group): `get_loose_ends(agent?)`, `cancel_loose_end(kind, id)`, `remind(message, delay_seconds? | @@ -148,7 +148,7 @@ already in flight. - **Capability-gated** — `get_host_journal` (requires `read_host_journal` capability set via the P3RM1SS10NS tab; orthogonal to tool groups). Full list of capabilities and their - effects in [`docs/conventions.md#capabilities`](../conventions.md). + effects in [`docs/process/conventions.md#capabilities`](../process/conventions.md). Also documented in [`docs/tools/scheduling.md`](../tools/scheduling.md). - **Matrix MCP + extra servers** — `mcp__matrix__*` tools and per-agent extra MCP config. See diff --git a/docs/web-ui.md b/docs/web-ui.md index b8cb8f99..834f5863 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -3,7 +3,7 @@ Two web surfaces share the same skeleton: the dashboard (port 7000) and the per-agent UIs (each container's port is a deterministic hash in :8100-8999 — see -[`gotchas.md#web-ui-ports-collide-on-hash`](gotchas.md#web-ui-ports-collide-on-hash) +[`gotchas.md#web-ui-ports-collide-on-hash`](process/gotchas.md#web-ui-ports-collide-on-hash) for the mechanics and the collision caveat). Both are SPAs — `GET /` returns a static shell, `/api/state` returns JSON, JS renders. No full-page reloads. @@ -47,7 +47,7 @@ dashboard rather than how it's built, start at [`web-ui/dashboard.md`](web-ui/dashboard.md) (Dashboard endpoints, Dashboard event channel). - **"How does the per-agent terminal render tool calls?"** → - [`terminal-rendering.md`](terminal-rendering.md) (full row + [`terminal-rendering.md`](web-ui/terminal-rendering.md) (full row taxonomy + dispatch walkthrough); for a high-level summary see [`web-ui/agent.md`](web-ui/agent.md) (Per-stream rendering). - **"What slash commands does the agent accept?"** → diff --git a/docs/web-ui/agent.md b/docs/web-ui/agent.md index 6d802daa..7156589e 100644 --- a/docs/web-ui/agent.md +++ b/docs/web-ui/agent.md @@ -200,7 +200,7 @@ new" pill are the shared terminal-pane mechanics described in [Shape](shape.md#shared-terminal-pane) — this page's log is one instance of that same factory. -Per-stream rendering (see [`docs/terminal-rendering.md`](../terminal-rendering.md) for +Per-stream rendering (see [`docs/web-ui/terminal-rendering.md`](terminal-rendering.md) for the full row taxonomy and dispatch logic): - `Stream` `tool_use` → diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md index 5a5cc902..f7c415e6 100644 --- a/docs/web-ui/dashboard.md +++ b/docs/web-ui/dashboard.md @@ -175,7 +175,7 @@ rides the same row but is fed by a separate ~5 min background `du` sampler last-sampled value (`null` until the first sample). Network is intentionally omitted — agents share the host netns, so there is no per-container net counter (per-agent network needs the netns-isolation roadmap in -`docs/network.md`). +`docs/networking/network.md`). Hive infrastructure services (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`) have no dashboard panel — `hivectl stop`/`start`/`restart` @@ -362,7 +362,7 @@ account appears only once it has been provisioned through the form. ### GITHUB tab Provision a single per-agent GitHub personal access token (see -[`docs/github.md`](../github.md) for the injection + `gh`/git-push +[`docs/integrations/github.md`](../integrations/github.md) for the injection + `gh`/git-push mechanics). No login flow — the operator pastes an existing PAT for a dedicated bot account, with a security-warning banner (dedicated account + minimally-scoped token) and a link to @@ -640,7 +640,7 @@ the matrix module exposes). The unified nginx-front re-root to `https://chat.${hyperhive.swarm.domain}` + `.well-known/matrix/client` -auto-discovery lives in `docs/gateway.md` (atlas's lane). +auto-discovery lives in `docs/networking/gateway.md` (atlas's lane). ## FL0W page (`/flow.html`) @@ -782,7 +782,7 @@ front (`StateSnapshot.gateway_enabled`, sourced from the base URL is `/agent/` (same origin, gateway proxies to the per-agent harness — TCP or unix-domain depending on the agent's `HIVE_WEB_SOCKET` opt-in, see -`docs/gateway.md::Per-agent unix-socket upstream`). +`docs/networking/gateway.md::Per-agent unix-socket upstream`). Gateway-off (legacy / local dev): base URL is `http://:` (direct TCP fallback). Forge links resolve against `state.forge_public_url` (sourced from diff --git a/docs/web-ui/shape.md b/docs/web-ui/shape.md index c1f3d409..2159f968 100644 --- a/docs/web-ui/shape.md +++ b/docs/web-ui/shape.md @@ -301,8 +301,8 @@ The per-agent UI optionally binds a `UnixListener` instead of TCP when `HIVE_WEB_SOCKET` is set — the unix-socket transition mechanics (per-agent `/run/hive-agent//` bind-mount, `.bound` marker filtering, `agent-sockets.json` consumer on the -gateway side) live in [`docs/gateway.md::Per-agent unix-socket -upstream`](gateway.md). The env var is opt-in per agent so the +gateway side) live in [`docs/networking/gateway.md::Per-agent unix-socket +upstream`](../networking/gateway.md). The env var is opt-in per agent so the two modes coexist while sub-agents transition. ## Per-agent relative paths diff --git a/docs/terminal-rendering.md b/docs/web-ui/terminal-rendering.md similarity index 100% rename from docs/terminal-rendering.md rename to docs/web-ui/terminal-rendering.md diff --git a/hive-agent-mcp/src/mcp/render.rs b/hive-agent-mcp/src/mcp/render.rs index 7656c0a8..c16afb7e 100644 --- a/hive-agent-mcp/src/mcp/render.rs +++ b/hive-agent-mcp/src/mcp/render.rs @@ -321,7 +321,7 @@ pub(super) struct MatrixRoomUnread { /// on (`hyperhive.mcp.matrixHttpPort`'s nix default). Overridable via /// `HIVE_MATRIX_HTTP_PORT` for parity with the port options nix already /// exposes; unset in practice since a single fixed port is safe (each -/// agent container is its own network namespace — see docs/network.md). +/// agent container is its own network namespace — see docs/networking/network.md). const DEFAULT_MATRIX_HTTP_PORT: u16 = 8792; /// Short request timeout for the local status query below — this must diff --git a/hive-agent/src/harness_state.rs b/hive-agent/src/harness_state.rs index 6ef30968..24dee94b 100644 --- a/hive-agent/src/harness_state.rs +++ b/hive-agent/src/harness_state.rs @@ -69,7 +69,7 @@ pub(crate) fn persist_effort(level: &str) -> std::io::Result<()> { // (`hyperhive-rate-limited`, `hyperhive-needs-login`) that grew organically // and had no shared schema. A single JSON file is self-documenting, atomic // to write, and cheaper for hive-c0re to read on each sweep (one fopen vs -// two stat calls). See `docs/persistence.md::Harness state files`. +// two stat calls). See `docs/agent-lifecycle/persistence.md::Harness state files`. // // Legacy sentinel files written by older harness builds are still honoured // by `read_harness_state` so in-place upgrades don't lose state (the new diff --git a/hive-agent/src/identity.rs b/hive-agent/src/identity.rs index 85c36619..8f4f2cf2 100644 --- a/hive-agent/src/identity.rs +++ b/hive-agent/src/identity.rs @@ -1,7 +1,7 @@ //! Agent identity helpers — short label + hive-qualified long name + //! human display names for the hive and swarm. Full env var surface + //! domain-vs-name distinction documented in -//! `docs/conventions.md::Hive identity (label + domain + display names)`. +//! `docs/process/conventions.md::Hive identity (label + domain + display names)`. use std::env; diff --git a/hive-agent/src/mcp_config.rs b/hive-agent/src/mcp_config.rs index 8304d173..fb44f208 100644 --- a/hive-agent/src/mcp_config.rs +++ b/hive-agent/src/mcp_config.rs @@ -37,7 +37,7 @@ pub const ALLOWED_BUILTIN_TOOLS: &[&str] = &["Edit", "Glob", "Grep", "Read", "Sk /// Env var written by the meta renderer with a comma-separated list of /// `hive_sh4re::permissions::ToolGroup` `snake_case` names (e.g. `"messaging,inbox,meta"`). /// When present, the harness expands the groups into per-tool allow entries -/// instead of using the hardcoded flavor default. See `docs/conventions.md::Tool groups`. +/// instead of using the hardcoded flavor default. See `docs/process/conventions.md::Tool groups`. const TOOL_GROUPS_ENV: &str = "HIVE_TOOL_GROUPS"; /// `HIVE_CAPABILITIES` env var injected by `meta::render_flake` when the @@ -164,7 +164,7 @@ mod extra_server_gate_tests { /// MCP tools claude is allowed to call without prompting, derived from /// the supplied tool groups. Adding a new `#[tool]` fn to a server impl /// requires updating the matching `ToolGroup::tools()` slice in hive-sh4re -/// (single source of truth). See `docs/conventions.md::Tool groups`. +/// (single source of truth). See `docs/process/conventions.md::Tool groups`. #[must_use] pub fn allowed_mcp_tools(groups: &[hive_sh4re::permissions::ToolGroup]) -> Vec { // Collect all tool names, deduplicating while preserving order. diff --git a/hive-agent/src/otel_turn_metrics.rs b/hive-agent/src/otel_turn_metrics.rs index 0f968f11..86f89a7b 100644 --- a/hive-agent/src/otel_turn_metrics.rs +++ b/hive-agent/src/otel_turn_metrics.rs @@ -1,6 +1,6 @@ //! Per-turn OTEL metric export for the fields Claude Code's own built-in //! OTEL integration cannot know about. Claude's own export already covers -//! token usage, cost, and tool-call counts (see `docs/observability.md`) — +//! token usage, cost, and tool-call counts (see `docs/scheduler/observability.md`) — //! duplicating those here under different metric names would just give a //! collector two disagreeing series for the same number. This module emits //! only the harness-only concepts: wall-clock turn duration as *this harness* @@ -149,7 +149,7 @@ fn build_provider(interval: Duration) -> anyhow::Result { // OTLP client is required — `PeriodicReader` drives export from a // background thread with no Tokio reactor), and same rationale as // `hive-metric` for the resource (the SDK reads `OTEL_RESOURCE_ATTRIBUTES` - // — set container-wide, see `docs/observability.md::Built-in resource + // — set container-wide, see `docs/scheduler/observability.md::Built-in resource // labels` — on its own; passing an explicit `Resource` here would need to // duplicate `agent`/`hive`/`swarm` this crate has no other reason to know). let exporter = MetricExporter::builder() diff --git a/hive-agent/src/paths.rs b/hive-agent/src/paths.rs index e97cd92c..fdc4e968 100644 --- a/hive-agent/src/paths.rs +++ b/hive-agent/src/paths.rs @@ -7,7 +7,7 @@ //! Claude credentials live at `$HOME/.claude` (resolves to //! `/home//.claude` because the harness service runs as a //! non-root unix user matching the agent label — see -//! `docs/persistence.md::First-boot agent-user migration`). +//! `docs/agent-lifecycle/persistence.md::First-boot agent-user migration`). //! //! All three paths can be overridden via env vars (`HYPERHIVE_STATE_DIR`, //! `HYPERHIVE_HARNESS_DIR`, `HYPERHIVE_CLAUDE_DIR`) for dev / test scenarios. diff --git a/hive-agent/src/vacuum.rs b/hive-agent/src/vacuum.rs index fa37184f..53730a50 100644 --- a/hive-agent/src/vacuum.rs +++ b/hive-agent/src/vacuum.rs @@ -11,7 +11,7 @@ //! Trade-off (accepted — issue tracker "perms borked"): a misbehaving harness //! could skip its own cleanup, which the host-side version was meant to //! prevent. But a compromised harness is already inside the container trust -//! boundary (`docs/security.md`), and these are ephemeral local artifacts — so +//! boundary (`docs/trust-boundary/security.md`), and these are ephemeral local artifacts — so //! the honest fix is to clean them up where they live. use std::path::Path; diff --git a/hive-agent/src/web_ui/mod.rs b/hive-agent/src/web_ui/mod.rs index 1c65a448..cb9d9a53 100644 --- a/hive-agent/src/web_ui/mod.rs +++ b/hive-agent/src/web_ui/mod.rs @@ -72,7 +72,7 @@ struct AppState { /// `HIVE_WEB_SOCKET` opt-in selects unix-socket vs TCP binding; the /// dual-mode transition + gateway-side consumer live in /// [`docs/web-ui/shape.md::Listener bind`](../../../docs/web-ui/shape.md) and -/// [`docs/gateway.md::Per-agent unix-socket upstream`](../../../docs/gateway.md). +/// [`docs/networking/gateway.md::Per-agent unix-socket upstream`](../../../docs/networking/gateway.md). /// /// # Errors /// @@ -146,7 +146,7 @@ pub async fn serve( // `UnixListener` at the given path. Empty string treated as // unset so a stray `HIVE_WEB_SOCKET=` doesn't trap us into an // un-bindable empty path. Falls through to the TCP path below - // otherwise. See docs/gateway.md::Per-agent unix-socket upstream + // otherwise. See docs/networking/gateway.md::Per-agent unix-socket upstream // for the gateway-side consumer. if let Some(socket_path) = std::env::var_os("HIVE_WEB_SOCKET") && !socket_path.is_empty() @@ -176,7 +176,7 @@ pub async fn serve( /// to ask. /// /// Marker-gating + the gateway-side consumer: see -/// [`docs/gateway.md::Per-agent unix-socket upstream`](../../../docs/gateway.md). +/// [`docs/networking/gateway.md::Per-agent unix-socket upstream`](../../../docs/networking/gateway.md). fn bind_unix(path: &Path) -> Result { use std::os::unix::fs::PermissionsExt; if let Some(parent) = path.parent() { diff --git a/hive-c0re/README.md b/hive-c0re/README.md index 28328d29..211b6688 100644 --- a/hive-c0re/README.md +++ b/hive-c0re/README.md @@ -26,7 +26,7 @@ to track it: - **`dashboard/`** — the operator dashboard (containers, approvals, schedules, logs, topology). - **`job_queue/`** — the job-DAG queue + desired-state reconciliation - (`docs/coordinator.md`). + (`docs/scheduler/coordinator.md`). - **`lifecycle/`** — `nixos-container` lifecycle + per-agent config flake generation. - **`stores/`** — sqlite-backed stores (broker, queues, audit, power). @@ -37,7 +37,7 @@ to track it: - **`stats/`** — dashboard metrics aggregation + OTEL export. - **`socket_server/`** — the unix-socket request server shared by per-agent + manager sockets. -- **`forge/`** — optional Forgejo wiring (`docs/forge.md`). +- **`forge/`** — optional Forgejo wiring (`docs/integrations/forge.md`). - **`coordinator.rs`** — top-level wiring for `serve`. - **`meta.rs`**, **`migrate.rs`** — the meta flake + schema/state migrations. diff --git a/hive-c0re/src/agent_config/topology.rs b/hive-c0re/src/agent_config/topology.rs index 69991727..87e2f349 100644 --- a/hive-c0re/src/agent_config/topology.rs +++ b/hive-c0re/src/agent_config/topology.rs @@ -9,9 +9,9 @@ //! `lifecycle::set_nspawn_flags` for the consumer. //! //! Format, rationale, read/reconcile/inject/surface flow, and target -//! enforcement semantics: `docs/agent-hierarchy.md::Current state`. +//! enforcement semantics: `docs/agent-lifecycle/agent-hierarchy.md::Current state`. //! `` sentinel resolution (delivered by [`resolve_recipient`]): -//! `docs/conventions.md::Recipient sentinels`. +//! `docs/process/conventions.md::Recipient sentinels`. //! //! ## Graph representation //! @@ -109,7 +109,7 @@ pub fn top_level_agents_in(topo: &BTreeMap>) -> Vec` triggers `read()` on `topology.json`. #[must_use] @@ -262,7 +262,7 @@ pub fn default_seed(agent_names: &[String]) -> BTreeMap> /// powers come from the privileged MCP socket, not its tree /// position. The cycle walk below covers "moving X under its own /// descendant" for the manager as much as any other agent. -/// `docs/agent-hierarchy.md::Current state` has the rationale. +/// `docs/agent-lifecycle/agent-hierarchy.md::Current state` has the rationale. pub fn apply_set_parent( topo: &BTreeMap>, child: &str, diff --git a/hive-c0re/src/container_view.rs b/hive-c0re/src/container_view.rs index 4c4e7a08..b189b93a 100644 --- a/hive-c0re/src/container_view.rs +++ b/hive-c0re/src/container_view.rs @@ -53,7 +53,7 @@ pub struct ContainerView { /// indentation. Sourced from `meta/topology.json` (single source of /// truth, hive-c0re-owned) — NOT from per-agent agent.nix, because /// an agent shouldn't be able to unilaterally declare its own place - /// in the tree. See `docs/agent-hierarchy.md::Current state`. + /// in the tree. See `docs/agent-lifecycle/agent-hierarchy.md::Current state`. #[serde(default, skip_serializing_if = "Option::is_none")] pub parent: Option, /// The Claude model the agent's harness is currently using, read from diff --git a/hive-c0re/src/coordinator.rs b/hive-c0re/src/coordinator.rs index e2ff80ce..bd9c4725 100644 --- a/hive-c0re/src/coordinator.rs +++ b/hive-c0re/src/coordinator.rs @@ -867,7 +867,7 @@ impl Coordinator { /// `_or_root` resolves to the literal string `""` when the /// slot is `None`, keeping the wording consistent with the /// `` sentinel's "root → operator" routing (see - /// `docs/conventions.md::Recipient sentinels`). The + /// `docs/process/conventions.md::Recipient sentinels`). The /// notifications fire as ordinary broker messages with /// `from = hive_sh4re::manager::SYSTEM_SENDER` so the dashboard renders /// them under the existing system-source styling. diff --git a/hive-c0re/src/dashboard/mod.rs b/hive-c0re/src/dashboard/mod.rs index 4f56f7c2..86da1de5 100644 --- a/hive-c0re/src/dashboard/mod.rs +++ b/hive-c0re/src/dashboard/mod.rs @@ -214,7 +214,7 @@ pub async fn serve( webhook_secret, }); // Binds loopback-only; external access via gateway. - // Rationale: docs/gateway.md::Firewall posture. + // Rationale: docs/networking/gateway.md::Firewall posture. let addr = SocketAddr::from(([127, 0, 0, 1], port)); let listener = bind_with_retry(addr).await?; tracing::info!(%addr, "dashboard listening"); diff --git a/hive-c0re/src/dashboard/state_files.rs b/hive-c0re/src/dashboard/state_files.rs index ee32245f..114eacf4 100644 --- a/hive-c0re/src/dashboard/state_files.rs +++ b/hive-c0re/src/dashboard/state_files.rs @@ -2,7 +2,7 @@ //! //! `GET /api/state-file?path=…` serves an allow-listed file (per-agent //! `state/` or `shared/`) with defense-in-depth symlink + traversal checks -//! (see `docs/security.md::State-file endpoint`); raster images are served +//! (see `docs/trust-boundary/security.md::State-file endpoint`); raster images are served //! with their real content-type, everything else as truncated text. //! `scan_validated_paths` runs the same allow-list at broker-message ingest //! so dashboard events carry a pre-verified file-ref set. @@ -19,7 +19,7 @@ use crate::paths::{AGENTS_ROOT, SHARED_ROOT}; #[derive(Deserialize, IntoParams)] pub(super) struct StateFileQuery { /// Absolute path under an agent's `state/` dir or under `shared/`; - /// checked against the allow-list (`docs/security.md::State-file + /// checked against the allow-list (`docs/trust-boundary/security.md::State-file /// endpoint`). path: String, } @@ -27,7 +27,7 @@ pub(super) struct StateFileQuery { /// Resolve a caller-supplied path against the allow-listed roots /// (`agents//state/` and `shared/`). Applies defense-in-depth /// symlink + traversal checks before serving. Security model and -/// all five layers: `docs/security.md::State-file endpoint`. +/// all five layers: `docs/trust-boundary/security.md::State-file endpoint`. fn resolve_state_path( raw: &str, ) -> std::result::Result<(std::path::PathBuf, std::fs::Metadata), String> { diff --git a/hive-c0re/src/dashboard/state_snapshot.rs b/hive-c0re/src/dashboard/state_snapshot.rs index ee578b3b..ed691d56 100644 --- a/hive-c0re/src/dashboard/state_snapshot.rs +++ b/hive-c0re/src/dashboard/state_snapshot.rs @@ -89,7 +89,7 @@ pub(super) struct StateSnapshot { /// runtime-generated `agents.conf` include file — see /// `gateway_nginx.rs`). The `false` branch (direct /// `http://:/` TCP links) is retained as a defensive - /// fallback for the env being unset. See `docs/gateway.md::Vhost map`. + /// fallback for the env being unset. See `docs/networking/gateway.md::Vhost map`. gateway_enabled: bool, /// Public URL of the forge (e.g. /// `"https://forge.pr1ma.darkest.space"`). Sourced from the diff --git a/hive-c0re/src/forge/mod.rs b/hive-c0re/src/forge/mod.rs index 67b8ad73..dd6458ae 100644 --- a/hive-c0re/src/forge/mod.rs +++ b/hive-c0re/src/forge/mod.rs @@ -2,7 +2,7 @@ //! config-repo mirroring, meta read-access grants. Also seeds //! `internal/docs` — a private repo every agent gets read-only //! collaborator access to for operator-curated shared content. -//! No-op when `hive-forge` isn't running. Full design: `docs/forge.md`. +//! No-op when `hive-forge` isn't running. Full design: `docs/integrations/forge.md`. mod ci_runner; pub mod config_pr_poll; diff --git a/hive-c0re/src/forge/users.rs b/hive-c0re/src/forge/users.rs index 4c716e81..83e7b8ed 100644 --- a/hive-c0re/src/forge/users.rs +++ b/hive-c0re/src/forge/users.rs @@ -59,7 +59,7 @@ fn config_org_avatar_png_path() -> std::path::PathBuf { )) } /// Per-agent token scopes (broad-but-not-admin) for tokens hive-c0re -/// mints itself on the **internal** forge. See `docs/forge.md::Token +/// mints itself on the **internal** forge. See `docs/integrations/forge.md::Token /// scopes` for the per-scope rationale. Not `pub(super)` — external /// forges (`dashboard/extra_forges.rs`) take an operator-pasted token /// verbatim, so their scope is whatever the operator's remote account @@ -70,7 +70,7 @@ const TOKEN_SCOPES: &str = "read:user,write:user,read:notification,write:notific /// top of `TOKEN_SCOPES` so the host daemon can drive /// `/api/v1/admin/*`. Site-admin membership alone isn't enough: the /// token's own scope gate runs before the user-permission check. -/// See `docs/forge.md::Token scopes`. +/// See `docs/integrations/forge.md::Token scopes`. const CORE_TOKEN_SCOPES: &str = "read:admin,write:admin,read:user,write:user,read:notification,write:notification,write:repository,write:issue,write:organization,write:misc"; /// Pull the access token out of forgejo's success message. Format @@ -242,7 +242,7 @@ pub(super) async fn ensure_user_email(name: &str) { } Err(e) if is_forbidden(&e) => { // Core token missing admin scope — see - // `docs/forge.md::Token scopes` migration note. + // `docs/integrations/forge.md::Token scopes` migration note. tracing::warn!( %name, %email, error = %e, "forge: PATCH user email forbidden — core token likely missing admin scope. \ diff --git a/hive-c0re/src/gateway_nginx.rs b/hive-c0re/src/gateway_nginx.rs index dd75a6a4..ba54f8f5 100644 --- a/hive-c0re/src/gateway_nginx.rs +++ b/hive-c0re/src/gateway_nginx.rs @@ -2,7 +2,7 @@ //! `/agent//` location blocks. Writes //! `/var/lib/hive-gateway/conf/agents.conf` on every topology change. //! UDS upstream selection, the reload trigger, and idempotency: -//! `docs/gateway.md::Per-agent unix-socket upstream`. +//! `docs/networking/gateway.md::Per-agent unix-socket upstream`. use anyhow::{Context, Result}; use std::fmt::Write as _; @@ -173,7 +173,7 @@ fn render(names: &[String], frontend_dir: Option<&str>) -> String { /// It stays an explicit trigger rather than a systemd path /// unit watching the file: the write already knows it changed something, /// and a watcher would turn one causal edge into a race with the writer's -/// own rename (see `docs/gateway.md`). +/// own rename (see `docs/networking/gateway.md`). /// /// The priv call is best-effort — a failed sync is logged but not fatal. /// `reload_if_pending` retries on the next `spawn_poll` tick so a diff --git a/hive-c0re/src/job_queue/mod.rs b/hive-c0re/src/job_queue/mod.rs index 6794a882..961863c5 100644 --- a/hive-c0re/src/job_queue/mod.rs +++ b/hive-c0re/src/job_queue/mod.rs @@ -24,7 +24,7 @@ //! The queue is runtime-only (no persistence): an empty graph on boot; desired //! state is re-derived by the reconcile sweep. A single scheduler task //! ([`scheduler::run_worker`]) drives it; concurrency comes from the build-slot -//! capacity, not multiple workers. Design: `docs/coordinator.md::Job queue`. +//! capacity, not multiple workers. Design: `docs/scheduler/coordinator.md::Job queue`. pub mod exec; pub mod model; diff --git a/hive-c0re/src/job_queue/model.rs b/hive-c0re/src/job_queue/model.rs index 4c7b2514..3f329dfa 100644 --- a/hive-c0re/src/job_queue/model.rs +++ b/hive-c0re/src/job_queue/model.rs @@ -9,7 +9,7 @@ //! execution, build-log, cancel, and the dashboard group (a group root's //! subtree *is* the group). A DAG used to be a second level above it, with //! its own store and its own id; there is no container node any more, so a -//! job is exactly the nodes it declared. See `docs/coordinator.md::Job queue` +//! job is exactly the nodes it declared. See `docs/scheduler/coordinator.md::Job queue` //! for the full design. pub use hive_host_sock::jobs::{PermPayload, State}; @@ -206,7 +206,7 @@ pub enum NodeKind { /// /// Its children declare no resources and borrow these grants, which is what /// lets `Prebuild` run beside the `Signal` → `Drain` window. Why braces - /// exist and what they cost: `docs/coordinator.md`, _Braces_. + /// exist and what they cost: `docs/scheduler/coordinator.md`, _Braces_. AgentWindow { agent: String }, /// Deploy phase 1 — **verify only, mutates nothing.** Drift-gate the /// approval's PR head, fetch it into the applied repo, and eval-verify the diff --git a/hive-c0re/src/job_queue/templates.rs b/hive-c0re/src/job_queue/templates.rs index 881b52b6..7e5575d4 100644 --- a/hive-c0re/src/job_queue/templates.rs +++ b/hive-c0re/src/job_queue/templates.rs @@ -8,12 +8,12 @@ //! **The one sanctioned exception is a brace** — a pure-resource-holder root //! ([`NodeKind::AgentWindow`], [`NodeKind::DeployWindow`]) declaring for a //! coordinated subtree whose members then declare nothing. See -//! `docs/coordinator.md`, _Braces_ — which also carries the per-operation DAG +//! `docs/scheduler/coordinator.md`, _Braces_ — which also carries the per-operation DAG //! shapes, so they are not restated here. //! //! Nodes are **named, not counted** — a template holds the handle //! [`JobBuilder::node`] hands back, so an edge says which node it waits on. Why that -//! removes submit-time cycle validation: `docs/coordinator.md`. +//! removes submit-time cycle validation: `docs/scheduler/coordinator.md`. //! //! The hive-wide **power ops** (`stop` / `start` / `restart`) are NOT here: //! their per-agent shape depends on live running state (an async @@ -203,7 +203,7 @@ impl<'a> RebuildRoots<'a> { /// The rebuild node subtree — three group roots (`MetaSync`, the `AgentWindow` /// brace, `Reconcile`). `after`, when given, is the node it chains behind. The -/// shape itself is in `docs/coordinator.md`; the code below is the source of +/// shape itself is in `docs/scheduler/coordinator.md`; the code below is the source of /// truth for it, so only the three choices a reader would otherwise undo are /// called out here: /// diff --git a/hive-c0re/src/job_queue/tests.rs b/hive-c0re/src/job_queue/tests.rs index 11988883..602d0080 100644 --- a/hive-c0re/src/job_queue/tests.rs +++ b/hive-c0re/src/job_queue/tests.rs @@ -579,7 +579,7 @@ fn rebuild_chain_declares_its_resources_on_the_brace() { // ⚠️ **The rename is a reversal, not tidying.** The old name asserted "the // slot follows the nix work and the lease follows the container" — each node // declaring what it personally needed. The brace inverts that for a - // coordinated subtree; see `docs/coordinator.md`, _Braces_. + // coordinated subtree; see `docs/scheduler/coordinator.md`, _Braces_. // // (`hive_jobq` owns slot *fairness*, pinned there by // `a_contended_resource_goes_to_the_oldest_waiter`. What is c0re's is diff --git a/hive-c0re/src/lifecycle/host_config.rs b/hive-c0re/src/lifecycle/host_config.rs index 2c9e2487..3eeddbe9 100644 --- a/hive-c0re/src/lifecycle/host_config.rs +++ b/hive-c0re/src/lifecycle/host_config.rs @@ -85,7 +85,7 @@ fn config_bind_source(name: &str) -> PathBuf { } /// Append bind flags for `child`'s state and config dirs into `binds`. -/// See docs/persistence.md ("Parent access to child state") for what a +/// See docs/agent-lifecycle/persistence.md ("Parent access to child state") for what a /// parent may touch and why. Creates missing host-side directories so /// nspawn doesn't refuse to start; missing dirs are non-fatal. /// @@ -134,7 +134,7 @@ fn bind_child_agent_dirs(child: &str, binds: &mut Vec) { /// that hive-c0re owns: `PRIVATE_NETWORK` (always 1), `HOST_ADDRESS` (the /// bridge gateway IP) and `EXTRA_NSPAWN_FLAGS` (the runtime-dir bind). What /// those network vars mean and why isolation is unconditional: -/// `docs/network.md` § *What the Rust side does*. +/// `docs/networking/network.md` § *What the Rust side does*. /// /// ⚠️ The start script expands `$EXTRA_NSPAWN_FLAGS` unquoted into the /// `systemd-nspawn` command. diff --git a/hive-c0re/src/lifecycle/mod.rs b/hive-c0re/src/lifecycle/mod.rs index 8f20525d..087a71ae 100644 --- a/hive-c0re/src/lifecycle/mod.rs +++ b/hive-c0re/src/lifecycle/mod.rs @@ -94,7 +94,7 @@ pub fn agent_web_port(name: &str) -> u16 { /// /// `HIVE_NETWORK_SUBNET` carries the host-side bridge address verbatim /// (e.g. `10.42.0.1/24`), **not** the canonical network address — see -/// the note in `set_nspawn_flags` + `docs/network.md`. The IP part is +/// the note in `set_nspawn_flags` + `docs/networking/network.md`. The IP part is /// therefore the bridge IP itself: the host end of the bridge, the /// default-route target for isolated containers, and the address the /// hive dnsmasq resolver binds. Returns the dotted-decimal IP with the @@ -522,7 +522,7 @@ pub async fn converge_start_preamble( /// activation-error shape), retry once via stop + kill + start before /// giving up. Used by the queue's fast-lane `Start` handler and the /// inline start-after-rebuild path. -/// See `docs/coordinator.md::Cold-start fallback`. +/// See `docs/scheduler/coordinator.md::Cold-start fallback`. /// /// Requires a [`StartableAgent`] token from [`converge_start_preamble`] /// to prove the preamble ran. For internal use within this module (where @@ -701,8 +701,8 @@ pub async fn destroy(name: &str) -> Result<()> { /// Pre-build `system.build.toplevel` against `meta#` so the /// subsequent `nixos-container update` finds the result cached and /// skips straight to the profile-swap. Store-warming only — container -/// is untouched. See `docs/coordinator.md::Rebuild path` for why -/// the prebuild happens before stop, and `docs/coordinator.md::Prebuild +/// is untouched. See `docs/scheduler/coordinator.md::Rebuild path` for why +/// the prebuild happens before stop, and `docs/scheduler/coordinator.md::Prebuild /// attr path` for why the explicit nixosConfigurations attr is required. /// /// `node_id` is the queue node this build belongs to, when there is one — diff --git a/hive-c0re/src/main.rs b/hive-c0re/src/main.rs index e3436613..a1525d37 100644 --- a/hive-c0re/src/main.rs +++ b/hive-c0re/src/main.rs @@ -528,7 +528,7 @@ async fn cmd_serve( // Also retries any pending gateway nginx reload that failed on // the previous tick. write() is idempotent so steady-state cost // is one stat per agent per tick. - // See `docs/gateway.md::Per-agent unix-socket upstream`. + // See `docs/networking/gateway.md::Per-agent unix-socket upstream`. agent_sockets::spawn_poll(); // MCP socket listener startup sync: one-shot sweep that re-registers any // running agent container whose MCP listener was lost when hive-c0re diff --git a/hive-c0re/src/matrix.rs b/hive-c0re/src/matrix.rs index dd08459b..a750ef26 100644 --- a/hive-c0re/src/matrix.rs +++ b/hive-c0re/src/matrix.rs @@ -4,7 +4,7 @@ //! haven't flipped `services.hyperhive.deploy.matrix.enable = true` pay //! nothing. //! -//! See `docs/matrix.md::Provisioning flow (registration token)` for +//! See `docs/integrations/matrix.md::Provisioning flow (registration token)` for //! the full UIAA round-trip, token-file shape, and host/container //! bind-mount layout. @@ -744,7 +744,7 @@ pub async fn ensure_user_for( tracing::info!(%name, "matrix: provisioned access token"); // Kick the daemon so it picks up the new token without waiting for a - // full container restart — see docs/matrix.md::Provisioning flow. + // full container restart — see docs/integrations/matrix.md::Provisioning flow. if let Err(e) = crate::priv_client::restart_matrix_daemon(name).await { tracing::warn!(%name, error = ?e, "matrix: could not restart hive-matrix-daemon (token written; daemon will reload on next container start)"); } else { diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index d667c8d9..2a834850 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -2,7 +2,7 @@ //! exports one `nixosConfiguration` per agent and drives the system-wide //! deploy audit trail. Flow (`sync_agents`, two-phase `prepare_deploy` / //! `finalize_deploy` / `abort_deploy`, `lock_update_hyperhive`): -//! `docs/approvals.md::Meta flake`. +//! `docs/agent-lifecycle/approvals.md::Meta flake`. use std::path::Path; @@ -162,7 +162,7 @@ pub async fn sync_agents(hive: &HiveEnv, agents: &[AgentSpec]) -> Result<()> { // (`HIVE_WEB_SOCKET`); the gateway routes via this map, falling back // to a computed TCP loopback port (`lifecycle::agent_web_port`) only // while an agent's socket marker is absent. Best-effort + non-fatal. - // See `docs/gateway.md::Per-agent unix-socket upstream`. + // See `docs/networking/gateway.md::Per-agent unix-socket upstream`. if let Err(e) = crate::agent_sockets::write(&agent_names) { tracing::warn!(error = ?e, "agent_sockets::write failed (non-fatal)"); } @@ -249,7 +249,7 @@ pub async fn prepare_deploy(name: &str, node_id: Option) -> Result<()> { let input = format!("agent-{name}"); // Re-lock the agent input against the LOCAL applied mirror, not the // persistent forge URL declared in the meta flake (see the `## Meta flake` - // note in docs/approvals.md): the deploy must build the exact reviewed + // note in docs/agent-lifecycle/approvals.md): the deploy must build the exact reviewed // config that `verify_commit` gated and `applied//main` was // fast-forwarded to, and it must keep working when the forge is // unreachable (rebuilds fire on crash-restart / meta bumps too, not just diff --git a/hive-c0re/src/migrate.rs b/hive-c0re/src/migrate.rs index f80a30fa..dd50202c 100644 --- a/hive-c0re/src/migrate.rs +++ b/hive-c0re/src/migrate.rs @@ -16,7 +16,7 @@ //! from birth does not need re-asserting each morning. //! //! Kill-switch: `HIVE_SKIP_META_MIGRATION=1`. Full sequence and phase -//! details: `docs/approvals.md::Migration from the pre-tag`. +//! details: `docs/agent-lifecycle/approvals.md::Migration from the pre-tag`. use std::path::Path; use std::sync::Arc; diff --git a/hive-c0re/src/socket_server/mod.rs b/hive-c0re/src/socket_server/mod.rs index 5a90e644..5bd47450 100644 --- a/hive-c0re/src/socket_server/mod.rs +++ b/hive-c0re/src/socket_server/mod.rs @@ -1073,7 +1073,7 @@ pub(crate) fn handle_send( // Resolve magic-recipient sentinels (``) against topology.json; // no-op for ordinary names. Lets agents address structural roles without // learning the label — runtime reparenting propagates for free. See - // `docs/conventions.md::Recipient sentinels`. + // `docs/process/conventions.md::Recipient sentinels`. let resolved = crate::topology::resolve_recipient(agent, to); // Validate that the resolved recipient is a known local agent or the // special "operator" recipient. Without this check a typo in `to` diff --git a/hive-c0re/src/stats/container_stats.rs b/hive-c0re/src/stats/container_stats.rs index ed298561..24d579ad 100644 --- a/hive-c0re/src/stats/container_stats.rs +++ b/hive-c0re/src/stats/container_stats.rs @@ -12,7 +12,7 @@ //! No network: agents share the host network namespace //! (`privateNetwork = false`), so there is no per-container net //! counter to read. Per-agent network only becomes meaningful with the -//! netns-isolation roadmap (`docs/network.md`). +//! netns-isolation roadmap (`docs/networking/network.md`). //! //! CPU is cumulative (`usage_usec` is monotonic), so a single read is //! meaningless — we sample every machine's counter, sleep one short diff --git a/hive-c0re/src/stores/build_logs.rs b/hive-c0re/src/stores/build_logs.rs index 67b28c3e..b43c02de 100644 --- a/hive-c0re/src/stores/build_logs.rs +++ b/hive-c0re/src/stores/build_logs.rs @@ -1,7 +1,7 @@ //! Sqlite-backed full build-log capture — stdout + stderr per //! `nixos-container` / `nix build` invocation, accumulated live. //! Schema, indices, retention, and the rationale for replacing -//! the old ring buffer: `docs/persistence.md::/var/lib/hyperhive/db/build_logs.sqlite`. +//! the old ring buffer: `docs/agent-lifecycle/persistence.md::/var/lib/hyperhive/db/build_logs.sqlite`. use std::path::Path; use std::sync::{Arc, Mutex, OnceLock}; diff --git a/hive-c0re/src/stores/scheduled_prompts.rs b/hive-c0re/src/stores/scheduled_prompts.rs index 75bff2a3..a4b213fe 100644 --- a/hive-c0re/src/stores/scheduled_prompts.rs +++ b/hive-c0re/src/stores/scheduled_prompts.rs @@ -4,13 +4,13 @@ //! `interval_seconds` and re-arm `next_fire_at` on delivery; //! one-shots are reaped. //! -//! Schema + retention: `docs/persistence.md::/var/lib/hyperhive/db/broker.sqlite` +//! Schema + retention: `docs/agent-lifecycle/persistence.md::/var/lib/hyperhive/db/broker.sqlite` //! (the `scheduled_prompts` / `scheduled_prompt_targets` table bullets). //! Submit paths (operator-direct vs `ApprovalKind::SchedulePrompt`, //! plus why even agent-self schedules go through approval): -//! `docs/approvals.md::Scheduled prompts (submit paths)`. +//! `docs/agent-lifecycle/approvals.md::Scheduled prompts (submit paths)`. //! Catch-up clamp on resume + per-target tombstoning: -//! `docs/approvals.md::Scheduled prompt worker (catch-up clamp)`. +//! `docs/agent-lifecycle/approvals.md::Scheduled prompt worker (catch-up clamp)`. use std::path::Path; use std::sync::Mutex; diff --git a/hive-c0re/src/workers/agent_sockets.rs b/hive-c0re/src/workers/agent_sockets.rs index ac1c0cbd..3de04282 100644 --- a/hive-c0re/src/workers/agent_sockets.rs +++ b/hive-c0re/src/workers/agent_sockets.rs @@ -6,7 +6,7 @@ //! //! Full mechanism — per-agent subdir bind-mount, `hyperhive-socket-bound` //! marker gate, gateway UDS upstream, 10s poll loop: -//! `docs/gateway.md::Per-agent unix-socket upstream`. +//! `docs/networking/gateway.md::Per-agent unix-socket upstream`. use std::collections::BTreeMap; use std::path::{Path, PathBuf}; diff --git a/hive-c0re/src/workers/auto_update.rs b/hive-c0re/src/workers/auto_update.rs index 30ec32cf..282c71eb 100644 --- a/hive-c0re/src/workers/auto_update.rs +++ b/hive-c0re/src/workers/auto_update.rs @@ -14,7 +14,7 @@ //! path are skipped — nothing changed, no nix work to do. //! //! Booting with no config change performs no meta commit — only -//! reconciles. See `docs/coordinator.md::Boot reconcile`. +//! reconciles. See `docs/scheduler/coordinator.md::Boot reconcile`. use std::path::Path; use std::sync::Arc; diff --git a/hive-c0re/src/workers/crash_watch.rs b/hive-c0re/src/workers/crash_watch.rs index 1dd67f34..222d87d1 100644 --- a/hive-c0re/src/workers/crash_watch.rs +++ b/hive-c0re/src/workers/crash_watch.rs @@ -1,7 +1,7 @@ //! Per-container crash and login-state watcher. Polls every managed //! container on a 10s interval. Fires `ContainerCrash`, `LoggedIn`, //! and `NeedsLogin` helper events. Event semantics and the -//! `RECENT_TRANSIENT_GRACE` window: `docs/approvals.md::Helper events`. +//! `RECENT_TRANSIENT_GRACE` window: `docs/agent-lifecycle/approvals.md::Helper events`. use std::collections::HashSet; use std::sync::Arc; diff --git a/hive-c0re/src/workers/scheduled_prompts_worker.rs b/hive-c0re/src/workers/scheduled_prompts_worker.rs index 2f99f5bc..6c5b6045 100644 --- a/hive-c0re/src/workers/scheduled_prompts_worker.rs +++ b/hive-c0re/src/workers/scheduled_prompts_worker.rs @@ -1,7 +1,7 @@ //! Background loop that drains due `scheduled_prompts` rows and fans //! the body to each active target. 5s poll cadence, shutdown-aware. //! Catch-up clamp, missing-target handling, and broker-error retry -//! semantics: `docs/approvals.md::Scheduled prompt worker`. +//! semantics: `docs/agent-lifecycle/approvals.md::Scheduled prompt worker`. use std::sync::Arc; use std::time::Duration; diff --git a/hive-core-agent-sock/README.md b/hive-core-agent-sock/README.md index 2032d46b..b97e5beb 100644 --- a/hive-core-agent-sock/README.md +++ b/hive-core-agent-sock/README.md @@ -18,4 +18,4 @@ This is the **host-served** protocol: the harness talks *out* to `hive-c0re` over `/run/hive/mcp.sock` (broker sends, approvals, lifecycle). `hive-agent-sock` is the separate *in-container* socket the harness serves to its own local producers — that one never leaves the container. See -`docs/boundary.md` for the socket topology. +`docs/trust-boundary/boundary.md` for the socket topology. diff --git a/hive-core-agent-sock/src/lib.rs b/hive-core-agent-sock/src/lib.rs index 036a6670..5da4a737 100644 --- a/hive-core-agent-sock/src/lib.rs +++ b/hive-core-agent-sock/src/lib.rs @@ -38,7 +38,7 @@ pub enum Request { }, /// Pop pending messages from this agent's inbox. /// Delivery + ack cycle: see - /// `docs/conventions.md::Broker delivery + ack cycle`. + /// `docs/process/conventions.md::Broker delivery + ack cycle`. Recv { #[serde(default)] wait_seconds: Option, @@ -55,7 +55,7 @@ pub enum Request { OperatorMsg { body: String }, /// Wake-up event injected from inside the container. Recipient is /// implicit (this agent); `from` is caller-chosen. See - /// `docs/conventions.md::Wake injection` for the trust model and + /// `docs/process/conventions.md::Wake injection` for the trust model and /// typical callers. The wake is persisted in the sqlite broker /// like any other message — the agent can ack it via `AckUntil`. Wake { from: String, body: String }, @@ -68,7 +68,7 @@ pub enum Request { /// `query_agent_state` capability — rejected with an error otherwise; /// `"*"` is always rejected (use the manager socket). On the manager /// socket: `None` = manager self, `"*"` = hive-wide, any name = - /// that agent. See `docs/conventions.md::Loose-ends wire shape`. + /// that agent. See `docs/process/conventions.md::Loose-ends wire shape`. GetLooseEnds { #[serde(default, skip_serializing_if = "Option::is_none")] agent: Option, @@ -80,14 +80,14 @@ pub enum Request { SetStatus { text: String }, /// Fetch identity + status for an agent. `name = None` = /// self-introspection; `Some()` = target query. See - /// `docs/conventions.md::Agent metadata`. + /// `docs/process/conventions.md::Agent metadata`. GetAgentMeta { #[serde(default, skip_serializing_if = "Option::is_none")] name: Option, }, /// Cancel an open thread the agent owns. Authorisation + /// per-kind semantics in - /// `docs/conventions.md::Loose-ends wire shape`. + /// `docs/process/conventions.md::Loose-ends wire shape`. CancelLooseEnd { kind: CancelLooseEndKind, id: i64 }, /// Create a git repo *through hive-c0re*. Agents can't create /// repos with their own forge token (`max_repo_creation = 0`); this is @@ -98,7 +98,7 @@ pub enum Request { CreateRepo { repo: String }, /// Mark every message popped since the last `AckTurn` as handled. /// Harness↔broker pairing fired after `TurnOutcome::Ok`. See - /// `docs/conventions.md::Broker delivery + ack cycle`. + /// `docs/process/conventions.md::Broker delivery + ack cycle`. AckTurn, /// Mark every inbox message with broker row id `<= up_to` as /// handled (`acked_at` set), whether still pending or already @@ -107,12 +107,12 @@ pub enum Request { /// re-reading dozens of already-handled messages one turn at a /// time, the agent acks everything up to the id it has seen. /// Recipient-scoped — an agent can only ack its own rows. See - /// `docs/conventions.md::Broker delivery + ack cycle`. + /// `docs/process/conventions.md::Broker delivery + ack cycle`. AckUntil { up_to: i64 }, /// Requeue every popped-but-unacked message back into the inbox. /// Harness fires this once at boot to recover from /// crashed-mid-turn sessions. See - /// `docs/conventions.md::Broker delivery + ack cycle`. + /// `docs/process/conventions.md::Broker delivery + ack cycle`. RequeueInflight, /// Harness → c0re: "I saw the `GracefulStop` signal, ran my /// stop-checkpoint turn (durable `/state` flushed) and am exiting my @@ -267,7 +267,7 @@ pub enum Response { /// this agent. Ordered newest-first within each kind. LooseEnds { loose_ends: Vec }, /// `GetAgentMeta` result. Per-field semantics + serde defaults - /// live in `docs/conventions.md::Agent metadata`. + /// live in `docs/process/conventions.md::Agent metadata`. AgentMeta { name: String, #[serde(default = "default_true")] diff --git a/hive-forge-notify/README.md b/hive-forge-notify/README.md index 208006fd..b3a7d4b0 100644 --- a/hive-forge-notify/README.md +++ b/hive-forge-notify/README.md @@ -14,7 +14,7 @@ self-echo filter, the comment / review / new-item / state-change wrapper formats, body-excerpt truncation, and the assigned-issue rollup all live in `notify.rs`. The behaviour contract — activation gates, filtering rules, the review-request override — is documented in -[`docs/forge.md`](../docs/forge.md), "Notification poller". +[`docs/integrations/forge.md`](../docs/integrations/forge.md), "Notification poller". ## Shape diff --git a/hive-forge-notify/src/bin/hive-github-notify/main.rs b/hive-forge-notify/src/bin/hive-github-notify/main.rs index 947e914a..fa9beb50 100644 --- a/hive-forge-notify/src/bin/hive-github-notify/main.rs +++ b/hive-forge-notify/src/bin/hive-github-notify/main.rs @@ -5,7 +5,7 @@ //! //! Takes no arguments. `HYPERHIVE_STATE_DIR` holds the PAT //! (`github-token`, provisioned from the dashboard credentials tab — see -//! `docs/github.md`) and `HIVE_AGENT_SOCKET` is the harness's todo socket. +//! `docs/integrations/github.md`) and `HIVE_AGENT_SOCKET` is the harness's todo socket. //! Having a PAT *is* the opt-in: with no token the poller logs why and //! exits 0, so deploying this unit to an agent that never gets one costs a //! settled process rather than a restart loop. diff --git a/hive-forge-notify/src/bin/hive-github-notify/source.rs b/hive-forge-notify/src/bin/hive-github-notify/source.rs index 9f84dfef..712b694d 100644 --- a/hive-forge-notify/src/bin/hive-github-notify/source.rs +++ b/hive-forge-notify/src/bin/hive-github-notify/source.rs @@ -11,7 +11,7 @@ use hive_forge_notify::notify::UNREAD_FETCH_LIMIT; use hive_forge_notify::source::Source; use tracing::{debug, warn}; -/// GitHub's REST base. `docs/github.md` scopes the integration to +/// GitHub's REST base. `docs/integrations/github.md` scopes the integration to /// github.com only — the same constraint the `gh` wrapper and the git /// credential helper already carry — so this is a constant rather than /// another operator-entered URL. diff --git a/hive-forge-notify/src/notify.rs b/hive-forge-notify/src/notify.rs index 7c9a0e95..bec6162a 100644 --- a/hive-forge-notify/src/notify.rs +++ b/hive-forge-notify/src/notify.rs @@ -18,12 +18,12 @@ //! **Multi-source**: always the internal Forgejo, plus github.com when the //! agent has a PAT. Each source polls independently behind //! [`Source`]; everything below is shared. Rationale -//! + host differences: [`docs/forge.md::Sources`](../../../docs/forge.md). +//! + host differences: [`docs/integrations/forge.md::Sources`](../../../docs/integrations/forge.md). //! //! Activation gates, self-notification filtering, body excerpt + //! truncation + heading escape, wrapper formats (comment / review / //! new-item / state-change), meta suffix, and review-request override -//! all live in [`docs/forge.md::Notification poller`](../../../docs/forge.md). +//! all live in [`docs/integrations/forge.md::Notification poller`](../../../docs/integrations/forge.md). use std::collections::{HashMap, HashSet}; use std::fmt::Write as _; @@ -63,7 +63,7 @@ pub const TOKEN_RETRY_MAX: u32 = 20; /// a `new ` label. Later activity that lands on the state-change /// path because it carries no `latest_comment_url` (e.g. a bodiless /// review submission) fires well outside this window, so we must not -/// claim it's "new" — see docs/forge.md, "new vs activity on". +/// claim it's "new" — see docs/integrations/forge.md, "new vs activity on". const NEW_ITEM_TOLERANCE_SECS: i64 = 120; /// Fetch the account's own login for self-notification filtering. @@ -111,7 +111,7 @@ fn notif_type_label(t: Option) -> &'static str { /// Escape strict-ATX markdown headings in a body before embedding it /// inside the forge-notify wrapper, so a leading `## title` line /// doesn't blow into an h2 in the dashboard render. See -/// `docs/forge.md::Body excerpt + truncation + heading escape` for +/// `docs/integrations/forge.md::Body excerpt + truncation + heading escape` for /// the strict-ATX-vs-`#tag` rationale and the `split_inclusive` /// trailing-newline contract. fn escape_md_headings(body: &str) -> String { @@ -196,7 +196,7 @@ fn is_username_byte(b: u8) -> bool { /// `@username` mention AND aren't already present (as a substring) in /// `included_excerpt`. Surfaces tags that fell outside the truncation /// window so addressed agents never silently miss a mention on a long -/// body. See `docs/forge.md::Body excerpt + truncation + heading +/// body. See `docs/integrations/forge.md::Body excerpt + truncation + heading /// escape` for the truncate-before-escape ordering rule. fn extract_truncated_mention_lines<'a>(full_body: &'a str, included_excerpt: &str) -> Vec<&'a str> { full_body @@ -253,7 +253,7 @@ fn review_state_label(state: &str) -> Option<&str> { /// Build a human-readable wake message for one Forgejo notification, /// or `None` for a self-echo the caller should mark-read without /// delivery. Wrapper format table + meta-suffix shape + number/repo -/// extraction live in `docs/forge.md::Wrapper format` + +/// extraction live in `docs/integrations/forge.md::Wrapper format` + /// `::Meta suffix`. async fn format_notification( client: &reqwest::Client, @@ -305,7 +305,7 @@ async fn format_notification( // Always fetch subject detail for assignee/reviewer metadata so // the meta suffix can show current ownership without a follow-up - // fetch (see `docs/forge.md::Meta suffix`). + // fetch (see `docs/integrations/forge.md::Meta suffix`). let subject = if subject_api_url.is_empty() { None } else { @@ -361,7 +361,7 @@ async fn format_notification( // that comment was genuinely posted AFTER the close — a comment // racing the merge inside the state-change tolerance window — append // it so it isn't lost (best of both worlds; see - // `docs/forge.md::Merge racing a comment`). The ordinary pre-merge + // `docs/integrations/forge.md::Merge racing a comment`). The ordinary pre-merge // last comment (created before `closed_at`) is left off. let comment_tail = if has_comment { fresh_post_close_comment_tail( @@ -413,7 +413,7 @@ struct NotifMeta<'a> { /// Build the `\nassignee: ...` (and optionally `\nreviewer: ...`) /// suffix appended to every wrapper. Shape + presence rules live in -/// `docs/forge.md::Meta suffix`. +/// `docs/integrations/forge.md::Meta suffix`. fn build_meta_suffix(subject: Option<&serde_json::Value>, is_pr: bool) -> String { let assignees: Vec<&str> = subject .and_then(|s| s["assignees"].as_array()) @@ -675,7 +675,7 @@ fn format_state_change_notification( // time. A review submitted with no body carries no // `latest_comment_url`, so it lands here instead of on the comment // path — and its event time is well after `created_at`. Labeling - // that "new PR" is misleading (see docs/forge.md, "new vs activity + // that "new PR" is misleading (see docs/integrations/forge.md, "new vs activity // on"): agents dismiss it as a // duplicate of the original open notification. When we can't confirm // creation, fall back to a neutral "activity on" label. @@ -707,7 +707,7 @@ fn format_state_change_notification( // Review-request override: Forgejo doesn't reliably set // `reason == "review_requested"` (often null), so we check the // subject payload's `requested_reviewers` list directly. See - // `docs/forge.md::Review-request override`. + // `docs/integrations/forge.md::Review-request override`. let is_review_request = is_new && *is_pr && !own_login.is_empty() @@ -751,7 +751,7 @@ fn format_state_change_notification( /// `created_at`: within `NEW_ITEM_TOLERANCE_SECS` ⇒ creation. When /// either timestamp is missing or unparseable we default to `true`, /// preserving the prior "new" behavior rather than masking a genuine -/// new item behind the neutral fallback. See docs/forge.md, "new vs +/// new item behind the neutral fallback. See docs/integrations/forge.md, "new vs /// activity on". fn notification_is_creation( event: Option, diff --git a/hive-forge/src/verbs/ci_rerun.rs b/hive-forge/src/verbs/ci_rerun.rs index 00a822de..2682e984 100644 --- a/hive-forge/src/verbs/ci_rerun.rs +++ b/hive-forge/src/verbs/ci_rerun.rs @@ -27,7 +27,7 @@ //! //! Dispatch re-runs the whole workflow (no single-job variant). ⚠️ `--pr` //! verifies the code but doesn't reliably move the PR's own status -//! checks — see `docs/ci.md`'s "CI checks" for why; re-check `pr-status`. +//! checks — see `docs/scheduler/ci.md`'s "CI checks" for why; re-check `pr-status`. use anyhow::{Context as _, Result, bail}; use clap::Args as ClapArgs; diff --git a/hive-host-sock/README.md b/hive-host-sock/README.md index d8efdf36..2ff6dfd5 100644 --- a/hive-host-sock/README.md +++ b/hive-host-sock/README.md @@ -25,6 +25,6 @@ needs (`Source`, `State`, `PermPayload`, `NodeId`) is protocol-local. The typed served as a generic graph (`hive-jobq-wire`), not as a second hand-written view. -See `docs/boundary.md` (host admin socket access) for the trust model around who +See `docs/trust-boundary/boundary.md` (host admin socket access) for the trust model around who may connect to the socket, and `hive-priv-sock` for the sibling split on the privileged-helper socket. diff --git a/hive-host-sock/src/jobs.rs b/hive-host-sock/src/jobs.rs index 7930807e..36f7048a 100644 --- a/hive-host-sock/src/jobs.rs +++ b/hive-host-sock/src/jobs.rs @@ -13,7 +13,7 @@ //! `GET /api/jobq/graph` — so there is no second shape to keep in //! agreement with the first. Queue internals (node kinds, edges, //! scheduling) live in `hive-c0re::job_queue`. -//! Semantics: `docs/coordinator.md::Job queue`. +//! Semantics: `docs/scheduler/coordinator.md::Job queue`. use serde::{Deserialize, Serialize}; diff --git a/hive-host-sock/src/lib.rs b/hive-host-sock/src/lib.rs index 96d11bbe..9be11f44 100644 --- a/hive-host-sock/src/lib.rs +++ b/hive-host-sock/src/lib.rs @@ -68,7 +68,7 @@ pub const AGENT_SOCKET_DIR: &str = "/run/hive-agent"; /// Per-agent web UI unix socket path — `AGENT_SOCKET_DIR//web.sock`. /// Same socket the gateway's nginx upstream and the harness's /// `HIVE_WEB_SOCKET` bind both derive from; see -/// `docs/gateway.md::Per-agent unix-socket upstream`. +/// `docs/networking/gateway.md::Per-agent unix-socket upstream`. #[must_use] pub fn agent_web_socket(name: &Ident) -> PathBuf { PathBuf::from(AGENT_SOCKET_DIR) @@ -121,14 +121,14 @@ pub enum HostRequest { /// first-time provisioning: proposed/applied repos, state subvolume, /// meta-flake sync, `nixos-container create`), bypassing the approval /// queue. Privileged-context only. Exposed on the CLI as `hivectl - /// agent create`. See `docs/approvals.md::Approval kinds + /// agent create`. See `docs/agent-lifecycle/approvals.md::Approval kinds /// (wire shapes)`. Wire name kept as `Spawn` (unrenamed underneath /// the CLI-verb rename — `hivectl agent start` reuses the /// existing scope-based [`HostRequest::Start`] below instead of a /// new per-agent variant, see its doc comment). Spawn { name: Ident }, /// Submit a first-creation request for the operator to approve. See - /// `docs/approvals.md::Approval kinds (wire shapes)` (`Spawn`). + /// `docs/agent-lifecycle/approvals.md::Approval kinds (wire shapes)` (`Spawn`). /// Exposed on the CLI as `hivectl agent request-create`. RequestSpawn { name: Ident }, /// Hard stop a managed container. Exposed on the CLI as `hivectl @@ -139,7 +139,7 @@ pub enum HostRequest { /// `Kill`. Kill { name: Ident }, /// Tear down a sub-agent container, optionally purging state. - /// See `docs/approvals.md::Destroy semantics`. + /// See `docs/agent-lifecycle/approvals.md::Destroy semantics`. Destroy { name: Ident, #[serde(default)] @@ -234,7 +234,7 @@ pub enum HostRequest { /// Move an agent in the topology tree. `new_parent = None` /// promotes the agent to root, `Some(name)` sets a new parent. /// Validation rules + bind-mount caveat documented in - /// `docs/agent-hierarchy.md::Current state`. + /// `docs/agent-lifecycle/agent-hierarchy.md::Current state`. SetParent { child: Ident, new_parent: Option, diff --git a/hive-metric/README.md b/hive-metric/README.md index 7a43cd1f..7c60de18 100644 --- a/hive-metric/README.md +++ b/hive-metric/README.md @@ -22,5 +22,5 @@ Standard OTEL environment variables are read automatically by the SDK: - `OTEL_RESOURCE_ATTRIBUTES` — resource labels (`k=v,...`) The harness populates all of these per-agent when -`services.hyperhive.otel.enable = true`. See `docs/observability.md` for the +`services.hyperhive.otel.enable = true`. See `docs/scheduler/observability.md` for the collector setup and the metrics hyperhive exports. diff --git a/hive-priv-sock/README.md b/hive-priv-sock/README.md index bbfdfbba..6bcb8ce3 100644 --- a/hive-priv-sock/README.md +++ b/hive-priv-sock/README.md @@ -16,6 +16,6 @@ admin socket. ## Shape Serde-derived request/response types only — no server or client logic. Both -sides import them so the shapes stay in sync. See `docs/boundary.md` + -`docs/security.md` for the privilege boundary these types sit on, and +sides import them so the shapes stay in sync. See `docs/trust-boundary/boundary.md` + +`docs/trust-boundary/security.md` for the privilege boundary these types sit on, and `hive-priv/README` for the helper itself. diff --git a/hive-priv-sock/src/lib.rs b/hive-priv-sock/src/lib.rs index af15605b..938cb721 100644 --- a/hive-priv-sock/src/lib.rs +++ b/hive-priv-sock/src/lib.rs @@ -277,7 +277,7 @@ pub struct CredentialMount { /// Network isolation parameters for `WriteNspawnFlags`. hive-priv writes /// `PRIVATE_NETWORK=1` + veth bridge wiring from these; every container /// is isolated, so they are required rather than a mode selector. -/// No static address is pre-assigned here — see `docs/network.md` +/// No static address is pre-assigned here — see `docs/networking/network.md` /// § *Container isolation* for how a container gets its address. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkIsolation { @@ -288,7 +288,7 @@ pub struct NetworkIsolation { /// container-side setup installs a default route (`default via `) /// before DHCP completes: without it the container has no route off /// the bridge subnet until the DHCP lease arrives. The same IP runs the - /// hive dnsmasq resolver — see `docs/network.md` § *How the isolated + /// hive dnsmasq resolver — see `docs/networking/network.md` § *How the isolated /// container gets its resolver*. pub gateway_ip: String, } diff --git a/hive-priv/README.md b/hive-priv/README.md index d23b44c3..40fcc8cf 100644 --- a/hive-priv/README.md +++ b/hive-priv/README.md @@ -12,7 +12,7 @@ coordination logic (broker, HTTP, scheduling) stays in the *unprivileged* Privsep. `hive-c0re` runs as the unprivileged `hive-core` user so a bug or a prompt-injection in the large daemon can't directly wield root. The few root operations it needs are funnelled through this small, auditable helper instead. -See `docs/boundary.md` and `docs/security.md` for the privilege boundary. +See `docs/trust-boundary/boundary.md` and `docs/trust-boundary/security.md` for the privilege boundary. ## Security model diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index 686b5455..298105c0 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -66,7 +66,7 @@ async fn main() -> Result<()> { fn socket_listener() -> Result { // hive-priv is ALWAYS socket-activated by the `hive-priv.socket` unit // (fd 3 via LISTEN_FDS). There is intentionally no self-bind fallback, - // so dev and prod take the same path; see docs/boundary.md. + // so dev and prod take the same path; see docs/trust-boundary/boundary.md. let listen_fds: Option = std::env::var("LISTEN_FDS") .ok() .and_then(|s| s.parse().ok()); @@ -2889,7 +2889,7 @@ fn write_nspawn_flags( // marker carries the gateway IP so the container need not re-derive it. // Always written — every container is isolated, so there is no mode in // which the marker should be absent. Why the rewrite is needed at all, - // and which unit does it: `docs/network.md` § *How the isolated + // and which unit does it: `docs/networking/network.md` § *How the isolated // container gets its resolver*. write_bridge_dns_marker(container, isolation)?; Ok(()) diff --git a/hive-sh4re/src/approvals.rs b/hive-sh4re/src/approvals.rs index a9df3c50..605de945 100644 --- a/hive-sh4re/src/approvals.rs +++ b/hive-sh4re/src/approvals.rs @@ -8,7 +8,7 @@ use hive_types::Ident; use serde::{Deserialize, Serialize}; /// One row in the approval queue. `commit_ref` is overloaded per -/// `kind` — see `docs/approvals.md::Approval kinds (wire shapes)` +/// `kind` — see `docs/agent-lifecycle/approvals.md::Approval kinds (wire shapes)` /// for the encoding table and lifecycle. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Approval { @@ -39,7 +39,7 @@ pub struct Approval { /// What action the approval, when granted, will trigger. /// Variant-specific payload encoding + flow lives in -/// `docs/approvals.md::Approval kinds (wire shapes)`. +/// `docs/agent-lifecycle/approvals.md::Approval kinds (wire shapes)`. #[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum ApprovalKind { @@ -63,7 +63,7 @@ pub enum ApprovalKind { /// sole config-change flow — a manager opens a PR on its /// `agent-configs/` repo and the operator reviews + approves it. /// `commit_ref` = PR number; `fetched_sha` = the reviewed PR head - /// pinned at submit. See `docs/approvals.md`. + /// pinned at submit. See `docs/agent-lifecycle/approvals.md`. #[default] MergeConfigPr, } @@ -95,7 +95,7 @@ pub enum ApprovalStatus { /// Manager withdrew the request before the operator acted on it. /// Distinct from `Denied` (operator decision) and `Failed` /// (post-approval lifecycle error). See - /// `docs/approvals.md::Withdrawing a pending approval`. + /// `docs/agent-lifecycle/approvals.md::Withdrawing a pending approval`. Cancelled, } diff --git a/hive-sh4re/src/inbox.rs b/hive-sh4re/src/inbox.rs index fc8be28a..7b8d8db3 100644 --- a/hive-sh4re/src/inbox.rs +++ b/hive-sh4re/src/inbox.rs @@ -86,7 +86,7 @@ pub struct InboxRow { } /// One delivered message in a `Recv` response. -/// See `docs/conventions.md::Broker delivery + ack cycle` for the +/// See `docs/process/conventions.md::Broker delivery + ack cycle` for the /// full delivery/ack/requeue story. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DeliveredMessage { @@ -121,7 +121,7 @@ pub enum ReminderTiming { /// thread kinds can land without breaking existing handlers. /// Per-flavour scoping + per-variant fields + clock-anomaly /// saturation behaviour live in -/// `docs/conventions.md::Loose-ends wire shape`. +/// `docs/process/conventions.md::Loose-ends wire shape`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum LooseEnd { @@ -190,7 +190,7 @@ pub enum LooseEnd { /// Kind discriminator for `CancelLooseEnd`. Per-kind store + /// authorisation rules live in -/// `docs/conventions.md::Loose-ends wire shape`. +/// `docs/process/conventions.md::Loose-ends wire shape`. #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum CancelLooseEndKind { diff --git a/hive-sh4re/src/manager.rs b/hive-sh4re/src/manager.rs index 004cb425..c46d6529 100644 --- a/hive-sh4re/src/manager.rs +++ b/hive-sh4re/src/manager.rs @@ -60,7 +60,7 @@ pub fn trusted_sender(s: &str) -> Ident { /// Out-of-band events the host-side daemon pushes to the manager's inbox. /// Serialised as JSON in `Message::body` (sender = `SYSTEM_SENDER`). /// Per-variant triggers + the optional `sha`/`tag` semantics live in -/// `docs/approvals.md::Helper events to the manager`. +/// `docs/agent-lifecycle/approvals.md::Helper events to the manager`. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "event", rename_all = "snake_case")] pub enum HelperEvent { diff --git a/hive-sh4re/src/permissions.rs b/hive-sh4re/src/permissions.rs index 9fc6ba53..6fbb42f9 100644 --- a/hive-sh4re/src/permissions.rs +++ b/hive-sh4re/src/permissions.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; /// `snake_case` group names written by the meta renderer from per-agent /// config) and expands it to the matching tool names for `--allowedTools`. /// When the env var is absent the harness falls back to `AGENT_DEFAULT`. -/// See `docs/conventions.md::Tool groups`. +/// See `docs/process/conventions.md::Tool groups`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum ToolGroup { @@ -200,7 +200,7 @@ impl ToolGroup { /// Injected into containers as `HIVE_CAPABILITIES` (comma-separated /// `snake_case`) via `meta::render_flake`. The harness reads this to /// conditionally register capability-gated MCP tools so claude only -/// sees tools it can actually invoke. See `docs/conventions.md::Capabilities`. +/// sees tools it can actually invoke. See `docs/process/conventions.md::Capabilities`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum Capability { diff --git a/hive-types/README.md b/hive-types/README.md index f3e897fa..cc942401 100644 --- a/hive-types/README.md +++ b/hive-types/README.md @@ -14,5 +14,5 @@ Foundational shared **newtypes** for the hyperhive workspace. A zero-dependency instead of flowing into a handler that has to re-validate by hand. The crate is deliberately a leaf with the smallest possible dependency footprint -so anything in the workspace can depend on it. See `docs/conventions.md` +so anything in the workspace can depend on it. See `docs/process/conventions.md` ("identity = socket") for how agent identity is modelled across the sockets. diff --git a/hivectl/src/watch.rs b/hivectl/src/watch.rs index 750e33c5..c259ae31 100644 --- a/hivectl/src/watch.rs +++ b/hivectl/src/watch.rs @@ -90,7 +90,7 @@ pub(crate) async fn watch(socket: &Path, name: &str) -> Result<()> { /// Render one SSE frame (everything up to the blank-line separator, /// already stripped) as a single compact terminal line. Not an attempt at -/// the web UI's full row taxonomy (`docs/terminal-rendering.md`) — that's +/// the web UI's full row taxonomy (`docs/web-ui/terminal-rendering.md`) — that's /// presentation for a browser, not a `tail -f`. Falls back to the raw /// payload for anything unrecognised rather than silently dropping it, so /// a shape this doesn't know about is still visible. diff --git a/nix/agent-modules/agent-service.nix b/nix/agent-modules/agent-service.nix index dd2226ed..f3721311 100644 --- a/nix/agent-modules/agent-service.nix +++ b/nix/agent-modules/agent-service.nix @@ -153,7 +153,7 @@ in not `state/` — `harness/` survives container rebuilds exactly like `state/` does, but is never bind-mounted into a parent agent's container (unlike `state/`, which a parent gets read-write for child - recovery — see `docs/persistence.md`'s "Parent access to child + recovery — see `docs/agent-lifecycle/persistence.md`'s "Parent access to child 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. Loaded with a leading `-` (optional `EnvironmentFile`), @@ -232,8 +232,8 @@ in # Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars, # RuntimeDirectory, User=, standalone-eval fallbacks): - # docs/agent-hierarchy.md::Harness systemd unit shape. PATH /bin - # auto-append behaviour: docs/gotchas.md::systemd.services.*.path + # docs/agent-lifecycle/agent-hierarchy.md::Harness systemd unit shape. PATH /bin + # auto-append behaviour: docs/process/gotchas.md::systemd.services.*.path # appends /bin to every entry. systemd.services.hive-agent = let diff --git a/nix/agent-modules/dashboard-links.nix b/nix/agent-modules/dashboard-links.nix index 3181b1b1..9ba7a5b6 100644 --- a/nix/agent-modules/dashboard-links.nix +++ b/nix/agent-modules/dashboard-links.nix @@ -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/conventions.md::Best-effort oneshot services. + # docs/process/conventions.md::Best-effort oneshot services. systemd.services.hive-dashboard-links = lib.mkIf (config.hyperhive.dashboardLinks != [ ]) { description = "write declarative dashboardLinks to agent state dir"; wantedBy = [ "multi-user.target" ]; diff --git a/nix/agent-modules/default.nix b/nix/agent-modules/default.nix index 42c9ce3c..9cdbf52f 100644 --- a/nix/agent-modules/default.nix +++ b/nix/agent-modules/default.nix @@ -151,8 +151,8 @@ # `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/gotchas.md::Containerized nix-daemon needs - # sandbox-fallback = true` + `docs/security.md` for the rationale. + # See `docs/process/gotchas.md::Containerized nix-daemon needs + # sandbox-fallback = true` + `docs/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 @@ -208,7 +208,7 @@ # 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/observability.md). + # docs/scheduler/observability.md). environment.systemPackages = [ config.hyperhive.packages.hive-metric ] diff --git a/nix/agent-modules/forge.nix b/nix/agent-modules/forge.nix index 6fe6c64b..d83cc1f1 100644 --- a/nix/agent-modules/forge.nix +++ b/nix/agent-modules/forge.nix @@ -180,7 +180,7 @@ in # One-shot: tea config.yml from the seeded forge token. Shape # contract (always exit 0, no set -e, skip-silently, re-runnable): - # docs/conventions.md::Best-effort oneshot services. + # docs/process/conventions.md::Best-effort oneshot services. # Not generated at all when no forge is configured: an absent # integration rather than one pointed at a guessed address. systemd.services.tea-login = lib.mkIf (config.hyperhive.forge.url != null) { @@ -251,7 +251,7 @@ in # service fires too early and exits with "no forge-token found". # Without this path unit, RemainAfterExit=true would prevent systemd # from ever re-running the service. See - # docs/persistence.md::forge-avatar-sync. + # docs/agent-lifecycle/persistence.md::forge-avatar-sync. systemd.paths.forge-avatar-sync = lib.mkIf (config.hyperhive.icon != null) { description = "trigger forge-avatar-sync when forge-token appears"; wantedBy = [ "multi-user.target" ]; @@ -259,7 +259,7 @@ in }; # One-shot: hyperhive.icon → Forgejo profile avatar. Shape contract: - # docs/conventions.md::Best-effort oneshot services. + # docs/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 diff --git a/nix/agent-modules/matrix.nix b/nix/agent-modules/matrix.nix index 23f5d3d2..83e93114 100644 --- a/nix/agent-modules/matrix.nix +++ b/nix/agent-modules/matrix.nix @@ -166,7 +166,7 @@ in loopback-only binding means no auth token is needed (same `allowed_hosts` reasoning as `hyperhive.mcp.httpPort`). Safe as a single fixed default across all agents (private per-container - network namespace — see docs/network.md). + network namespace — see docs/networking/network.md). ''; }; @@ -229,7 +229,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/persistence.md::Matrix per-agent daemon + token-arrival + # `docs/agent-lifecycle/persistence.md::Matrix per-agent daemon + token-arrival # trigger` for the first-boot-ordering rationale. systemd.services.hive-matrix-daemon = lib.mkIf config.hyperhive.matrix.enable { description = "long-running matrix-sdk Client + MCP daemon"; @@ -304,7 +304,7 @@ in # provisions it after agent containers come up). Without this # the daemon would exit 0 silently on first boot and the MCP # would have no backend until next restart. See - # `docs/persistence.md` (same section as above). + # `docs/agent-lifecycle/persistence.md` (same section as above). systemd.paths.hive-matrix-daemon = lib.mkIf config.hyperhive.matrix.enable { description = "trigger hive-matrix-daemon when a matrix token appears"; wantedBy = [ "multi-user.target" ]; diff --git a/nix/agent-modules/mcp.nix b/nix/agent-modules/mcp.nix index 24a0f939..6ffc80c5 100644 --- a/nix/agent-modules/mcp.nix +++ b/nix/agent-modules/mcp.nix @@ -171,7 +171,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/network.md), so `127.0.0.1:` is per-container-private + see docs/networking/network.md), so `127.0.0.1:` is per-container-private and cannot collide across agents. Override only if a container-local service already occupies this port. @@ -193,7 +193,7 @@ in self-healing, and loopback-only binding means no auth token is needed (same `allowed_hosts` reasoning as `hyperhive.mcp.httpPort`). Safe as a single fixed default across all agents (private - per-container network namespace — see docs/network.md). + per-container network namespace — see docs/networking/network.md). ''; }; diff --git a/nix/agent-modules/user.nix b/nix/agent-modules/user.nix index f4a2d8b0..ec42b296 100644 --- a/nix/agent-modules/user.nix +++ b/nix/agent-modules/user.nix @@ -161,7 +161,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/persistence.md::First-boot agent-user migration` for the + # `docs/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} diff --git a/nix/agent-modules/weston-vnc.nix b/nix/agent-modules/weston-vnc.nix index 6599f92f..1167b8eb 100644 --- a/nix/agent-modules/weston-vnc.nix +++ b/nix/agent-modules/weston-vnc.nix @@ -37,7 +37,7 @@ in # # Port allocation, weston bind-address quirk, PAM service name, the # Type=simple choice, idle-time=0: all in - # docs/gotchas.md::Weston VNC compositor. + # docs/process/gotchas.md::Weston VNC compositor. # Harness-side WebSocket relay shape: docs/web-ui/agent.md::Per-agent # endpoints (`/screen` + `/screen/ws`). diff --git a/nix/docs/default.nix b/nix/docs/default.nix index 187fde18..65dbfa56 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -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/gotchas.md::Nix options +# subtree-pick / output-tree rationale: docs/process/gotchas.md::Nix options # reference. # # `swarm.md`/`deploy.md` split off `host.md` (mara: "move swarm options @@ -102,7 +102,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/gotchas.md::Nix options + # options. Root choice matters: see docs/process/gotchas.md::Nix options # reference for the services.hyperhive consolidation history. pickSubtrees = options: roots: diff --git a/nix/host-modules/hive-c0re/default.nix b/nix/host-modules/hive-c0re/default.nix index a94a73bb..87482bc9 100644 --- a/nix/host-modules/hive-c0re/default.nix +++ b/nix/host-modules/hive-c0re/default.nix @@ -215,7 +215,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/boundary.md), so this is + # hive control (spawn/kill/destroy/deploy, docs/trust-boundary/boundary.md), so this is # an explicit opt-in allowlist — empty by default (root-only). users.groups.hive-admin = { members = cfg.adminUsers; @@ -223,7 +223,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/gateway.md::Firewall posture (host-level)`. + # the host firewall. See `docs/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 @@ -368,7 +368,7 @@ in # `0660 root:hive-admin` — group-owned by `hive-admin` so operators in # that group (services.hyperhive.c0re.adminUsers) drive `hivectl` # without root; an empty adminUsers list leaves the group memberless, - # so it stays effectively root-only. See docs/boundary.md. + # so it stays effectively root-only. See docs/trust-boundary/boundary.md. SocketMode = "0660"; SocketGroup = "hive-admin"; # `0751` (traverse-only, no listing) so hive-admin can reach the socket diff --git a/nix/host-modules/hive-c0re/environment.nix b/nix/host-modules/hive-c0re/environment.nix index ffe9ba18..3345bd06 100644 --- a/nix/host-modules/hive-c0re/environment.nix +++ b/nix/host-modules/hive-c0re/environment.nix @@ -57,7 +57,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/conventions.md::Hive identity. `domain` is + # full chain in docs/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; } @@ -141,7 +141,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/gateway.md::HIVE_FORGE_URL`. + # gated on hyperhive being enabled). See `docs/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 @@ -197,7 +197,7 @@ in # Availability flags read by the dashboard's `/api/state`. # Matrix GUI lives entirely on the gateway nginx (matrix tab # only shows when both flags are on). Gateway routing detail: - # docs/gateway.md::Vhost map. + # docs/networking/gateway.md::Vhost map. HIVE_MATRIX_GUI_ENABLED = "1"; } // { diff --git a/nix/host-modules/hive-c0re/options.nix b/nix/host-modules/hive-c0re/options.nix index b69482f9..0994a649 100644 --- a/nix/host-modules/hive-c0re/options.nix +++ b/nix/host-modules/hive-c0re/options.nix @@ -427,7 +427,7 @@ `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/boundary.md`. + control — spawn / kill / destroy / deploy — see `docs/trust-boundary/boundary.md`. Keep the list to trusted operators. Empty (the default) keeps the socket root-only, as before. ''; diff --git a/nix/host-modules/hive-ci.nix b/nix/host-modules/hive-ci.nix index a77a83a6..9907cbdc 100644 --- a/nix/host-modules/hive-ci.nix +++ b/nix/host-modules/hive-ci.nix @@ -58,7 +58,7 @@ in # gateway at `http://${forgeCfg.domain}` (resolved to the bridge IP # via `networking.extraHosts`; gateway port 80 is always open on the # bridge; `addSSL = true` means HTTP is served alongside HTTPS without - # a redirect). See docs/network.md. + # a redirect). See docs/networking/network.md. # Container is non-ephemeral: the runner's registered credentials # survive restarts (gitea-actions-runner writes them to its stateDir # on first registration and reuses them on every subsequent start). @@ -74,7 +74,7 @@ in # Nix builds inside the container use the shared /nix/store (standard # nixos-container behaviour) with sandbox-fallback = true, because # nspawn containers can't create the user-namespaces that nix sandboxing - # requires. See docs/gotchas.md. + # requires. See docs/process/gotchas.md. options.services.hyperhive.deploy.forgejo.ci = { enable = lib.mkOption { @@ -218,7 +218,7 @@ in # runner reaches the forge via the gateway — and cannot reach # host-loopback (127.0.0.1:7000 dashboard, raw forge port, etc.). # Requires `forge.behindGateway = true` (asserted in the options - # block above). See docs/network.md. + # block above). See docs/networking/network.md. privateNetwork = true; hostBridge = networkCfg.bridgeName; @@ -285,7 +285,7 @@ in # sandboxing always fails. Fall back to unsandboxed builds. # Moot once every nix invocation in the container routes # through the host daemon (the daemon governs sandboxing). - # See docs/gotchas.md and nix/agent-modules/default.nix. + # See docs/process/gotchas.md and nix/agent-modules/default.nix. nix.settings.sandbox-fallback = lib.mkForce true; # Degrade to a local build when a remote builder is unreachable # rather than failing the check. `fallback` is a client-side diff --git a/nix/host-modules/hive-forge/default.nix b/nix/host-modules/hive-forge/default.nix index f80b3abe..0a5f4dc2 100644 --- a/nix/host-modules/hive-forge/default.nix +++ b/nix/host-modules/hive-forge/default.nix @@ -107,7 +107,7 @@ in # Private Forgejo in a `hive-forge` nixos-container, shared host # netns. Agents reach it at `forge.` via the gateway. State # at `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/` survives - # restart. See `docs/gateway.md::hive-forge container shape`. + # restart. See `docs/networking/gateway.md::hive-forge container shape`. # External Forgejo/Gitea/Codeberg-compatible forges (beyond the mandatory # internal one) are entirely dashboard-provisioned — no nix config here. @@ -179,7 +179,7 @@ in Public hostname for the forge. Doubles as both the forgejo `DOMAIN` setting (clone URLs forgejo advertises) AND the gateway vhost server-name when `behindGateway = true` - (sub-domain routing — see `docs/gateway.md`). + (sub-domain routing — see `docs/networking/gateway.md`). Defaults to `forge.''${services.hyperhive.swarm.domain}` — the swarm's domain, not this hive's, because a swarm runs **one** @@ -251,7 +251,7 @@ in description = '' Serve forgejo through the hive-gateway nginx as a sub-domain vhost (`server_name = cfg.domain`) instead of directly on - `httpPort` (sub-domain routing — see `docs/gateway.md`). + `httpPort` (sub-domain routing — see `docs/networking/gateway.md`). When `true`: - The gateway adds a `server { server_name = ''${cfg.domain}; }` @@ -473,7 +473,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/gateway.md`. + # `docs/networking/gateway.md`. services.nginx.virtualHosts = lib.optionalAttrs cfg.behindGateway { "${cfg.domain}" = (gatewayCfg.lib.tlsFor cfg.domain) // { listen = gatewayCfg.lib.listen; diff --git a/nix/host-modules/hive-gateway/default.nix b/nix/host-modules/hive-gateway/default.nix index 5703b588..13df618c 100644 --- a/nix/host-modules/hive-gateway/default.nix +++ b/nix/host-modules/hive-gateway/default.nix @@ -5,7 +5,7 @@ # answers on the hive bridge, so neither can be confined to a network # namespace of its own. # Full vhost map + discovery flow + design rationale in -# `docs/gateway.md`. Layout: ./options.nix (option declarations), +# `docs/networking/gateway.md`. Layout: ./options.nix (option declarations), # ./vhosts.nix (the nginx virtual-host tree), ./error-pages.nix # (styled static pages), ./dnsmasq.nix (resolver + DHCP config). { @@ -313,7 +313,7 @@ in # from an interrupted oneshot, etc.). The leaf covers the bare # hive domain plus `forge.`, `matrix.` and `*.${hyperhiveDomain}` # so all sub-domains validate under the same cert + the hive CA. - # See `docs/gateway.md` ("Self-signed TLS"). + # See `docs/networking/gateway.md` ("Self-signed TLS"). systemd.services.hive-gateway-self-signed-cert = lib.mkIf useSelfSigned { description = "Import host-generated TLS leaf for hive-gateway"; wantedBy = [ "multi-user.target" ]; @@ -444,7 +444,7 @@ in }; # `/etc/hosts` entries for local dev — the bare hive domain plus - # every name a service module contributed. See `docs/gateway.md` + # every name a service module contributed. See `docs/networking/gateway.md` # ("Local dev"). # # This used to restate the per-service list a THIRD time (after the diff --git a/nix/host-modules/hive-gateway/error-pages.nix b/nix/host-modules/hive-gateway/error-pages.nix index f1ad62e9..3c42df76 100644 --- a/nix/host-modules/hive-gateway/error-pages.nix +++ b/nix/host-modules/hive-gateway/error-pages.nix @@ -1,7 +1,7 @@ # Static error/help pages the gateway serves for routes it has # special-cased, all rendered from one Catppuccin-styled template. # Useful pages instead of nginx's default 404/502 — see -# `docs/gateway.md::Per-agent error pages` for the design rationale + +# `docs/networking/gateway.md::Per-agent error pages` for the design rationale + # page-vs-status semantics. Consumed by ./vhosts.nix. { pkgs }: let diff --git a/nix/host-modules/hive-gateway/vhost-lib.nix b/nix/host-modules/hive-gateway/vhost-lib.nix index 77aaa6f6..0b1fb91f 100644 --- a/nix/host-modules/hive-gateway/vhost-lib.nix +++ b/nix/host-modules/hive-gateway/vhost-lib.nix @@ -53,7 +53,7 @@ in # The gateway always terminates TLS: self-signed is the implicit # floor when neither `tls.certDir` nor ACME is set, so there is no # http-only mode. Listen addresses every vhost shares — plain http - # on `cfg.port` plus TLS on `cfg.httpsPort`. See `docs/gateway.md` + # on `cfg.port` plus TLS on `cfg.httpsPort`. See `docs/networking/gateway.md` # ("TLS modes"). listen = [ { @@ -92,7 +92,7 @@ in # — invisibly, it works and keeps working against any certificate at # all. Full reasoning for every directive here (plus two real # footguns — session-cache keying, and a `Host`-header clobber that - # can recurse a subrequest into itself) is in docs/gateway.md's + # can recurse a subrequest into itself) is in docs/networking/gateway.md's # "Dialing another vhost by name" section — read it before touching # this. verifiedProxyTo = name: '' diff --git a/nix/host-modules/hive-gateway/vhosts.nix b/nix/host-modules/hive-gateway/vhosts.nix index e54dddda..0a9d2752 100644 --- a/nix/host-modules/hive-gateway/vhosts.nix +++ b/nix/host-modules/hive-gateway/vhosts.nix @@ -36,13 +36,13 @@ let # mints into responses (well-known JSON, the deprecated # `/matrix/*` 301 redirect, future absolute-URL needs): # always `https://` (matrix-spec compliance) — the canonical - # 443 elides the port. See `docs/gateway.md` ("Self-signed TLS"). + # 443 elides the port. See `docs/networking/gateway.md` ("Self-signed TLS"). publicScheme = "https"; publicPort = cfg.httpsPort; publicPortSuffix = if publicPort == 443 then "" else ":${toString publicPort}"; # `/matrix/*` → 301 → `matrix./$1` (legacy deep-link - # shim during the fluffychat sub-domain move). See `docs/gateway.md`. + # shim during the fluffychat sub-domain move). See `docs/networking/gateway.md`. matrixRedirectLocations = lib.optionalAttrs (matrixCfg.enable && matrixCfg.gui.enable && matrixCfg.gatewayHost != null) ( @@ -61,7 +61,7 @@ let # `.well-known/matrix/{client,server}` discovery JSON. Points # clients at `matrixCfg.gatewayHost` when set; falls back to direct # `:`. CORS `*` per matrix spec. The `m.server` - # port-8448 carve-out is documented inline. See `docs/gateway.md`. + # port-8448 carve-out is documented inline. See `docs/networking/gateway.md`. wellKnownLocations = lib.optionalAttrs matrixCfg.enable ( let clientBaseUrl = @@ -77,7 +77,7 @@ let # (which drops :443) is right for the client base_url above but # wrong for federation delegation. Without this, peers federate to # :8448 (closed) while the endpoint actually lives on - # the gateway's 443 vhost. See docs/gateway.md discovery flow. + # the gateway's 443 vhost. See docs/networking/gateway.md discovery flow. serverHostPort = if matrixCfg.gatewayHost != null then "${matrixCfg.gatewayHost}:${toString publicPort}" diff --git a/nix/host-modules/hive-matrix.nix b/nix/host-modules/hive-matrix.nix index bc836acc..5099973b 100644 --- a/nix/host-modules/hive-matrix.nix +++ b/nix/host-modules/hive-matrix.nix @@ -84,7 +84,7 @@ let # skips the dart web-worker compile + the emscripten native_imaging # build. Two derivations below cover both. Full rationale (why # passthru.pubspecLock.dependencySources, why `dontConfigure`, why - # `make -C js`, why build-CWD-relative dart path): docs/matrix.md:: + # `make -C js`, why build-CWD-relative dart path): docs/integrations/matrix.md:: # fluffychat-web build fixes. fluffychat-web-imaging = pkgs.stdenv.mkDerivation { @@ -153,8 +153,8 @@ in # optional fluffychat-web client at matrix./. Container shape, # serverName vs gatewayHost split, provisioning flow (registration # token + LoadCredential), assertion rationale, initial rollout - # settings: docs/matrix.md. Vhost map + discovery flow + tuning - # knobs: docs/gateway.md. + # settings: docs/integrations/matrix.md. Vhost map + discovery flow + tuning + # knobs: docs/networking/gateway.md. # Matrix moved under `swarm` when the swarm-global services were # consolidated. One rename for the namespace: the subtree comes with it, @@ -291,7 +291,7 @@ in Defaults to `chat.''${services.hyperhive.swarm.domain}` — the swarm's domain, because a swarm runs **one** homeserver. Set to `null` to skip the gateway vhost (tuwunel stays direct on - `httpPort`). See `docs/gateway.md` for the vhost map + matrix + `httpPort`). See `docs/networking/gateway.md` for the vhost map + matrix discovery flow, and the federation port-8448 caveat at the bottom of that doc. @@ -410,7 +410,7 @@ in Requires `matrix.gatewayHost != null` (default `matrix.` when hive-domain set); the gateway itself always runs. When off, the dashboard's `M4TR1X →` tab is hidden. See - `docs/gateway.md` for the discovery flow that lets clients + `docs/networking/gateway.md` for the discovery flow that lets clients auto-find the sub-domain. ''; }; @@ -528,7 +528,7 @@ in }; # Accept-header SPA map, used only by the `/` location below (see - # docs/gateway.md "SPA fallback"): text/html → index.html, else a + # docs/networking/gateway.md "SPA fallback"): text/html → index.html, else a # sentinel so `try_files` falls through to 404. `appendHttpConfig` # is a `lines` option, so this merges with anything else the host # contributes instead of replacing it. @@ -601,7 +601,7 @@ in # hive-network.nix) when not set explicitly, so no separate # domain/serverName assertion is needed here. gatewayHost may not be # "" (same footgun as forge.domain — nginx rejects an empty - # server_name). docs/matrix.md::Assertion rationale. + # server_name). docs/integrations/matrix.md::Assertion rationale. assertions = [ { assertion = cfg.gatewayHost == null || cfg.gatewayHost != ""; @@ -764,7 +764,7 @@ in # Activation-time token generation — without this the bind-mount # would hand tuwunel an empty file on first boot and break every # registration until restart. Idempotent; - # docs/matrix.md::Provisioning flow. + # docs/integrations/matrix.md::Provisioning flow. # Tell an operator whose homeserver already exists that `serverName` is # unpinned, at the one moment they are looking: the rebuild. # @@ -895,7 +895,7 @@ in # `environment.etc."resolv.conf".text` is `nameserver `. # This container always shares the host netns # (`privateNetwork = false`), so it reaches `bridgeIp` regardless - # of agent-container isolation. See `docs/network.md`. + # of agent-container isolation. See `docs/networking/network.md`. networking = { # resolvconf is taken out of the loop entirely; the static # `environment.etc."resolv.conf"` below is the sole source of diff --git a/nix/host-modules/hive-network.nix b/nix/host-modules/hive-network.nix index c4eed090..0a8b1ffa 100644 --- a/nix/host-modules/hive-network.nix +++ b/nix/host-modules/hive-network.nix @@ -31,7 +31,7 @@ in { # Hive-internal network — host-side bridge + per-agent DNS resolver. # Always active when hyperhive is enabled: agent containers run in - # private netns behind the bridge. Full design: docs/network.md. + # private netns behind the bridge. Full design: docs/networking/network.md. imports = [ (lib.mkRemovedOptionModule [ "services" "hyperhive" "network" "enable" ] '' @@ -234,7 +234,7 @@ in }) # Container isolation overlay — now unconditional (the shared-netns - # mode was removed). See docs/network.md#container-isolation. + # mode was removed). See docs/networking/network.md#container-isolation. (lib.mkIf config.services.hyperhive.enable { # Agents route internet traffic via the bridge; NAT masquerades their RFC-1918 IPs. diff --git a/nix/host-modules/hive-priv.nix b/nix/host-modules/hive-priv.nix index fdf5fc74..05fabadb 100644 --- a/nix/host-modules/hive-priv.nix +++ b/nix/host-modules/hive-priv.nix @@ -1,5 +1,5 @@ # hive-priv — the narrow root privileged helper hive-c0re delegates -# to, socket-activated at /run/hive/priv.sock. See docs/boundary.md +# to, socket-activated at /run/hive/priv.sock. See docs/trust-boundary/boundary.md # for the operator/agent trust-boundary design. { pkgs, diff --git a/nix/host-modules/hive-tls.nix b/nix/host-modules/hive-tls.nix index db3163b6..2ebe6b67 100644 --- a/nix/host-modules/hive-tls.nix +++ b/nix/host-modules/hive-tls.nix @@ -276,7 +276,7 @@ in # So the issuer is a long-lived **hive CA** held on the host. The # gateway serves a **leaf** signed by that CA (via the `tls.certDir` # bind-mount path); agents and federation peers trust the *CA* once, - # and leaf rotation never re-breaks them. See `docs/gateway.md` + # and leaf rotation never re-breaks them. See `docs/networking/gateway.md` # ("Self-signed TLS"). # # That CA is self-signed by default. Under diff --git a/nix/host-modules/hyperhive.nix b/nix/host-modules/hyperhive.nix index b6d177d2..2925c214 100644 --- a/nix/host-modules/hyperhive.nix +++ b/nix/host-modules/hyperhive.nix @@ -33,7 +33,7 @@ in # matrix bakes it in on first boot and the gateway/forge/agent URLs all # derive from it (no safe default). Full identity-surface # context (HYPERHIVE_HIVE_DOMAIN / HIVE_NAME / SWARM_NAME env-var - # chain → identity.rs → claude prompt): docs/conventions.md:: + # chain → identity.rs → claude prompt): docs/process/conventions.md:: # Hive identity (label + domain + display names). options.services.hyperhive.domain = lib.mkOption { type = lib.types.nullOr lib.types.str; @@ -133,7 +133,7 @@ in }; # Human display name for this hive. Distinct from the DNS domain - # above (machine-readable) — see docs/conventions.md::Hive identity + # above (machine-readable) — see docs/process/conventions.md::Hive identity # for the domain-vs-name-vs-swarm distinction + the env-var # propagation chain. The swarm's display name is # `services.hyperhive.swarm.name`, one level out: this hive is named diff --git a/nix/host-modules/swarm-authelia.nix b/nix/host-modules/swarm-authelia.nix index 38fbb08b..bccca68e 100644 --- a/nix/host-modules/swarm-authelia.nix +++ b/nix/host-modules/swarm-authelia.nix @@ -42,7 +42,7 @@ let # `swarmctl user add --group ` is what grants it — the two must # agree, and one constant is how they stay agreeing. # - # ⚠️ `admins` and not a new word, because `docs/setup.md` and + # ⚠️ `admins` and not a new word, because `docs/getting-started/setup.md` and # `docs/swarm/sso.md` have been telling every operator to create # exactly that group since the bootstrap step existed. This is the # first rule that CONSUMES a group name; picking a different one would diff --git a/nix/host-modules/swarm-snapshot-store.nix b/nix/host-modules/swarm-snapshot-store.nix index bcbafc99..24694ab9 100644 --- a/nix/host-modules/swarm-snapshot-store.nix +++ b/nix/host-modules/swarm-snapshot-store.nix @@ -16,7 +16,7 @@ # Confinement is a property of the DEPLOYMENT, not of this unit: in a # real swarm the store is its own small VM (the machine is the # boundary); in the all-local case it's a container on the c0re host. -# The module hardcodes neither. docs/snapshot-store.md covers what the +# The module hardcodes neither. docs/networking/snapshot-store.md covers what the # deployment is expected to provide. { pkgs, @@ -201,7 +201,7 @@ in # nature. The hardening below is defence in depth and NOT a # boundary: a process holding CAP_SYS_ADMIN can call mount(2) and # undo the namespace these directives set up. The real boundary is - # the deployment --- see docs/snapshot-store.md. + # the deployment --- see docs/networking/snapshot-store.md. systemd.services."swarm-snapshot-store@" = { description = "hyperhive swarm snapshot store receiver"; after = [ "swarm-snapshot-store.socket" ]; diff --git a/nix/packages/assets.nix b/nix/packages/assets.nix index 2eea1304..4dab97f3 100644 --- a/nix/packages/assets.nix +++ b/nix/packages/assets.nix @@ -5,7 +5,7 @@ # Branding SVG/PNG family + claude prompts, split out from the rust # workspace so a tweak here doesn't invalidate the rust cargo cache. -# Rationale + agent-configs PNG rendering: docs/gotchas.md::Split asset +# Rationale + agent-configs PNG rendering: docs/process/gotchas.md::Split asset # derivations away from the rust workspace. # # Output layout: diff --git a/swarm-controller/src/main.rs b/swarm-controller/src/main.rs index 7e38020a..3cda4121 100644 --- a/swarm-controller/src/main.rs +++ b/swarm-controller/src/main.rs @@ -673,7 +673,7 @@ impl axum::response::IntoResponse for StatusUnavailable { /// Every error this daemon returns, in one shape. /// /// RFC 9457 `application/problem+json` is the hive-wide contract for HTTP -/// error bodies (`docs/conventions.md`), and the operator UIs read `detail` +/// error bodies (`docs/process/conventions.md`), and the operator UIs read `detail` /// for display. A bare string forces the reader to treat the entire body as /// the message, which is the difference between a UI that can offer "copy the /// cause" and one that can only dump a response. diff --git a/swarm-controller/src/vcs_metrics.rs b/swarm-controller/src/vcs_metrics.rs index 10d51723..96a10eb5 100644 --- a/swarm-controller/src/vcs_metrics.rs +++ b/swarm-controller/src/vcs_metrics.rs @@ -1,6 +1,6 @@ //! OTEL export of commit/push activity across every forge repo in the //! instance — the metric forgejo's own native `/metrics` endpoint (see -//! `docs/forge.md`'s note on the survey that shipped it) does not carry. +//! `docs/integrations/forge.md`'s note on the survey that shipped it) does not carry. //! That endpoint gives issue/comment/repo counts because Forgejo tracks //! those as durable rows it can `SELECT COUNT(*)` on demand; a commit or a //! push is not stored anywhere as a row to count, only observed in passing