docs: restructure into topic subdirectories, collapse duplicated index
Per mara's go-ahead on hyperhive#3902 ("getting started is good, but
terminal rendering does not go in there i think"):
Moved 21 top-level docs/*.md files into 7 new topic subdirectories
(existing web-ui/, turn-loop/, swarm/, tools/, crates/ untouched):
getting-started/ setup.md
agent-lifecycle/ agent-hierarchy.md, approvals.md, persistence.md
trust-boundary/ boundary.md, security.md
integrations/ forge.md, matrix.md, github.md, knowledge.md
networking/ gateway.md, network.md, snapshot-store.md
scheduler/ jobq.md, coordinator.md, ci.md, observability.md
process/ conventions.md, gotchas.md, pr-review-gate.md
web-ui/ terminal-rendering.md (moved into the EXISTING dir,
per mara's correction to the original getting-started
guess -- it's UI implementation detail, not onboarding)
The physical layout now matches docs/README.md's own topical headers,
which already amounted to this taxonomy -- see the scoping comment on
the issue for the two findings that motivated this (a genuine
duplication between CLAUDE.md's old "Reading paths" list and
docs/README.md's grouped one, since drifted out of sync with each
other; and the flat layout not matching the grouping we already had).
Fixed every cross-reference this moved across the whole repo (~120
files: docs/ internal links at every depth, Rust doc comments, nix
module option docs, crate READMEs) -- verified two ways: a grep sweep
confirming zero remaining references to any old path, and a script
that resolves every markdown link in docs/**/*.md + CLAUDE.md +
README.md against the filesystem and reports anything that doesn't
exist (zero broken links).
Collapsed CLAUDE.md's "Reading paths" section (the duplicate) down to
a pointer at docs/README.md, now the single index. Rewrote
docs/README.md itself to use the new subdirectory paths and added the
one doc it was missing that CLAUDE.md's old copy had (pr-review-gate.md).
Classified all 22 docs/*.md files first via a haiku subagent (mara's
suggestion) on two axes -- proposed grouping and operator-vs-
implementation focus -- before finalizing the taxonomy; spot-checked
the report and found internal inconsistencies (its classification
table disagreed with its own summary section for a few files), so this
taxonomy is my original proposal + the one correction mara gave
directly, not a blind application of the subagent's table. The
operator-focus data it gathered is still useful for a follow-up
content pass (docs skewing 'mixed' rather than pure operator-facing),
not addressed in this PR -- structure only.
nix fmt clean, both pre-push lints clean.
This commit is contained in:
parent
e4a22b4190
commit
07b62612b0
124 changed files with 301 additions and 377 deletions
96
CLAUDE.md
96
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`
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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 `<parent>` / `<children>` 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 <name> 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)
|
||||
|
|
@ -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/<name>`) 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`.
|
||||
|
|
@ -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-<n>.url = git+http://<forge>/agent-configs/<n>.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).
|
||||
|
|
@ -46,7 +46,7 @@ hivectl github set-token <agent> --token <pat> # 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).
|
||||
|
|
@ -4,11 +4,11 @@ Private Matrix homeserver (matrix-tuwunel — the conduwuit
|
|||
successor) wrapped in a nixos-container, plus optional fluffychat-web
|
||||
client at `chat.<swarm-domain>/` (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
|
||||
|
||||
|
|
@ -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
|
||||
`<dir>/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://<host>/.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 `<hive>` plus wildcard `*.<hive>`, 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 `<hive>`, 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 `<hive>` plus wildcard `*.<hive>`, 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 `<hive>`, 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`.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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:
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ authelia binds loopback only. The **gateway** on the host running it
|
|||
publishes it as `auth.<swarm.domain>` — 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
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ fine and still gets bounced.
|
|||
swarmctl user add <you> --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.
|
||||
|
|
|
|||
|
|
@ -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 <pat> # 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/<name>/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
|
||||
|
|
|
|||
|
|
@ -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/<name>` 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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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:<dashboard_port>`, 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**:
|
||||
|
|
@ -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 <name> 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?"** →
|
||||
|
|
|
|||
|
|
@ -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` →
|
||||
|
|
|
|||
|
|
@ -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/<name>` (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://<host>:<container.port>` (direct TCP fallback). Forge
|
||||
links resolve against `state.forge_public_url` (sourced from
|
||||
|
|
|
|||
|
|
@ -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/<name>/` 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
// Collect all tool names, deduplicating while preserving order.
|
||||
|
|
|
|||
|
|
@ -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<SdkMeterProvider> {
|
|||
// 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()
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
//! Claude credentials live at `$HOME/.claude` (resolves to
|
||||
//! `/home/<agent-name>/.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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<tokio::net::UnixListener> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if let Some(parent) = path.parent() {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
//! `<parent>` 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<String, Option<String>>) -> Vec<Strin
|
|||
/// straight into [`crate::broker::Broker::send`] without
|
||||
/// borrow-juggling around the temporary lookup.
|
||||
///
|
||||
/// Rules + rationale: `docs/conventions.md::Recipient sentinels`.
|
||||
/// Rules + rationale: `docs/process/conventions.md::Recipient sentinels`.
|
||||
/// Fast path: ordinary recipient names short-circuit before any
|
||||
/// disk read — only `<parent>` triggers `read()` on `topology.json`.
|
||||
#[must_use]
|
||||
|
|
@ -262,7 +262,7 @@ pub fn default_seed(agent_names: &[String]) -> BTreeMap<String, Option<String>>
|
|||
/// 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<String, Option<String>>,
|
||||
child: &str,
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
/// The Claude model the agent's harness is currently using, read from
|
||||
|
|
|
|||
|
|
@ -867,7 +867,7 @@ impl Coordinator {
|
|||
/// `_or_root` resolves to the literal string `"<root>"` when the
|
||||
/// slot is `None`, keeping the wording consistent with the
|
||||
/// `<parent>` 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.
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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/<n>/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> {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ pub(super) struct StateSnapshot {
|
|||
/// runtime-generated `agents.conf` include file — see
|
||||
/// `gateway_nginx.rs`). The `false` branch (direct
|
||||
/// `http://<hostname>:<port>/` 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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. \
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! `/agent/<name>/` 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<BindMount>) {
|
|||
/// 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.
|
||||
|
|
|
|||
|
|
@ -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#<name>` 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 —
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<u64>) -> 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/<n>/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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1073,7 +1073,7 @@ pub(crate) fn handle_send(
|
|||
// Resolve magic-recipient sentinels (`<parent>`) 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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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<u64>,
|
||||
|
|
@ -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<String>,
|
||||
|
|
@ -80,14 +80,14 @@ pub enum Request {
|
|||
SetStatus { text: String },
|
||||
/// Fetch identity + status for an agent. `name = None` =
|
||||
/// self-introspection; `Some(<agent>)` = 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<Ident>,
|
||||
},
|
||||
/// 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<LooseEnd> },
|
||||
/// `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")]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 <kind>` 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<NotifySubjectType>) -> &'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<S: Source>(
|
||||
client: &reqwest::Client,
|
||||
|
|
@ -305,7 +305,7 @@ async fn format_notification<S: Source>(
|
|||
|
||||
// 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<S: Source>(
|
|||
// 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<OffsetDateTime>,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ pub const AGENT_SOCKET_DIR: &str = "/run/hive-agent";
|
|||
/// Per-agent web UI unix socket path — `AGENT_SOCKET_DIR/<name>/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 <name> create`. See `docs/approvals.md::Approval kinds
|
||||
/// agent <name> create`. See `docs/agent-lifecycle/approvals.md::Approval kinds
|
||||
/// (wire shapes)`. Wire name kept as `Spawn` (unrenamed underneath
|
||||
/// the CLI-verb rename — `hivectl agent <name> 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 <name> 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<Ident>,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 <gw>`)
|
||||
/// 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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ async fn main() -> Result<()> {
|
|||
fn socket_listener() -> Result<UnixListener> {
|
||||
// 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<i32> = 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(())
|
||||
|
|
|
|||
|
|
@ -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/<agent>` 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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" ];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue