docs: fix write-good.So/ThereIs/Weasel lint findings
Fixes the "obvious ones first" slice of #4042 (mara: do the obvious ones first) -- 81 hits across write-good.So, write-good.ThereIs, and write-good.Weasel, all in docs/. Each is a genuine sentence rewrite (lead with the real subject instead of "There is/are", drop a sentence-initial "So ", replace a vague intensifier), not a blind regex substitution -- read every hit in its real file context before touching it. 3 of the 81 hits were in CI-generated CLI docs (docs/tools/{hivectl, swarmctl,forge}-cli.md) -- fixed at the clap #[arg(...)]/doc-comment source in hivectl/src/cli.rs, swarmctl/src/main.rs, and hive-forge/src/verbs/repo_add_collaborator.rs, then regenerated via each crate's `markdown-docs` subcommand so CI's freshness check stays green. Verified: fresh vale re-run shows 0 remaining So/ThereIs/Weasel hits and no new hits introduced (983->982, exactly the one incidental fix this pass also picked up at docs/scheduler/observability.md:48). cargo fmt --check and clippy clean on the three touched crates. Remaining write-good backlog (Passive: 726, TooWordy: 207) is judgment-heavy and left for a follow-up slice of #4042, not bulk- rewritten here.
This commit is contained in:
parent
6d4e0e5fa1
commit
e82a735745
35 changed files with 99 additions and 99 deletions
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ flow.
|
|||
|
||||
Config changes flow through a **forge pull request** on the agent's
|
||||
`agent-configs/<name>` 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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#<n>` 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
|
||||
|
|
|
|||
|
|
@ -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 <author>: <excerpt>` 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 <kind>` 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 <kind>`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 `<hive>`. 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.<swarm>:<httpsPort>"}` (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.<swarm>` → 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.<swarm>:<httpsPort>"}` (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.<swarm>` → 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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 <name> subvol snapshot push <label>
|
||||
[--parent <label>]` streams a snapshot straight into the store. There is
|
||||
no destination argument, because a swarm has exactly one store (see
|
||||
[--parent <label>]` streams a snapshot straight into the store. No
|
||||
destination argument exists, because a swarm has exactly one store (see
|
||||
[One subvolume per agent, not per hive](#one-subvolume-per-agent-not-per-hive)),
|
||||
and no credential argument, because the mesh is the authentication.
|
||||
|
||||
## The mesh is the authentication
|
||||
|
||||
There are no certificates here, and no key material of its own. That's
|
||||
No certificates live here, and no key material of its own. That's
|
||||
deliberate rather than an omission.
|
||||
|
||||
WireGuard's cryptokey routing already binds a peer's source address to
|
||||
|
|
@ -106,7 +106,7 @@ the case the store exists to serve.
|
|||
## What the sender can and can't choose
|
||||
|
||||
A `btrfs send` stream carries no notion of _which agent_ it belongs to,
|
||||
and the subvolume name inside it's chosen by the sender. So the
|
||||
and the subvolume name inside it's chosen by the sender. The
|
||||
protocol is one `agent <name>` header line, then the raw stream.
|
||||
|
||||
The rule that matters:
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ peer's name when the caller knows it's hive-local.
|
|||
|
||||
## Identity = socket
|
||||
|
||||
There are no auth tokens on the per-agent unix sockets. The socket
|
||||
No auth tokens exist on the per-agent unix sockets. The socket
|
||||
*path* identifies the principal; perms come from "who has the
|
||||
bind-mount." A sub-agent only sees its own `/run/hive/mcp.sock`;
|
||||
hive-c0re owns the host admin socket.
|
||||
|
|
@ -490,7 +490,7 @@ check derivations they don't:
|
|||
|
||||
- **`hivectl-docs`** regenerates `docs/tools/hivectl-cli.md` from
|
||||
hivectl's clap tree and **fails if the committed copy is stale**.
|
||||
So **after any change to a hivectl verb or flag, regenerate it**:
|
||||
**After any change to a hivectl verb or flag, regenerate it**:
|
||||
```sh
|
||||
nix develop -c cargo run --bin hivectl -- markdown-docs > docs/tools/hivectl-cli.md
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Gotchas
|
||||
|
||||
NixOS + nspawn quirks and lessons we hit the hard way. If something
|
||||
here looks unmotivated in the code, there's usually a story underneath.
|
||||
here looks unmotivated in the code, there's probably a story underneath.
|
||||
Grouped by area — jump to the section that matches what you're
|
||||
touching.
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ path = [ "/run/wrappers" "/run/current-system/sw" ];
|
|||
```
|
||||
|
||||
The bug is silent: `nix eval` succeeds, the unit starts, but PATH
|
||||
contains a non-existent directory. The first symptom is usually
|
||||
contains a non-existent directory. The first symptom is typically
|
||||
`sudo: must be owned by uid 0 and have the setuid bit set` because
|
||||
the setuid sudo wrapper lives at `/run/wrappers/bin/sudo` and
|
||||
the path entry resolves to `/run/wrappers/bin/bin` instead.
|
||||
|
|
@ -144,7 +144,7 @@ store-path _string_ coerces through `lib.toDerivation`, i.e.
|
|||
`builtins.storePath`. `environment.systemPackages` and
|
||||
`systemd.services.<name>.path` both do it (the latter takes plain
|
||||
strings like `/run/wrappers` happily, but anything under
|
||||
`builtins.storeDir` is treated as a package). So a path handed to the
|
||||
`builtins.storeDir` is treated as a package). A path handed to the
|
||||
container as text has to be wrapped in a real derivation — a symlink
|
||||
farm built from the interpolated string — before it can go anywhere a
|
||||
package is expected.
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Cheap — no build slot:
|
|||
| `KnowledgePull` | one-shot boot-time `/knowledge` pull (`knowledge::pull`), reconciling commits that landed while `hive-c0re` was down. Same rationale as `MatrixSweep`: the periodic hourly re-pull stays a background loop |
|
||||
| `WantedPull` | one-shot boot-time pull of the agent set the swarm controller declares for this hive (`wanted::pull`), converging the agents it names. No background loop behind this one — boot is the whole cadence; the deploy event (`swarm_status`) is the fast path, this repairs a missed one. Agentless |
|
||||
|
||||
There is deliberately **no `GitCommit` node**: `meta.rs` fuses each mutation
|
||||
Deliberately, **no `GitCommit` node** exists: `meta.rs` fuses each mutation
|
||||
with its commit under its internal `META_LOCK` mutex, so a standalone commit
|
||||
node would open a dirty-working-tree window between nodes.
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ resources are free. Resources:
|
|||
`Reconcile`, `WriteDropin`, `Create`) or at a **brace** (`AgentWindow`,
|
||||
`DeployWindow`) on behalf of a whole coordinated subtree; held by the owning
|
||||
DAG until it's terminal, so two DAGs never interleave container ops on the
|
||||
same agent. A DAG touching several agents holds one lease per agent.
|
||||
same agent. A DAG touching multiple agents holds one lease per agent.
|
||||
(`SetWanted` is a store write, not a container op, but takes the lease anyway
|
||||
so a power-op DAG's intent write + reconcile is atomic — two racing ops can't
|
||||
clobber intent before either reconciles.) **Lease-exempt**: `MetaSync`,
|
||||
|
|
@ -333,7 +333,7 @@ string) and free-form `data` for what only some kinds have (`agent`,
|
|||
`approval_id`, `inputs`, `build_log_id`). Group roots ride as ordinary
|
||||
nodes, so a group's state is just the root's own `state`.
|
||||
|
||||
There is **no group-level `agent`** — agent is per-node, so one group can
|
||||
No group-level `agent` exists — agent is per-node, so one group can
|
||||
span agents; consumers derive a group's agents from its nodes. Build logs
|
||||
are likewise **per-node**: the dashboard renders the node tree and keys the
|
||||
live-log panel off the running node.
|
||||
|
|
@ -497,7 +497,7 @@ limits, build parallelism, and first-spawn latency.
|
|||
`buildSlots` (default `1`) sets how many nix-heavy job-queue nodes
|
||||
(prebuilds, profile swaps, first-spawn creates, meta lock bumps) run
|
||||
concurrently. The default serializes all heavy nix work like the pre-DAG
|
||||
rebuild queue did; raise it on hosts with the cores/RAM to build several
|
||||
rebuild queue did; raise it on hosts with the cores/RAM to build multiple
|
||||
agent toplevels at once. Per-agent correctness is independent of the count —
|
||||
each agent's container-affecting ops serialize on its lifecycle lease
|
||||
regardless.
|
||||
|
|
@ -513,7 +513,7 @@ requiring a host rebuild.
|
|||
The same drop-in carries `CPUWeight=` / `IOWeight=` from
|
||||
`agentCpuWeight` / `agentIoWeight`. Those are a different kind of
|
||||
setting: the quota and the memory max are **hard caps** that throttle
|
||||
an agent even on a completely idle host, while the weights are cgroup
|
||||
an agent even on an idle host, while the weights are cgroup
|
||||
v2 **relative shares** that only decide who yields _under contention_.
|
||||
A low-weight container still gets the whole machine when nothing else
|
||||
wants it.
|
||||
|
|
@ -560,7 +560,7 @@ trade-off:
|
|||
binary are added to the host system closure (low single-digit GB
|
||||
additional).
|
||||
- **Off** (default): the host closure stays lean; the first spawn
|
||||
does all the eval + fetch work at runtime (can take several
|
||||
does all the eval + fetch work at runtime (can take a few
|
||||
minutes on a fresh store).
|
||||
|
||||
**Note**: toplevels are pinned to `x86_64-linux`. Enabling on an
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ token usage, cost, tool call counts — to any OTLP-compatible collector via
|
|||
Claude Code's built-in OpenTelemetry integration.
|
||||
|
||||
This is a **hive-wide** setting: one switch in the host NixOS config enables it
|
||||
for every agent container simultaneously. There is no per-agent opt-in or opt-out.
|
||||
for every agent container simultaneously. No per-agent opt-in or opt-out exists.
|
||||
|
||||
## Enabling export
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ leaves the swarm — optional, because the swarm's own metrics store
|
|||
telemetry goes to both. See
|
||||
[`swarm/services.md`](../swarm/services.md#metrics-victoriametrics--grafana).
|
||||
|
||||
**There is exactly one way telemetry leaves a hive: through the collector that
|
||||
**Telemetry leaves a hive exactly one way: 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
|
||||
|
|
@ -45,7 +45,7 @@ no longer survives independently of anything host-side.
|
|||
|
||||
**It has no application-level auth.** The receiver takes any OTLP that reaches
|
||||
it; what bounds who can reach it's the firewall — `exposeHostPorts` opens the
|
||||
port on the bridge interface only. So "unauthenticated to a bridge address"
|
||||
port on the bridge interface only — "unauthenticated to a bridge address"
|
||||
means _reachable from an agent container_, not _presents a credential_.
|
||||
|
||||
The consequence, stated because it's a choice rather than an oversight: **any
|
||||
|
|
@ -76,7 +76,7 @@ is getting a credential:
|
|||
`hive=` from the connection it authenticated, so _that_ label becomes
|
||||
unforgeable.
|
||||
|
||||
So a verified `hive` is reachable and a verified `agent` isn't — and that falls
|
||||
A verified `hive` is reachable and a verified `agent` isn't — and that falls
|
||||
out of the topology rather than being a gap someone forgot to close. The swarm
|
||||
runs one collector, and the mechanism gives it no finer grain: a bearer-token
|
||||
check never reveals _which_ token matched, and a receiver reads request metadata
|
||||
|
|
@ -150,7 +150,7 @@ somehow — copy it across and name it:
|
|||
services.hyperhive.otel.clientSecretFile = "/run/secrets/hive-telemetry.secret";
|
||||
```
|
||||
|
||||
**There is no unauthenticated mode.** A hive always presents an identity, so a
|
||||
**No unauthenticated mode exists.** A hive always presents an identity, so a
|
||||
missing credential is a build error rather than a quieter fallback — the
|
||||
collector has no anonymous route to accept samples on, and every path it serves
|
||||
belongs to exactly one hive.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ both once.**
|
|||
|
||||
`domain` is required too, but you no longer _write_ it: it's read from
|
||||
this hive's own entry in the directory, whose `domain` defaults to
|
||||
`<name>.<swarm.domain>`. So a conventional swarm states no addresses at
|
||||
`<name>.<swarm.domain>`. A conventional swarm states no addresses at
|
||||
all, and a hive addressed by something else states it in the one place
|
||||
the other hives read — `swarm.hives.edge.domain = "edge.elsewhere.example";`.
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ contain you derives every hive as a peer and you peer with yourself.
|
|||
conventionally `<name>.<swarm.domain>`, but a wrong domain that
|
||||
evaluates cleanly points at a real machine that isn't the one you meant.
|
||||
|
||||
> **There is no per-hive CA field, and no per-hive cert pinning.** Trust
|
||||
> **No per-hive CA field exists, and no per-hive cert pinning.** Trust
|
||||
> inside a swarm comes from the swarm root ([`ca.md`](ca.md)): every
|
||||
> hive chains to it, so one anchor replaces per-hive pinning entirely.
|
||||
> What that genuinely drops is trusting a hive whose root this swarm
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ swarm-services leaf can, and this host signs that one only under
|
|||
`autoConfigure`. A hive with neither serves the hive leaf on those names
|
||||
and every client sees a name mismatch.
|
||||
|
||||
So `hive-tls` emits an eval-time **warning** naming the uncovered names.
|
||||
`hive-tls` emits an eval-time **warning** naming the uncovered names.
|
||||
It's deliberately not an assertion: this module can see what _it_ is
|
||||
able to issue, but not an operator-installed sub-CA in
|
||||
`swarm.ca.stateDir`, an external ACME setup, or a certificate delivered
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ The rule is about what must stay secret, not about credentials generally.
|
|||
`deploy.nats.calloutUserPublicKey`, is published to every client that connects,
|
||||
so the store is a perfectly good place for it.
|
||||
|
||||
**The generator and the reader are usually in different containers.** They share
|
||||
**The generator and the reader typically live in different containers.** They share
|
||||
the host's network namespace, which makes them feel co-located, but their
|
||||
filesystem roots are separate. That's why delivery is a **host-side copy rather
|
||||
than a bind mount**: `nixos-container` refuses to start when a bind source is
|
||||
|
|
@ -30,7 +30,7 @@ container that starts after it.
|
|||
Every row below is read against one of these. This is a different cut
|
||||
from the [deployment shapes](services.md#deployment-shapes) --- those
|
||||
say _where services run_, these say _who is responsible for a secret
|
||||
file being there_ --- so the two lists don't line up one-to-one, and
|
||||
file being there_ --- the two lists don't line up one-to-one, and
|
||||
neither is a renaming of the other.
|
||||
|
||||
| topology | what it means | who places secrets |
|
||||
|
|
@ -63,7 +63,7 @@ because nothing outside that container ever reads them. **That's the test worth
|
|||
applying to any secret added here** — and the client secret's plaintext half is
|
||||
the one row that fails it, which is the entire reason a delivery step exists.
|
||||
|
||||
There are two telemetry collectors and they land on opposite sides of that test.
|
||||
Two telemetry collectors exist, and they land on opposite sides of that test.
|
||||
|
||||
The **hive's** collector needs no delivery step. It authenticates to the swarm's
|
||||
collector as its own hive, and it's a host unit rather than a container, so on
|
||||
|
|
@ -136,7 +136,7 @@ when the system builds. The server names the offending file and refuses to run.
|
|||
|
||||
Both delivery units wait for authelia's first boot to mint the secret — a
|
||||
bounded wait, 120s — and then **fail loudly** rather than skipping. A silent skip
|
||||
produces a service whose login button always fails, which is a symptom several
|
||||
produces a service whose login button always fails, which is a symptom many
|
||||
layers from its cause.
|
||||
|
||||
The store's **first reader** is the matrix registration token, and it's worth
|
||||
|
|
@ -170,7 +170,7 @@ everything else a hive needs does.
|
|||
|
||||
A hive CA carries `nameConstraints=permitted;DNS:<hive domain>`, and **a swarm
|
||||
service name is a sibling of the hive domain rather than a child** — `forge.<swarm>`
|
||||
next to `<hive>.<swarm>`. So a hive CA can't issue a certificate for a swarm
|
||||
next to `<hive>.<swarm>`. A hive CA can't issue a certificate for a swarm
|
||||
service. Not by policy: by construction, and openssl enforces it.
|
||||
|
||||
Whatever holds the swarm root is therefore what makes swarm-service certificates
|
||||
|
|
|
|||
|
|
@ -143,11 +143,11 @@ Drilldown_ app is deliberately not installed: it only supports Loki, and
|
|||
no setting here changes that, so Explore is the log browser for this
|
||||
swarm.
|
||||
|
||||
| Option | When you'd touch it |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `deploy.victorialogs.retentionPeriod` | Default `30d`, far shorter than the metrics store's — logs are bulkier per unit of value and are usually read within days of being written. Raise it if you need to answer questions about last quarter. |
|
||||
| `swarm.victorialogs.domain` | Only to rename it. |
|
||||
| `swarm.victorialogs.port` | Only if something else on the services host already claims `9428`. |
|
||||
| Option | When you'd touch it |
|
||||
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `deploy.victorialogs.retentionPeriod` | Default `30d`, far shorter than the metrics store's — logs are bulkier per unit of value and are typically read within days of being written. Raise it if you need to answer questions about last quarter. |
|
||||
| `swarm.victorialogs.domain` | Only to rename it. |
|
||||
| `swarm.victorialogs.port` | Only if something else on the services host already claims `9428`. |
|
||||
|
||||
Like the metrics store, it binds loopback and takes no credential of its
|
||||
own: the gateway vhost is the only way in, and the collector is the only
|
||||
|
|
@ -169,7 +169,7 @@ port is a coin toss at runtime rather than an error at build time.
|
|||
Every hive's own collector reaches this one by its gateway name,
|
||||
`swarm.otel.domain` (default `otel.<swarm domain>`) — the same
|
||||
by-domain-through-the-gateway shape every other swarm service uses, not a
|
||||
loopback URL an operator has to redirect. There is nothing to set on a hive
|
||||
loopback URL an operator has to redirect. Nothing needs setting on a hive
|
||||
that doesn't run the swarm's services; the name resolves through the
|
||||
gateway either way.
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ enabled but not bootstrapped shows a **crash-looping unit** and `502 Bad
|
|||
Gateway` from the vhost — not a login page with nobody able to use it.
|
||||
The gateway is working in that state; the upstream isn't up.
|
||||
|
||||
⚠️ So the step below is **required to finish the install**, not an
|
||||
⚠️ The step below is **required to finish the install**, not an
|
||||
optional first-login convenience. Run it before concluding anything is
|
||||
wrong with the proxy: a 502 here means "no users yet" far more often
|
||||
than it means a routing fault.
|
||||
|
|
@ -67,7 +67,7 @@ operator typing one command once.
|
|||
`user add` only ever adds: on a name that already exists it refuses,
|
||||
rather than resurfacing as a second account or a silent overwrite.
|
||||
Editing an existing subject is `user update`, and the flags compose, so
|
||||
one call can change several things:
|
||||
one call can change multiple things:
|
||||
|
||||
```console
|
||||
# swarmctl user update mara --add-group admins --email mara@example.com
|
||||
|
|
@ -99,7 +99,7 @@ Every secret in the swarm, with its generator and its path, is tabulated
|
|||
in one place: [`secrets.md`](secrets.md), including authelia's own keys
|
||||
(session, JWT, storage-encryption, OIDC HMAC, OIDC issuer) and the two
|
||||
halves of each client secret. That page's two rules — a secret is always
|
||||
a path, never a value, and the generator and the reader are usually in
|
||||
a path, never a value, and the generator and the reader typically live in
|
||||
different containers — are why the client secret's plaintext half needs
|
||||
the delivery step below and the rest of authelia's keys don't.
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ nixos-container refuses to start a container whose bind source is
|
|||
missing, and this secret doesn't exist until authelia's first boot has
|
||||
run. Binding it would make the service wait on a file that waits on a
|
||||
container that starts after it — on a fresh hive, a permanent stall
|
||||
presenting as "the forge is broken," several layers from its cause.
|
||||
presenting as "the forge is broken," many layers from its cause.
|
||||
|
||||
### 2. Swarm-managed services
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ services.hyperhive.swarm = {
|
|||
|
||||
**Both are asserted at eval.** A hive that boots with SSO
|
||||
half-configured shows a login button that always fails — a symptom
|
||||
several layers from its cause, and far worse to diagnose than an
|
||||
many layers from its cause, and far worse to diagnose than an
|
||||
evaluation error.
|
||||
|
||||
## Where each relying party differs
|
||||
|
|
@ -218,7 +218,7 @@ HTML instead of the failure that actually happened:
|
|||
- `/api/oidc/token`, `/.well-known/openid-configuration` — a client
|
||||
parsing an error page as its JSON document
|
||||
|
||||
So authelia's `/api/` and `/.well-known/` prefixes are routed **without**
|
||||
Authelia's `/api/` and `/.well-known/` prefixes are routed **without**
|
||||
error interception. The split is by _audience_, not by an enumerated path
|
||||
list: a human gets the page, every machine caller gets the status.
|
||||
Enumerating endpoints individually would leave the next one added
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ Exposed as `mcp__bash__kill`.
|
|||
|
||||
## Namespace note
|
||||
|
||||
`run` and `status` live in the `bash` MCP server, not `hyperhive`. So
|
||||
the tool names in claude are `mcp__bash__run` and `mcp__bash__status`.
|
||||
`run` and `status` live in the `bash` MCP server, not `hyperhive`. The
|
||||
tool names in claude are `mcp__bash__run` and `mcp__bash__status`.
|
||||
The `Bash` built-in tool is blocked — all shell execution goes through
|
||||
this structured path so tasks get task-id tracking and structured output.
|
||||
|
||||
|
|
|
|||
|
|
@ -1003,7 +1003,7 @@ Add a collaborator to the active repo (`-r`/`HIVE_FORGE_REPO`) with a permission
|
|||
|
||||
###### **Options:**
|
||||
|
||||
* `--permission <PERMISSION>` — Permission level to grant (default: write — a freshly added collaborator usually needs to push)
|
||||
* `--permission <PERMISSION>` — Permission level to grant (default: write — a freshly added collaborator typically needs to push)
|
||||
|
||||
Default value: `write`
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue