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`
|
One harness serve binary (`hive-agent`, with its `hive-agent-mcp`
|
||||||
sibling), one shared `nix/agent-modules/` tree, one service unit
|
sibling), one shared `nix/agent-modules/` tree, one service unit
|
||||||
(`systemd.services.hive-agent`) for all agents. There is no separate
|
(`systemd.services.hive-agent`) for all agents. No separate manager
|
||||||
manager service name or role distinction in the harness — privilege
|
service name or role distinction exists in the harness — privilege
|
||||||
differences live server-side in the broker socket (which tool groups
|
differences live server-side in the broker socket (which tool groups
|
||||||
and manager-surface calls each agent receives).
|
and manager-surface calls each agent receives).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ flow.
|
||||||
|
|
||||||
Config changes flow through a **forge pull request** on the agent's
|
Config changes flow through a **forge pull request** on the agent's
|
||||||
`agent-configs/<name>` repo — the same surface agents use for code PRs.
|
`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.
|
request.
|
||||||
|
|
||||||
1. The submitting agent (the child's parent, holding the `approvals`
|
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`
|
`destroy`, `rebuild`, and the startup migration. Renders `flake.nix`
|
||||||
from the agent list; if it differs from disk, runs
|
from the agent list; if it differs from disk, runs
|
||||||
`nix flake lock` + commits as `regenerate meta flake` (or
|
`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:
|
The root agent has `/meta` RO-bound inside its container:
|
||||||
`git -C /meta log --oneline` is the swarm-wide deploy log,
|
`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
|
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
|
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
|
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,
|
the operator review and approve it. By design, no second, mount-shaped
|
||||||
mount-shaped path that reaches the same file without the review.
|
path reaches the same file without the review.
|
||||||
|
|
||||||
Agents holding the `can_manage_top_level_agents` topology role (see
|
Agents holding the `can_manage_top_level_agents` topology role (see
|
||||||
`hive-c0re/src/agent_config/topology.rs`) get additional host-side
|
`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
|
The system prompt (`hive-agent/prompts/system.md`, rendered by
|
||||||
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
`hive-agent/src/prompt.rs`) is the **same for every agent**; what
|
||||||
varies is which MCP tools are surfaced (gated by tool groups and
|
varies is which MCP tools are surfaced (gated by tool groups and
|
||||||
capabilities in `agent.nix`). There is no `role:manager` block that
|
capabilities in `agent.nix`). No `role:manager` block renders only
|
||||||
renders only for the root agent. The root agent's approval-gating
|
for the root agent. The root agent's approval-gating
|
||||||
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
behaviour comes from its CLAUDE.md / agent-specific instructions, not
|
||||||
the system prompt template.
|
the system prompt template.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ power-intent registry:
|
||||||
last_fired_at_unix / last_result`. `ON DELETE CASCADE` from
|
last_fired_at_unix / last_result`. `ON DELETE CASCADE` from
|
||||||
`scheduled_prompts(id)` — requires `PRAGMA foreign_keys = ON`
|
`scheduled_prompts(id)` — requires `PRAGMA foreign_keys = ON`
|
||||||
per connection (set at open).
|
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`.
|
offline) / updated_at`, owned by `hive-c0re/src/stores/power.rs`.
|
||||||
This is the durable power *intent* the job queue reconciles the
|
This is the durable power *intent* the job queue reconciles the
|
||||||
observed container state against; intent survives hive-c0re
|
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
|
(`CREATE TABLE IF NOT EXISTS`), so it's absent until a bash
|
||||||
task runs.
|
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
|
(~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
|
and the hive-wide stats view, so pruning it would only lose trend
|
||||||
history for no space gain.
|
history for no space gain.
|
||||||
|
|
@ -409,7 +409,7 @@ Contents:
|
||||||
|
|
||||||
The root agent has the meta dir RO-mounted at `/meta/`.
|
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
|
one-shot container repoint it guarded has been removed, since
|
||||||
containers are rendered onto `meta#<n>` at creation. A stale
|
containers are rendered onto `meta#<n>` at creation. A stale
|
||||||
marker file left over from an older hive is inert and can be
|
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:
|
container lifetime:
|
||||||
|
|
||||||
1. **`${homeDir}` exists with the right ownership** — covers the
|
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
|
chance to chown. Also re-applies on every rebuild in case the
|
||||||
meta-flake's per-agent name evolves (rare).
|
meta-flake's per-agent name evolves (rare).
|
||||||
2. **Migrate any leftover `/root/.claude` content into
|
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
|
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
|
sqlite row with its own ack lifecycle — so the forge unread flag no
|
||||||
longer needs to track whether the agent has _processed_ a
|
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
|
by construction**: at rest it holds only threads that failed to
|
||||||
deliver plus whatever arrived since the last 30s poll.
|
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` /
|
An agent that wants the full thread still runs `hive-forge comments` /
|
||||||
`view`.
|
`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
|
id → last-delivered `updated_at`) guards the narrow window where a
|
||||||
mark-read call _transiently fails_ and the thread reappears unread in
|
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
|
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
|
- Initial client construction fails (the typed `forgejo-api` client
|
||||||
for the API calls, or the plain reqwest client kept for the
|
for the API calls, or the plain reqwest client kept for the
|
||||||
best-effort enrichment fetches of server-provided subject URLs;
|
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
|
Disabled = the spawned task returns immediately. All other failure
|
||||||
modes (HTTP errors, parse errors, mark-read failures) are
|
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
|
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
|
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.
|
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
|
event time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` of the
|
||||||
subject's `closed_at` (set for both `merged` and `closed`) — the
|
subject's `closed_at` (set for both `merged` and `closed`) — the
|
||||||
state-change path wins even with a comment url present
|
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
|
(`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
|
pre-merge last comment the subject keeps — append it as a
|
||||||
`comment by <author>: <excerpt>` block before the meta suffix
|
`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
|
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
|
`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`/
|
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
|
so it misses the comment path and lands on the state-change path with
|
||||||
`state == "open"` — exactly like a freshly opened PR. Labeling that
|
`state == "open"` — exactly like a freshly opened PR. Labeling that
|
||||||
`new PR` is misleading: agents dismiss it as a duplicate of the
|
`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
|
state only earns the `new <kind>` label when the notification's event
|
||||||
time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` (120s) of the
|
time (`updated_at`) is within `NEW_ITEM_TOLERANCE_SECS` (120s) of the
|
||||||
subject's `created_at`. Anything later is labeled `activity on <kind>`
|
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
|
## Enabling
|
||||||
|
|
||||||
The integration is **on by default** for every agent (`hyperhive.github.enable
|
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
|
= true`), inert until a PAT is provisioned. No per-agent declaration is
|
||||||
declare — an agent gains GitHub by having a PAT written to its token
|
needed — an agent gains GitHub by having a PAT written to its token
|
||||||
file.
|
file.
|
||||||
|
|
||||||
To turn it off for the whole hive, set the host option:
|
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
|
The PAT is operator-supplied. The primary path is the **dashboard
|
||||||
credentials tab** (github sub-tab): paste the PAT for an agent and submit
|
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:
|
recovery/scripting:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ hive-c0re maintains the local clone at
|
||||||
pull`, so agents see the new content on their next turn.
|
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
|
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
|
hive briefly showing older `/knowledge` content than another is
|
||||||
expected, and resolves by itself within the fallback interval.
|
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.
|
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
|
Per-agent UIs stay sub-path, forge and matrix get sub-domains — see
|
||||||
[Sub-domain shape (rationale)](#sub-domain-shape-rationale) below for why.
|
[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.
|
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)
|
## 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
|
### 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:
|
for an operator who wants their own TLS terminator:
|
||||||
|
|
||||||
- give the gateway the real cert via `tls.certDir` (or `tls.acme`) so it
|
- 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
|
## 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;
|
containers share the host netns** and bind host ports directly;
|
||||||
**compute containers (agents + CI) each get a private netns** behind
|
**compute containers (agents + CI) each get a private netns** behind
|
||||||
the bridge. The unix-socket control plane rides the VFS and is
|
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
|
upstream — they query the bridge IP and dnsmasq does the right thing
|
||||||
per-name.
|
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
|
upstreams in parallel, so a hardcoded public resolver would take a share
|
||||||
of normal traffic, not just cover the gap.
|
of normal traffic, not just cover the gap.
|
||||||
|
|
||||||
dnsmasq runs on the host and reads the host's `/etc/resolv.conf`
|
dnsmasq runs on the host and reads the host's `/etc/resolv.conf`
|
||||||
directly, so a network change (new router, new lease, laptop moving
|
directly, so a network change (new router, new lease, laptop moving
|
||||||
networks) reaches it the moment openresolv rewrites the file. There is
|
networks) reaches it the moment openresolv rewrites the file. Nothing
|
||||||
nothing to synchronise and no unit watching for it.
|
needs synchronising, and no unit watches for it.
|
||||||
|
|
||||||
`bind-interfaces` + `interface = [ bridgeName "lo" ]` means the
|
`bind-interfaces` + `interface = [ bridgeName "lo" ]` means the
|
||||||
listener only accepts queries from the bridge interface (plus lo for
|
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
|
daemon startup**, not per container: they're process-global, so a
|
||||||
missing or malformed value is a misconfigured daemon rather than one bad
|
missing or malformed value is a misconfigured daemon rather than one bad
|
||||||
container, and failing at boot gives a single diagnostic instead of one
|
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
|
container's dhcpcd acquires an address from the bridge dnsmasq pool
|
||||||
(`networking.useDHCP = true` in `nix/agent-modules/network.nix`). This applies uniformly
|
(`networking.useDHCP = true` in `nix/agent-modules/network.nix`). This applies uniformly
|
||||||
to all containers — agents and service containers alike.
|
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.
|
receiver, one only if it also runs a hive that pushes.
|
||||||
|
|
||||||
With it set, `hivectl agent <name> subvol snapshot push <label>
|
With it set, `hivectl agent <name> subvol snapshot push <label>
|
||||||
[--parent <label>]` streams a snapshot straight into the store. There is
|
[--parent <label>]` streams a snapshot straight into the store. No
|
||||||
no destination argument, because a swarm has exactly one store (see
|
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)),
|
[One subvolume per agent, not per hive](#one-subvolume-per-agent-not-per-hive)),
|
||||||
and no credential argument, because the mesh is the authentication.
|
and no credential argument, because the mesh is the authentication.
|
||||||
|
|
||||||
## 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.
|
deliberate rather than an omission.
|
||||||
|
|
||||||
WireGuard's cryptokey routing already binds a peer's source address to
|
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
|
## What the sender can and can't choose
|
||||||
|
|
||||||
A `btrfs send` stream carries no notion of _which agent_ it belongs to,
|
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.
|
protocol is one `agent <name>` header line, then the raw stream.
|
||||||
|
|
||||||
The rule that matters:
|
The rule that matters:
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ peer's name when the caller knows it's hive-local.
|
||||||
|
|
||||||
## Identity = socket
|
## 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
|
*path* identifies the principal; perms come from "who has the
|
||||||
bind-mount." A sub-agent only sees its own `/run/hive/mcp.sock`;
|
bind-mount." A sub-agent only sees its own `/run/hive/mcp.sock`;
|
||||||
hive-c0re owns the host admin socket.
|
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-docs`** regenerates `docs/tools/hivectl-cli.md` from
|
||||||
hivectl's clap tree and **fails if the committed copy is stale**.
|
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
|
```sh
|
||||||
nix develop -c cargo run --bin hivectl -- markdown-docs > docs/tools/hivectl-cli.md
|
nix develop -c cargo run --bin hivectl -- markdown-docs > docs/tools/hivectl-cli.md
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Gotchas
|
# Gotchas
|
||||||
|
|
||||||
NixOS + nspawn quirks and lessons we hit the hard way. If something
|
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
|
Grouped by area — jump to the section that matches what you're
|
||||||
touching.
|
touching.
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ path = [ "/run/wrappers" "/run/current-system/sw" ];
|
||||||
```
|
```
|
||||||
|
|
||||||
The bug is silent: `nix eval` succeeds, the unit starts, but PATH
|
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
|
`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 setuid sudo wrapper lives at `/run/wrappers/bin/sudo` and
|
||||||
the path entry resolves to `/run/wrappers/bin/bin` instead.
|
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
|
`builtins.storePath`. `environment.systemPackages` and
|
||||||
`systemd.services.<name>.path` both do it (the latter takes plain
|
`systemd.services.<name>.path` both do it (the latter takes plain
|
||||||
strings like `/run/wrappers` happily, but anything under
|
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
|
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
|
farm built from the interpolated string — before it can go anywhere a
|
||||||
package is expected.
|
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 |
|
| `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 |
|
| `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
|
with its commit under its internal `META_LOCK` mutex, so a standalone commit
|
||||||
node would open a dirty-working-tree window between nodes.
|
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`,
|
`Reconcile`, `WriteDropin`, `Create`) or at a **brace** (`AgentWindow`,
|
||||||
`DeployWindow`) on behalf of a whole coordinated subtree; held by the owning
|
`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
|
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
|
(`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
|
so a power-op DAG's intent write + reconcile is atomic — two racing ops can't
|
||||||
clobber intent before either reconciles.) **Lease-exempt**: `MetaSync`,
|
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
|
`approval_id`, `inputs`, `build_log_id`). Group roots ride as ordinary
|
||||||
nodes, so a group's state is just the root's own `state`.
|
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
|
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
|
are likewise **per-node**: the dashboard renders the node tree and keys the
|
||||||
live-log panel off the running node.
|
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
|
`buildSlots` (default `1`) sets how many nix-heavy job-queue nodes
|
||||||
(prebuilds, profile swaps, first-spawn creates, meta lock bumps) run
|
(prebuilds, profile swaps, first-spawn creates, meta lock bumps) run
|
||||||
concurrently. The default serializes all heavy nix work like the pre-DAG
|
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 —
|
agent toplevels at once. Per-agent correctness is independent of the count —
|
||||||
each agent's container-affecting ops serialize on its lifecycle lease
|
each agent's container-affecting ops serialize on its lifecycle lease
|
||||||
regardless.
|
regardless.
|
||||||
|
|
@ -513,7 +513,7 @@ requiring a host rebuild.
|
||||||
The same drop-in carries `CPUWeight=` / `IOWeight=` from
|
The same drop-in carries `CPUWeight=` / `IOWeight=` from
|
||||||
`agentCpuWeight` / `agentIoWeight`. Those are a different kind of
|
`agentCpuWeight` / `agentIoWeight`. Those are a different kind of
|
||||||
setting: the quota and the memory max are **hard caps** that throttle
|
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_.
|
v2 **relative shares** that only decide who yields _under contention_.
|
||||||
A low-weight container still gets the whole machine when nothing else
|
A low-weight container still gets the whole machine when nothing else
|
||||||
wants it.
|
wants it.
|
||||||
|
|
@ -560,7 +560,7 @@ trade-off:
|
||||||
binary are added to the host system closure (low single-digit GB
|
binary are added to the host system closure (low single-digit GB
|
||||||
additional).
|
additional).
|
||||||
- **Off** (default): the host closure stays lean; the first spawn
|
- **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).
|
minutes on a fresh store).
|
||||||
|
|
||||||
**Note**: toplevels are pinned to `x86_64-linux`. Enabling on an
|
**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.
|
Claude Code's built-in OpenTelemetry integration.
|
||||||
|
|
||||||
This is a **hive-wide** setting: one switch in the host NixOS config enables it
|
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
|
## Enabling export
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ leaves the swarm — optional, because the swarm's own metrics store
|
||||||
telemetry goes to both. See
|
telemetry goes to both. See
|
||||||
[`swarm/services.md`](../swarm/services.md#metrics-victoriametrics--grafana).
|
[`swarm/services.md`](../swarm/services.md#metrics-victoriametrics--grafana).
|
||||||
|
|
||||||
**There is exactly one way telemetry leaves a hive: through the collector that
|
**Telemetry leaves a hive exactly one way: through the collector that
|
||||||
`enable` starts on the host.** Agents never talk to `endpoint` themselves —
|
`enable` starts on the host.** Agents never talk to `endpoint` themselves —
|
||||||
they export unauthenticated to a bridge address only their own containers can
|
they export unauthenticated to a bridge address only their own containers can
|
||||||
reach. That collector forwards to the swarm's
|
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 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
|
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_.
|
means _reachable from an agent container_, not _presents a credential_.
|
||||||
|
|
||||||
The consequence, stated because it's a choice rather than an oversight: **any
|
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
|
`hive=` from the connection it authenticated, so _that_ label becomes
|
||||||
unforgeable.
|
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
|
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
|
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
|
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";
|
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
|
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
|
collector has no anonymous route to accept samples on, and every path it serves
|
||||||
belongs to exactly one hive.
|
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
|
`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
|
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
|
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";`.
|
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
|
conventionally `<name>.<swarm.domain>`, but a wrong domain that
|
||||||
evaluates cleanly points at a real machine that isn't the one you meant.
|
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
|
> 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.
|
> hive chains to it, so one anchor replaces per-hive pinning entirely.
|
||||||
> What that genuinely drops is trusting a hive whose root this swarm
|
> 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
|
`autoConfigure`. A hive with neither serves the hive leaf on those names
|
||||||
and every client sees a name mismatch.
|
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
|
It's deliberately not an assertion: this module can see what _it_ is
|
||||||
able to issue, but not an operator-installed sub-CA in
|
able to issue, but not an operator-installed sub-CA in
|
||||||
`swarm.ca.stateDir`, an external ACME setup, or a certificate delivered
|
`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,
|
`deploy.nats.calloutUserPublicKey`, is published to every client that connects,
|
||||||
so the store is a perfectly good place for it.
|
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
|
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
|
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
|
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
|
Every row below is read against one of these. This is a different cut
|
||||||
from the [deployment shapes](services.md#deployment-shapes) --- those
|
from the [deployment shapes](services.md#deployment-shapes) --- those
|
||||||
say _where services run_, these say _who is responsible for a secret
|
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.
|
neither is a renaming of the other.
|
||||||
|
|
||||||
| topology | what it means | who places secrets |
|
| 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
|
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.
|
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
|
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
|
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
|
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
|
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.
|
layers from its cause.
|
||||||
|
|
||||||
The store's **first reader** is the matrix registration token, and it's worth
|
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
|
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>`
|
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.
|
service. Not by policy: by construction, and openssl enforces it.
|
||||||
|
|
||||||
Whatever holds the swarm root is therefore what makes swarm-service certificates
|
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
|
no setting here changes that, so Explore is the log browser for this
|
||||||
swarm.
|
swarm.
|
||||||
|
|
||||||
| Option | When you'd touch it |
|
| 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. |
|
| `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.domain` | Only to rename it. |
|
||||||
| `swarm.victorialogs.port` | Only if something else on the services host already claims `9428`. |
|
| `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
|
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
|
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,
|
Every hive's own collector reaches this one by its gateway name,
|
||||||
`swarm.otel.domain` (default `otel.<swarm domain>`) — the same
|
`swarm.otel.domain` (default `otel.<swarm domain>`) — the same
|
||||||
by-domain-through-the-gateway shape every other swarm service uses, not a
|
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
|
that doesn't run the swarm's services; the name resolves through the
|
||||||
gateway either way.
|
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.
|
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.
|
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
|
optional first-login convenience. Run it before concluding anything is
|
||||||
wrong with the proxy: a 502 here means "no users yet" far more often
|
wrong with the proxy: a 502 here means "no users yet" far more often
|
||||||
than it means a routing fault.
|
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,
|
`user add` only ever adds: on a name that already exists it refuses,
|
||||||
rather than resurfacing as a second account or a silent overwrite.
|
rather than resurfacing as a second account or a silent overwrite.
|
||||||
Editing an existing subject is `user update`, and the flags compose, so
|
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
|
```console
|
||||||
# swarmctl user update mara --add-group admins --email mara@example.com
|
# 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
|
in one place: [`secrets.md`](secrets.md), including authelia's own keys
|
||||||
(session, JWT, storage-encryption, OIDC HMAC, OIDC issuer) and the two
|
(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
|
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
|
different containers — are why the client secret's plaintext half needs
|
||||||
the delivery step below and the rest of authelia's keys don't.
|
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
|
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
|
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
|
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
|
### 2. Swarm-managed services
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ services.hyperhive.swarm = {
|
||||||
|
|
||||||
**Both are asserted at eval.** A hive that boots with SSO
|
**Both are asserted at eval.** A hive that boots with SSO
|
||||||
half-configured shows a login button that always fails — a symptom
|
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.
|
evaluation error.
|
||||||
|
|
||||||
## Where each relying party differs
|
## 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
|
- `/api/oidc/token`, `/.well-known/openid-configuration` — a client
|
||||||
parsing an error page as its JSON document
|
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
|
error interception. The split is by _audience_, not by an enumerated path
|
||||||
list: a human gets the page, every machine caller gets the status.
|
list: a human gets the page, every machine caller gets the status.
|
||||||
Enumerating endpoints individually would leave the next one added
|
Enumerating endpoints individually would leave the next one added
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ Exposed as `mcp__bash__kill`.
|
||||||
|
|
||||||
## Namespace note
|
## Namespace note
|
||||||
|
|
||||||
`run` and `status` live in the `bash` MCP server, not `hyperhive`. So
|
`run` and `status` live in the `bash` MCP server, not `hyperhive`. The
|
||||||
the tool names in claude are `mcp__bash__run` and `mcp__bash__status`.
|
tool names in claude are `mcp__bash__run` and `mcp__bash__status`.
|
||||||
The `Bash` built-in tool is blocked — all shell execution goes through
|
The `Bash` built-in tool is blocked — all shell execution goes through
|
||||||
this structured path so tasks get task-id tracking and structured output.
|
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:**
|
###### **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`
|
Default value: `write`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ hive-forge repo-labels --json # full label ob
|
||||||
|
|
||||||
### Which read verb when
|
### 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:
|
distinct output shape — pick by what you need:
|
||||||
|
|
||||||
| Need | Verb | Shape |
|
| 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.
|
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>`
|
**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
|
reads the per-surface public URLs from c0re's service env — so custom
|
||||||
forge / matrix domains resolve correctly instead of assuming
|
forge / matrix domains resolve correctly instead of assuming
|
||||||
`forge.<domain>`. The URL is **always printed** (the reliable core, since
|
`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
|
as a convenience — a missing or failing opener is reported as a note, not
|
||||||
an error.
|
an error.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ Add a user, generating a password for them
|
||||||
|
|
||||||
Change an existing user's attributes.
|
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>`
|
**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
|
to the helper as fd 3 (`LISTEN_FDS`). The helper requires this and
|
||||||
bails if it isn't socket-activated.
|
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
|
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
|
group rather than `hive-core`, and a `hive-core` client couldn't
|
||||||
connect the way the socket unit's `SocketGroup` grant intends.
|
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`).
|
collaborator grants; can't act as another agent or as `core`).
|
||||||
- **matrix token** → that agent's matrix account only.
|
- **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
|
_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."
|
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`
|
After the outcome handler, the stats sink records a row. `handle_turn`
|
||||||
reports the result to `serve_loop` via `TurnControl { auth_failed }` —
|
reports the result to `serve_loop` via `TurnControl { auth_failed }` —
|
||||||
on auth failure the loop parks in `wait_for_login`; otherwise it loops
|
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
|
straight back to the idle wait (step 1). No same-turn
|
||||||
self-continue mechanism: every multi-step continuation rides an
|
self-continue mechanism exists: every multi-step continuation rides an
|
||||||
external wake instead — a new inbox message, a `remind`, or an
|
external wake instead — a new inbox message, a `remind`, or an
|
||||||
in-container todo wake (bash-task completion, forge notification,
|
in-container todo wake (bash-task completion, forge notification,
|
||||||
matrix activity). Ending the turn and letting one of
|
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
|
**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
|
harness unit's PATH. By default that's the `claude-code` in the agent's
|
||||||
own nixpkgs (the meta flake's `nixpkgs` input) via
|
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
|
this package moves fast, the operator can pin one hive-wide with
|
||||||
`services.hyperhive.c0re.claudeCodePackage`: its store path is written
|
`services.hyperhive.c0re.claudeCodePackage`: its store path is written
|
||||||
into each agent's flake, and `claude` on PATH becomes a symlink to it
|
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`
|
_still_ overflows, `run` surfaces `Error::PromptTooLong`; `drive_turn`
|
||||||
then archives the session (session lifecycle stays hive-side) and the
|
then archives the session (session lifecycle stays hive-side) and the
|
||||||
serve loop requeues the message so it redelivers into a fresh session
|
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).
|
the overflow was the accumulated context the archive clears).
|
||||||
- **Proactive** — a turn finishes cleanly but the last inference's context
|
- **Proactive** — a turn finishes cleanly but the last inference's context
|
||||||
size crossed the policy watermark. While the session is still healthy it
|
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
|
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,
|
`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
|
`--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` →
|
session (same mechanism as the operator reset — rename `<uuid>.jsonl` →
|
||||||
`.archived`) so the next turn's `--resume <title>` misses and starts
|
`.archived`) so the next turn's `--resume <title>` misses and starts
|
||||||
fresh. Unlike proactive compaction the session is dropped entirely, not
|
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
|
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
|
other turn: if the context crossed the watermark the harness runs a
|
||||||
notes-checkpoint turn and then `/compact`. Compacting before shutdown
|
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
|
- `ack_until(up_to)` — bulk-mark inbox rows handled: every row with
|
||||||
broker id `<= up_to` is stamped as acked in a single UPDATE.
|
broker id `<= up_to` is stamped as acked in a single UPDATE.
|
||||||
Recipient-scoped (agents can only ack their own rows). Use when a
|
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
|
payloads spill to `/agents/<self>/state/reminders/`. Pending count
|
||||||
capped at 50 per agent (`HIVE_REMIND_MAX_PENDING_PER_AGENT`).
|
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
|
[Turn outcomes](README.md#turn-outcomes) for why. Multi-step work rides
|
||||||
`remind` for a durable self-wake, or an in-container todo wake
|
`remind` for a durable self-wake, or an in-container todo wake
|
||||||
(bash-task completion, forge notification, matrix activity) for work
|
(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
|
- **Pills cluster** (`.agent-header-pills`, right-aligned): status
|
||||||
badges (`StatusChips.tsx`) and flyout triggers together in one row —
|
badges (`StatusChips.tsx`) and flyout triggers together in one row —
|
||||||
a deliberate choice (not the historical default) so the header reads
|
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:
|
separate clusters:
|
||||||
- **Alive badge**: `● alive` (green) / `⊘ rate limited` (red) /
|
- **Alive badge**: `● alive` (green) / `⊘ rate limited` (red) /
|
||||||
`◌ needs login` / `◌ logging in` / `○ offline` / `… connecting`.
|
`◌ 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
|
(`GET /api/state`'s `links` field) also feeds
|
||||||
`DashboardState.links` for the dashboard card's icon strip —
|
`DashboardState.links` for the dashboard card's icon strip —
|
||||||
`agent_links()` in hive-agent is the single source of truth for
|
`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
|
to hold exactly this dashboard link plus a rebuild-container action
|
||||||
(mara, hyperhive#3704: "remove rebuild button, move link to
|
(mara, hyperhive#3704: "remove rebuild button, move link to
|
||||||
dashboards into links menu") — rebuild had no real discoverability
|
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
|
⚠️ **Not only *destroyed* agents.** Nothing records a destroy: every
|
||||||
definition-side artifact (state subvolume, proposed + applied repos,
|
definition-side artifact (state subvolume, proposed + applied repos,
|
||||||
meta registration, topology entry) is written by `Provision` *before*
|
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
|
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
|
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
|
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
|
and its checkbox cells, where each checkbox carries `data-baseline` (its
|
||||||
render-time state) and the row carries `data-agent` for dirty-tracking.
|
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
|
**Saving — one button for the whole page.** No per-row save
|
||||||
buttons. A single page-level `.perm-save-bar` with a **save all (N
|
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
|
agents)** button sits at the bottom of the pane, enabled only when some
|
||||||
checkbox diverges from its baseline. Clicking it diffs every checkbox
|
checkbox diverges from its baseline. Clicking it diffs every checkbox
|
||||||
across *both* matrices and POSTs one batch to `POST /api/permissions` as
|
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
|
login`, `needs update` — plus **one `◐ pending-state…` pill per
|
||||||
active transient** (replaces buttons during operator-initiated
|
active transient** (replaces buttons during operator-initiated
|
||||||
start / stop / restart / rebuild / destroy). An agent can carry
|
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
|
running alongside a `stop_for_update` on the same agent — and each
|
||||||
renders as its own independent badge rather than being collapsed
|
renders as its own independent badge rather than being collapsed
|
||||||
into one label, matching the existing multi-badge convention this
|
into one label, matching the existing multi-badge convention this
|
||||||
|
|
@ -1337,8 +1337,8 @@ payload):
|
||||||
every queue mutation (enqueue, state transition, dedup collapse,
|
every queue mutation (enqueue, state transition, dedup collapse,
|
||||||
terminal-history trim). Unlike the snapshot events below it ships
|
terminal-history trim). Unlike the snapshot events below it ships
|
||||||
no state at all: the client re-fetches `GET /api/jobq/graph`, which
|
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
|
is where it reads the queue from cold too. No
|
||||||
`/api/state.rebuild_queue` — it went with the typed projection.
|
`/api/state.rebuild_queue` exists — it went with the typed projection.
|
||||||
- `schedules_changed` (seq, schedules: `Vec<WireSchedule>`) —
|
- `schedules_changed` (seq, schedules: `Vec<WireSchedule>`) —
|
||||||
full snapshot of all scheduled prompts. Emitted after every
|
full snapshot of all scheduled prompts. Emitted after every
|
||||||
operator mutation via the `/api/schedules` surface (new /
|
operator mutation via the `/api/schedules` surface (new /
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ repeated here.
|
||||||
- **Frosted / tinted glass** where it fits — translucency over flat
|
- **Frosted / tinted glass** where it fits — translucency over flat
|
||||||
panels, not a hard rule for every surface.
|
panels, not a hard rule for every surface.
|
||||||
- **Playful use of the multiple accent colours**, without tipping into
|
- **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
|
(`--purple`, `--cyan`, `--pink`, …); use more than one where it adds
|
||||||
distinction, not decoration for its own sake.
|
distinction, not decoration for its own sake.
|
||||||
- **Whimsy** — small, delightful touches are welcome (the per-hive
|
- **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."
|
"something went wrong, try reloading."
|
||||||
|
|
||||||
This follows from **power-user-first**, one of the standing principles:
|
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
|
errors should give them what they need to actually diagnose a problem
|
||||||
rather than a friendly wall. Power-user-first doesn't mean
|
rather than a friendly wall. Power-user-first doesn't mean
|
||||||
newcomer-hostile — the UI should still be self-explanatory, warn or ask
|
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.
|
/// Collaborator's forge login to add.
|
||||||
user: String,
|
user: String,
|
||||||
/// Permission level to grant (default: write — a freshly added
|
/// 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)]
|
#[arg(long, value_enum, default_value_t = Permission::Write)]
|
||||||
permission: Permission,
|
permission: Permission,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -668,7 +668,7 @@ pub enum SnapshotCmd {
|
||||||
/// binds the sender's address to its key), so there is no credential
|
/// binds the sender's address to its key), so there is no credential
|
||||||
/// to pass here.
|
/// 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`.
|
/// from `services.hyperhive.swarm.snapshotStore`.
|
||||||
Push {
|
Push {
|
||||||
/// Snapshot label passed to `subvol snapshot create --label`.
|
/// Snapshot label passed to `subvol snapshot create --label`.
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ enum UserVerb {
|
||||||
/// Change an existing user's attributes.
|
/// Change an existing user's attributes.
|
||||||
///
|
///
|
||||||
/// Every flag is optional and they compose, so one call can set
|
/// 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
|
/// password: regenerating a credential is a different intent from
|
||||||
/// editing an attribute, and folded together an attribute edit can
|
/// editing an attribute, and folded together an attribute edit can
|
||||||
/// invalidate a login by accident.
|
/// invalidate a login by accident.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue