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`
|
||||
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ hive-forge repo-labels --json # full label ob
|
|||
|
||||
### Which read verb when
|
||||
|
||||
Several verbs read an issue/PR and look overlapping, but each has a
|
||||
Multiple verbs read an issue/PR and look overlapping, but each has a
|
||||
distinct output shape — pick by what you need:
|
||||
|
||||
| Need | Verb | Shape |
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ Stream a snapshot to the swarm's snapshot store over the WireGuard mesh — the
|
|||
|
||||
Nothing is staged locally: `btrfs send` writes straight into the connection, so a multi-gigabyte agent needs no scratch space on this host. The mesh is the authentication (cryptokey routing binds the sender's address to its key), so there is no credential to pass here.
|
||||
|
||||
There is no destination argument: a swarm has one store, read from `services.hyperhive.swarm.snapshotStore`.
|
||||
No destination argument exists: a swarm has one store, read from `services.hyperhive.swarm.snapshotStore`.
|
||||
|
||||
**Usage:** `hivectl agent subvol snapshot push [OPTIONS] <LABEL>`
|
||||
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ The URL is resolved from the running daemon (`HostRequest::Urls`), which
|
|||
reads the per-surface public URLs from c0re's service env — so custom
|
||||
forge / matrix domains resolve correctly instead of assuming
|
||||
`forge.<domain>`. The URL is **always printed** (the reliable core, since
|
||||
the host is usually headless / driven over SSH), then `xdg-open` is tried
|
||||
the host is typically headless / driven over SSH), then `xdg-open` is tried
|
||||
as a convenience — a missing or failing opener is reported as a note, not
|
||||
an error.
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ Add a user, generating a password for them
|
|||
|
||||
Change an existing user's attributes.
|
||||
|
||||
Every flag is optional and they compose, so one call can set several things at once. Deliberately does **not** touch the password: regenerating a credential is a different intent from editing an attribute, and folded together an attribute edit can invalidate a login by accident.
|
||||
Every flag is optional and they compose, so one call can set multiple things at once. Deliberately does **not** touch the password: regenerating a credential is a different intent from editing an attribute, and folded together an attribute edit can invalidate a login by accident.
|
||||
|
||||
**Usage:** `swarmctl user update [OPTIONS] <USERNAME>`
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ systemd unit. The unit binds `/run/hive/priv.sock` with
|
|||
to the helper as fd 3 (`LISTEN_FDS`). The helper requires this and
|
||||
bails if it isn't socket-activated.
|
||||
|
||||
⚠️ There is intentionally no self-bind fallback: if `hive-priv` bound
|
||||
⚠️ Intentionally, no self-bind fallback exists: if `hive-priv` bound
|
||||
the socket itself, it would create the file owned by root's primary
|
||||
group rather than `hive-core`, and a `hive-core` client couldn't
|
||||
connect the way the socket unit's `SocketGroup` grant intends.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ Each agent gets its own scoped credentials, never shared:
|
|||
collaborator grants; can't act as another agent or as `core`).
|
||||
- **matrix token** → that agent's matrix account only.
|
||||
|
||||
So a compromised/confused agent's reach on the forge or matrix is bounded by
|
||||
A compromised/confused agent's reach on the forge or matrix is bounded by
|
||||
_its own_ account's scope, not the swarm's. This is the main thing standing
|
||||
between "one agent does something dumb" and "the whole hive is affected."
|
||||
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ window from `HIVE_TURN_IDLE_SECS` (`0` disables) and maps the driver's
|
|||
After the outcome handler, the stats sink records a row. `handle_turn`
|
||||
reports the result to `serve_loop` via `TurnControl { auth_failed }` —
|
||||
on auth failure the loop parks in `wait_for_login`; otherwise it loops
|
||||
straight back to the idle wait (step 1). There is no same-turn
|
||||
self-continue mechanism: every multi-step continuation rides an
|
||||
straight back to the idle wait (step 1). No same-turn
|
||||
self-continue mechanism exists: every multi-step continuation rides an
|
||||
external wake instead — a new inbox message, a `remind`, or an
|
||||
in-container todo wake (bash-task completion, forge notification,
|
||||
matrix activity). Ending the turn and letting one of
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ to the bus.
|
|||
**Which `claude` binary.** The bare name `claude`, resolved off the
|
||||
harness unit's PATH. By default that's the `claude-code` in the agent's
|
||||
own nixpkgs (the meta flake's `nixpkgs` input) via
|
||||
`environment.systemPackages`. Since that's usually a release channel and
|
||||
`environment.systemPackages`. Since that's typically a release channel and
|
||||
this package moves fast, the operator can pin one hive-wide with
|
||||
`services.hyperhive.c0re.claudeCodePackage`: its store path is written
|
||||
into each agent's flake, and `claude` on PATH becomes a symlink to it
|
||||
|
|
@ -117,7 +117,7 @@ into its `run`:
|
|||
_still_ overflows, `run` surfaces `Error::PromptTooLong`; `drive_turn`
|
||||
then archives the session (session lifecycle stays hive-side) and the
|
||||
serve loop requeues the message so it redelivers into a fresh session
|
||||
(see [Turn outcomes](README.md#turn-outcomes) — the wake prompt itself is tiny, so
|
||||
(see [Turn outcomes](README.md#turn-outcomes) — the wake prompt itself is small, so
|
||||
the overflow was the accumulated context the archive clears).
|
||||
- **Proactive** — a turn finishes cleanly but the last inference's context
|
||||
size crossed the policy watermark. While the session is still healthy it
|
||||
|
|
@ -185,7 +185,7 @@ still applies.
|
|||
exceeds the assumed prompt-cache TTL (`HIVE_CACHE_TTL_SECS`, default
|
||||
`3600`). Claude's prompt cache goes cold after a while; once it's cold,
|
||||
`--resume`-ing a large session pays the full re-upload cost with no
|
||||
benefit over starting fresh. So `drive_turn` **archives** the current
|
||||
benefit over starting fresh. `drive_turn` **archives** the current
|
||||
session (same mechanism as the operator reset — rename `<uuid>.jsonl` →
|
||||
`.archived`) so the next turn's `--resume <title>` misses and starts
|
||||
fresh. Unlike proactive compaction the session is dropped entirely, not
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ preset (`AGENT_DEFAULT`) includes `messaging`, `meta`, `inbox`, and
|
|||
The graceful-stop turn takes the same post-turn compaction path as any
|
||||
other turn: if the context crossed the watermark the harness runs a
|
||||
notes-checkpoint turn and then `/compact`. Compacting before shutdown
|
||||
keeps a later cold start cheap instead of re-uploading a huge transcript.
|
||||
keeps a later cold start cheap instead of re-uploading a large transcript.
|
||||
- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with
|
||||
broker id `<= up_to` is stamped as acked in a single UPDATE.
|
||||
Recipient-scoped (agents can only ack their own rows). Use when a
|
||||
|
|
@ -93,7 +93,7 @@ at_unix_timestamp?)`.
|
|||
payloads spill to `/agents/<self>/state/reminders/`. Pending count
|
||||
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).
|
||||
|
||||
There is no same-turn self-continue tool — see
|
||||
No same-turn self-continue tool exists — see
|
||||
[Turn outcomes](README.md#turn-outcomes) for why. Multi-step work rides
|
||||
`remind` for a durable self-wake, or an in-container todo wake
|
||||
(bash-task completion, forge notification, matrix activity) for work
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ structurally rather than for one specific trigger. Two columns:
|
|||
- **Pills cluster** (`.agent-header-pills`, right-aligned): status
|
||||
badges (`StatusChips.tsx`) and flyout triggers together in one row —
|
||||
a deliberate choice (not the historical default) so the header reads
|
||||
as one identity zone + one status/actions zone rather than several
|
||||
as one identity zone + one status/actions zone rather than multiple
|
||||
separate clusters:
|
||||
- **Alive badge**: `● alive` (green) / `⊘ rate limited` (red) /
|
||||
`◌ needs login` / `◌ logging in` / `○ offline` / `… connecting`.
|
||||
|
|
@ -83,7 +83,7 @@ structurally rather than for one specific trigger. Two columns:
|
|||
(`GET /api/state`'s `links` field) also feeds
|
||||
`DashboardState.links` for the dashboard card's icon strip —
|
||||
`agent_links()` in hive-agent is the single source of truth for
|
||||
both. There is no separate overflow (`⋯`) menu any more — it used
|
||||
both. No separate overflow (`⋯`) menu exists any more — it used
|
||||
to hold exactly this dashboard link plus a rebuild-container action
|
||||
(mara, hyperhive#3704: "remove rebuild button, move link to
|
||||
dashboards into links menu") — rebuild had no real discoverability
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ dirs; `POST /api/purge-tombstone/{name}`).
|
|||
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: every
|
||||
definition-side artifact (state subvolume, proposed + applied repos,
|
||||
meta registration, topology entry) is written by `Provision` *before*
|
||||
the container exists and survives `lifecycle::destroy`. So an agent
|
||||
the container exists and survives `lifecycle::destroy`. An agent
|
||||
part-way through a spawn is byte-identical on disk to a tombstone, and
|
||||
both are listed. The pane carries a standing warning to that effect, and
|
||||
the row badge says `offline` rather than `destroyed` — the absence of a
|
||||
|
|
@ -474,8 +474,8 @@ Both tables share the same visual shape: `.cap-table-wrap` /
|
|||
and its checkbox cells, where each checkbox carries `data-baseline` (its
|
||||
render-time state) and the row carries `data-agent` for dirty-tracking.
|
||||
|
||||
**Saving — one button for the whole page.** There are no per-row save
|
||||
buttons. A single page-level `.perm-save-bar` with a **save all (N
|
||||
**Saving — one button for the whole page.** No per-row save
|
||||
buttons exist. A single page-level `.perm-save-bar` with a **save all (N
|
||||
agents)** button sits at the bottom of the pane, enabled only when some
|
||||
checkbox diverges from its baseline. Clicking it diffs every checkbox
|
||||
across *both* matrices and POSTs one batch to `POST /api/permissions` as
|
||||
|
|
@ -826,7 +826,7 @@ limited` (red, while the harness is parked after a 429), `needs
|
|||
login`, `needs update` — plus **one `◐ pending-state…` pill per
|
||||
active transient** (replaces buttons during operator-initiated
|
||||
start / stop / restart / rebuild / destroy). An agent can carry
|
||||
**several transients at once** — for example a lease-exempt `prebuild`
|
||||
**multiple transients at once** — for example a lease-exempt `prebuild`
|
||||
running alongside a `stop_for_update` on the same agent — and each
|
||||
renders as its own independent badge rather than being collapsed
|
||||
into one label, matching the existing multi-badge convention this
|
||||
|
|
@ -1337,8 +1337,8 @@ payload):
|
|||
every queue mutation (enqueue, state transition, dedup collapse,
|
||||
terminal-history trim). Unlike the snapshot events below it ships
|
||||
no state at all: the client re-fetches `GET /api/jobq/graph`, which
|
||||
is where it reads the queue from cold too. There is no
|
||||
`/api/state.rebuild_queue` — it went with the typed projection.
|
||||
is where it reads the queue from cold too. No
|
||||
`/api/state.rebuild_queue` exists — it went with the typed projection.
|
||||
- `schedules_changed` (seq, schedules: `Vec<WireSchedule>`) —
|
||||
full snapshot of all scheduled prompts. Emitted after every
|
||||
operator mutation via the `/api/schedules` surface (new /
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ repeated here.
|
|||
- **Frosted / tinted glass** where it fits — translucency over flat
|
||||
panels, not a hard rule for every surface.
|
||||
- **Playful use of the multiple accent colours**, without tipping into
|
||||
visual noise — the base16 palette has several accent slots
|
||||
visual noise — the base16 palette has multiple accent slots
|
||||
(`--purple`, `--cyan`, `--pink`, …); use more than one where it adds
|
||||
distinction, not decoration for its own sake.
|
||||
- **Whimsy** — small, delightful touches are welcome (the per-hive
|
||||
|
|
@ -110,7 +110,7 @@ UI later. Concretely: full untruncated `detail`, a copy button, no
|
|||
"something went wrong, try reloading."
|
||||
|
||||
This follows from **power-user-first**, one of the standing principles:
|
||||
swarm-ui instances are mostly self-hosted and operators are techies, so
|
||||
swarm-ui instances are typically self-hosted and operators are techies, so
|
||||
errors should give them what they need to actually diagnose a problem
|
||||
rather than a friendly wall. Power-user-first doesn't mean
|
||||
newcomer-hostile — the UI should still be self-explanatory, warn or ask
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ pub struct Args {
|
|||
/// Collaborator's forge login to add.
|
||||
user: String,
|
||||
/// Permission level to grant (default: write — a freshly added
|
||||
/// collaborator usually needs to push).
|
||||
/// collaborator typically needs to push).
|
||||
#[arg(long, value_enum, default_value_t = Permission::Write)]
|
||||
permission: Permission,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -668,7 +668,7 @@ pub enum SnapshotCmd {
|
|||
/// binds the sender's address to its key), so there is no credential
|
||||
/// to pass here.
|
||||
///
|
||||
/// There is no destination argument: a swarm has one store, read
|
||||
/// No destination argument exists: a swarm has one store, read
|
||||
/// from `services.hyperhive.swarm.snapshotStore`.
|
||||
Push {
|
||||
/// Snapshot label passed to `subvol snapshot create --label`.
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ enum UserVerb {
|
|||
/// Change an existing user's attributes.
|
||||
///
|
||||
/// Every flag is optional and they compose, so one call can set
|
||||
/// several things at once. Deliberately does **not** touch the
|
||||
/// multiple things at once. Deliberately does **not** touch the
|
||||
/// password: regenerating a credential is a different intent from
|
||||
/// editing an attribute, and folded together an attribute edit can
|
||||
/// invalidate a login by accident.
|
||||
|
|
|
|||
Loading…
Reference in a new issue