diff --git a/docs/agent-lifecycle/agent-hierarchy.md b/docs/agent-lifecycle/agent-hierarchy.md index 3919231a..8e4f8b08 100644 --- a/docs/agent-lifecycle/agent-hierarchy.md +++ b/docs/agent-lifecycle/agent-hierarchy.md @@ -178,8 +178,8 @@ nspawn agent. Open questions, not yet wired: One harness serve binary (`hive-agent`, with its `hive-agent-mcp` sibling), one shared `nix/agent-modules/` tree, one service unit -(`systemd.services.hive-agent`) for all agents. There is no separate -manager service name or role distinction in the harness — privilege +(`systemd.services.hive-agent`) for all agents. No separate manager +service name or role distinction exists in the harness — privilege differences live server-side in the broker socket (which tool groups and manager-surface calls each agent receives). diff --git a/docs/agent-lifecycle/approvals.md b/docs/agent-lifecycle/approvals.md index c09532e7..544afc9c 100644 --- a/docs/agent-lifecycle/approvals.md +++ b/docs/agent-lifecycle/approvals.md @@ -56,7 +56,7 @@ flow. Config changes flow through a **forge pull request** on the agent's `agent-configs/` repo — the same surface agents use for code PRs. -There is no bespoke MCP tool for config changes: opening the PR IS the +No bespoke MCP tool exists for config changes: opening the PR IS the request. 1. The submitting agent (the child's parent, holding the `approvals` @@ -349,7 +349,7 @@ per-hive config — is the idempotent reconciler called by `spawn`, `destroy`, `rebuild`, and the startup migration. Renders `flake.nix` from the agent list; if it differs from disk, runs `nix flake lock` + commits as `regenerate meta flake` (or -`seed meta from N agent(s)` on the very first call). +`seed meta from N agent(s)` on the first call). The root agent has `/meta` RO-bound inside its container: `git -C /meta log --oneline` is the swarm-wide deploy log, @@ -499,8 +499,8 @@ current config — not an editing surface. An agent with the `approvals` tool group submits a change the same way any other change is made: **clone the child's config repo from the forge into its own state dir, commit on a branch, open a PR**, and let -the operator review and approve it. There is deliberately no second, -mount-shaped path that reaches the same file without the review. +the operator review and approve it. By design, no second, mount-shaped +path reaches the same file without the review. Agents holding the `can_manage_top_level_agents` topology role (see `hive-c0re/src/agent_config/topology.rs`) get additional host-side @@ -593,8 +593,8 @@ updates the root agent itself. The system prompt (`hive-agent/prompts/system.md`, rendered by `hive-agent/src/prompt.rs`) is the **same for every agent**; what varies is which MCP tools are surfaced (gated by tool groups and -capabilities in `agent.nix`). There is no `role:manager` block that -renders only for the root agent. The root agent's approval-gating +capabilities in `agent.nix`). No `role:manager` block renders only +for the root agent. The root agent's approval-gating behaviour comes from its CLAUDE.md / agent-specific instructions, not the system prompt template. diff --git a/docs/agent-lifecycle/persistence.md b/docs/agent-lifecycle/persistence.md index 812de3e2..f36ce344 100644 --- a/docs/agent-lifecycle/persistence.md +++ b/docs/agent-lifecycle/persistence.md @@ -72,7 +72,7 @@ power-intent registry: last_fired_at_unix / last_result`. `ON DELETE CASCADE` from `scheduled_prompts(id)` — requires `PRAGMA foreign_keys = ON` per connection (set at open). -- `agent_power` — one tiny row per agent: `agent PK / wanted (up | +- `agent_power` — one row per agent: `agent PK / wanted (up | offline) / updated_at`, owned by `hive-c0re/src/stores/power.rs`. This is the durable power *intent* the job queue reconciles the observed container state against; intent survives hive-c0re @@ -158,7 +158,7 @@ host-side). Best-effort and created on first write (`CREATE TABLE IF NOT EXISTS`), so it's absent until a bash task runs. -turn-stats.sqlite has **no vacuum** — it's one tiny row per turn +turn-stats.sqlite has **no vacuum** — it's one small row per turn (~hundreds of KB even over months), read directly by the `/stats` page and the hive-wide stats view, so pruning it would only lose trend history for no space gain. @@ -409,7 +409,7 @@ Contents: The root agent has the meta dir RO-mounted at `/meta/`. -There is no longer a `.meta-migration-done` marker: the +The `.meta-migration-done` marker no longer exists: the one-shot container repoint it guarded has been removed, since containers are rendered onto `meta#` at creation. A stale marker file left over from an older hive is inert and can be @@ -516,7 +516,7 @@ marker-guarded so the substantive moves only happen once per container lifetime: 1. **`${homeDir}` exists with the right ownership** — covers the - very first boot before `useradd`'s `createHome` has had a + the first boot before `useradd`'s `createHome` has had a chance to chown. Also re-applies on every rebuild in case the meta-flake's per-agent name evolves (rare). 2. **Migrate any leftover `/root/.claude` content into diff --git a/docs/integrations/forge.md b/docs/integrations/forge.md index 64705542..e33f2dfd 100644 --- a/docs/integrations/forge.md +++ b/docs/integrations/forge.md @@ -117,7 +117,7 @@ read on forge straight away (`PATCH /notifications/threads/{id}`). The broker inbox is the durable work queue now — each delivered wake is a sqlite row with its own ack lifecycle — so the forge unread flag no longer needs to track whether the agent has _processed_ a -notification. Clearing it on delivery keeps forge's unread set **tiny +notification. Clearing it on delivery keeps forge's unread set **small by construction**: at rest it holds only threads that failed to deliver plus whatever arrived since the last 30s poll. @@ -140,7 +140,7 @@ wake already carries the notification body, so _delivery is the read_. An agent that wants the full thread still runs `hive-forge comments` / `view`. -**In-process dedupe (tiny, ephemeral).** A single-process map (thread +**In-process dedupe (small, ephemeral).** A single-process map (thread id → last-delivered `updated_at`) guards the narrow window where a mark-read call _transiently fails_ and the thread reappears unread in the next poll before its `updated_at` bumps — so a flaky PATCH doesn't @@ -165,7 +165,7 @@ The poller starts disabled and stays that way for any of: - Initial client construction fails (the typed `forgejo-api` client for the API calls, or the plain reqwest client kept for the best-effort enrichment fetches of server-provided subject URLs; - both extremely unlikely; treated as fatal-to-the-task only). + both unlikely; treated as fatal-to-the-task only). Disabled = the spawned task returns immediately. All other failure modes (HTTP errors, parse errors, mark-read failures) are @@ -260,7 +260,7 @@ path. But a merged/closed subject **keeps** its `latest_comment_url` set, so a just-merged PR that had any prior discussion would route to the comment path and render `[comment on PR]` (with a stale pre-merge comment body) instead of `[PR merged]` — the agent never learns its PR merged. -So when the notification IS the merge/close transition — its +When the notification IS the merge/close transition — its event time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` of the subject's `closed_at` (set for both `merged` and `closed`) — the state-change path wins even with a comment url present @@ -281,7 +281,7 @@ its `created_at` is strictly **after** the subject's `closed_at` (`comment_is_after_close`) — that is it raced the merge rather than being the pre-merge last comment the subject keeps — append it as a `comment by : ` block before the meta suffix -(`fresh_post_close_comment_tail`). So the wake carries **both** `[PR merged]` +(`fresh_post_close_comment_tail`). The wake carries **both** `[PR merged]` and the racing comment. The kept pre-merge comment (created before `closed_at`) is left off, a self-authored racing comment is dropped (don't echo the agent's own write), and a missing/unparseable `created_at`/ @@ -295,7 +295,7 @@ A review submitted with **no body** carries no `latest_comment_url`, so it misses the comment path and lands on the state-change path with `state == "open"` — exactly like a freshly opened PR. Labeling that `new PR` is misleading: agents dismiss it as a duplicate of the -original open notification and miss the review. So the `open` +original open notification and miss the review. The `open` state only earns the `new ` label when the notification's event time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` (120s) of the subject's `created_at`. Anything later is labeled `activity on ` diff --git a/docs/integrations/github.md b/docs/integrations/github.md index f0f83fd4..9d3a6061 100644 --- a/docs/integrations/github.md +++ b/docs/integrations/github.md @@ -13,8 +13,8 @@ injected into the agent's state dir out of band. ## Enabling The integration is **on by default** for every agent (`hyperhive.github.enable -= true`), inert until a PAT is provisioned. There is nothing per-agent to -declare — an agent gains GitHub by having a PAT written to its token += true`), inert until a PAT is provisioned. No per-agent declaration is +needed — an agent gains GitHub by having a PAT written to its token file. To turn it off for the whole hive, set the host option: @@ -35,7 +35,7 @@ github.com only. The token **value** never touches nix — it's written to The PAT is operator-supplied. The primary path is the **dashboard credentials tab** (github sub-tab): paste the PAT for an agent and submit -(`POST /api/github-account`). There is also a CLI path for +(`POST /api/github-account`). A CLI path also exists for recovery/scripting: ```sh diff --git a/docs/integrations/knowledge.md b/docs/integrations/knowledge.md index 8a24f72d..ce9ea66a 100644 --- a/docs/integrations/knowledge.md +++ b/docs/integrations/knowledge.md @@ -45,7 +45,7 @@ hive-c0re maintains the local clone at pull`, so agents see the new content on their next turn. A hive that's offline when the event is sent doesn't get it on - reconnect — the periodic pull below is what closes that gap. So one + reconnect — the periodic pull below is what closes that gap. One hive briefly showing older `/knowledge` content than another is expected, and resolves by itself within the fallback interval. diff --git a/docs/networking/gateway.md b/docs/networking/gateway.md index cbf87e18..29076ce9 100644 --- a/docs/networking/gateway.md +++ b/docs/networking/gateway.md @@ -19,7 +19,7 @@ Single nginx in front of every hyperhive web surface. Runs on the **host**, next The authelia vhost is declared only by the host that **runs** authelia, not by every hive that uses it — a client hive knows the swarm's `authelia.url` but must not answer for a name it doesn't serve. Its server name is exactly `swarm.authelia.domain`: authelia validates `authelia_url ⊂ session cookie domain` at startup, so a near-miss is a container that refuses to boot. It carries no `auth_basic` — the login page must not sit behind the login mechanism it replaces — and sets the four `X-Forwarded-{Proto,Host,Uri,For}` headers, since authelia decides by the *original* request rather than the hop it sees. -⚠️ **A `502` from this vhost usually means authelia has no users yet, not that the proxy is misconfigured.** Authelia treats an empty user store as a fatal startup error, so an enabled-but-unbootstrapped swarm crash-loops the container while the vhost in front of it works perfectly. Check `journalctl -M swarm-authelia -u authelia-swarm` before suspecting anything here; the bootstrap step is in [`swarm/sso.md`](../swarm/sso.md). +⚠️ **A `502` from this vhost typically 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. @@ -34,7 +34,7 @@ Operator points client at ``. Sequence: matrix-dart-sdk (FluffyChat etc.) hardcodes `https` for the well-known fetch regardless of input scheme, so the discovery endpoint MUST be https — see "Self-signed TLS" below for the cert generation that backs the default-on path. -Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.:"}` (the federation delegation always carries an explicit port, even the HTTPS default 443 — the https-implies-443 elision only applies to the client base_url above). Gateway only listens on configured `port` (+ `httpsPort` when TLS on); cross-hive federation needs either an SRV record (`_matrix._tcp.chat.` → port 80 / 443) OR `matrix.openFirewall = true` so peers reach tuwunel's federation port directly. Hyperhive is mostly closed/internal, so this rarely bites. +Federation peers fetch `.well-known/matrix/server` → `{"m.server":"chat.:"}` (the federation delegation always carries an explicit port, even the HTTPS default 443 — the https-implies-443 elision only applies to the client base_url above). Gateway only listens on configured `port` (+ `httpsPort` when TLS on); cross-hive federation needs either an SRV record (`_matrix._tcp.chat.` → port 80 / 443) OR `matrix.openFirewall = true` so peers reach tuwunel's federation port directly. Hyperhive is closed/internal in most deployments, so this rarely bites. ## SPA fallback (Accept-header pattern) @@ -252,7 +252,7 @@ or make the key world-readable (`0644`) if your threat model allows it. nginx er ### Fronting with an external TLS terminator -There is no http-only mode (see [TLS modes](#tls-modes) above). Two paths +No http-only mode exists (see [TLS modes](#tls-modes) above). Two paths for an operator who wants their own TLS terminator: - give the gateway the real cert via `tls.certDir` (or `tls.acme`) so it diff --git a/docs/networking/network.md b/docs/networking/network.md index cf9a3e47..e30af440 100644 --- a/docs/networking/network.md +++ b/docs/networking/network.md @@ -12,7 +12,7 @@ whenever hyperhive is enabled. Configured via ## Network map -One picture of the whole hive. There are two planes: **infra +One picture of the whole hive — two planes: **infra containers share the host netns** and bind host ports directly; **compute containers (agents + CI) each get a private netns** behind the bridge. The unix-socket control plane rides the VFS and is @@ -129,14 +129,14 @@ own resolvers: dnsmasq runs on the host and reads the host's upstream — they query the bridge IP and dnsmasq does the right thing per-name. -There is deliberately no fallback `server=`: dnsmasq queries all known +No fallback `server=` exists, by design: dnsmasq queries all known upstreams in parallel, so a hardcoded public resolver would take a share of normal traffic, not just cover the gap. dnsmasq runs on the host and reads the host's `/etc/resolv.conf` directly, so a network change (new router, new lease, laptop moving -networks) reaches it the moment openresolv rewrites the file. There is -nothing to synchronise and no unit watching for it. +networks) reaches it the moment openresolv rewrites the file. Nothing +needs synchronising, and no unit watches for it. `bind-interfaces` + `interface = [ bridgeName "lo" ]` means the listener only accepts queries from the bridge interface (plus lo for @@ -228,7 +228,7 @@ creating or updating containers. Both variables are validated **once at daemon startup**, not per container: they're process-global, so a missing or malformed value is a misconfigured daemon rather than one bad container, and failing at boot gives a single diagnostic instead of one -per agent. There is no non-isolated mode to fall back to. `LOCAL_ADDRESS` is left empty so the +per agent. No non-isolated mode exists to fall back to. `LOCAL_ADDRESS` is left empty so the container's dhcpcd acquires an address from the bridge dnsmasq pool (`networking.useDHCP = true` in `nix/agent-modules/network.nix`). This applies uniformly to all containers — agents and service containers alike. diff --git a/docs/networking/snapshot-store.md b/docs/networking/snapshot-store.md index e1a17681..c30d5bed 100644 --- a/docs/networking/snapshot-store.md +++ b/docs/networking/snapshot-store.md @@ -71,14 +71,14 @@ role this host performs_. A store host sets both --- one to run the receiver, one only if it also runs a hive that pushes. With it set, `hivectl agent subvol snapshot push