agent capabilities live in a swarm KV record, set from swarmctl
An agent's capabilities move from the hive's meta repo
(`meta/capabilities.json`, injected as `HIVE_CAPABILITIES`) into the
`capabilities` field of its key in a new `agent-permissions` KV bucket,
which swarm-controller alone writes.
- swarm-queue-client: `agent_permissions` (bucket, key, `AgentPermissions`,
direct-get read, the import subject and its wire types).
- swarm-controller: creates the bucket at start; `GET|PUT|DELETE
/api/agents/{name}/permissions` (deny_unknown_fields and unknown
capability ⇒ 422, duplicate ⇒ 400, agent must be declared by a hive);
answers each hive's one-shot import, writing a key only where there is
none and only for agents that hive's wanted state declares.
- swarmctl: `agent permissions get|set|clear`.
- swarm-nats-auth: a hive reads every key of the bucket and sends its
import on its own subject; the controller subscribes to the imports and
may answer them (`responses`).
- hive-c0re: reads each of its agents' keys every 30s into
/var/lib/hyperhive/swarm-agent-permissions.json and restarts an agent,
gracefully, when its capabilities change. Unset is the compiled-in
default (ruth: manage_root_agent; everyone else: none). A failed read
keeps the stored value. On first boot it seeds that file from
meta/capabilities.json, sends the grants to the controller, and deletes
the file once the controller answers.
Deleted: the capabilities.json reader/writer, `HIVE_CAPABILITIES` in the
rendered meta flake, ruth's capability seed, the capabilities half of the
permissions rebuild job (`PermPayload::Capabilities`/`Combined`), the
dashboard's capabilities table and `capabilities_changed` event, and the
now-unused `Capability::description`.
Part of #5170.
This commit is contained in:
parent
fcda1f4a04
commit
f862354e43
47 changed files with 1943 additions and 1146 deletions
|
|
@ -77,8 +77,8 @@ H0M3 hub (served at
|
|||
`/core.html`), with minimal chrome: a `← home` back-link and a single pane,
|
||||
**K3PT ST4T3**. The page is its own esbuild bundle (`core.js`) that
|
||||
cold-loads `/api/state` and subscribes to `/api/dashboard/stream` for
|
||||
`tombstones_changed`, `capabilities_changed`, and `tool_groups_changed`
|
||||
(the latter two re-render the stale-perms sub-section when permission
|
||||
`tombstones_changed` and `tool_groups_changed`
|
||||
(the latter re-renders the stale-perms sub-section when permission
|
||||
data changes). (Rebuild queue and meta inputs are dashboard tabs — see
|
||||
below.)
|
||||
|
||||
|
|
@ -104,11 +104,10 @@ container is all hive-c0re can actually prove. Fixing it properly
|
|||
needs a recorded destroy — **deferred** to the swarm-controller /
|
||||
snapshot-storage rework where the problem changes shape.
|
||||
|
||||
*Stale permission entries*: agents with explicit capability or
|
||||
tool-group JSON entries but no live container — typically renamed or
|
||||
deleted agents whose entries persisted in `capabilities.json` /
|
||||
`tool-groups.json`. Loaded when the page opens;
|
||||
autorefreshes on `capabilities_changed` + `tool_groups_changed` SSE
|
||||
*Stale permission entries*: agents with explicit tool-group JSON
|
||||
entries but no live container — typically renamed or deleted agents
|
||||
whose entries persisted in `tool-groups.json`. Loaded when the page opens;
|
||||
autorefreshes on `tool_groups_changed` SSE
|
||||
events. Each ghost agent gets a `✕ clear perms` button
|
||||
(`DELETE /api/permissions/{name}`).
|
||||
|
||||
|
|
@ -230,11 +229,13 @@ entry with that id pre-expanded.
|
|||
|
||||
## P3RM1SS10NS tab
|
||||
|
||||
Per-agent permission configuration. Two sections, each rendered as a
|
||||
column-driven checkbox matrix: rows are agents, columns are the
|
||||
permission names fetched from hive-c0re. The column list is
|
||||
authoritative — adding a new tool-group or capability to hive-c0re
|
||||
requires no UI change; the new column appears automatically.
|
||||
Per-agent tool-group configuration, rendered as a column-driven
|
||||
checkbox matrix: rows are agents, columns are the group names fetched
|
||||
from hive-c0re. The column list is authoritative — adding a new
|
||||
tool-group to hive-c0re requires no UI change; the new column appears
|
||||
automatically. Capabilities aren't set here: they're the swarm's,
|
||||
set with `swarmctl agent permissions` (see
|
||||
`docs/process/conventions.md#capabilities`).
|
||||
|
||||
The snapshot carries `agents` (the full manageable roster — live
|
||||
containers ∪ agents with an explicit entry) and `effective` (per-agent
|
||||
|
|
@ -248,29 +249,11 @@ strip those defaults. The `(default)` badge keys off absence from
|
|||
|
||||
Fetches fire on tab activation (not page-load) to avoid unnecessary
|
||||
work when the operator never visits this tab. Live mutations from the
|
||||
rebuild-queue worker are also pushed via the `capabilities_changed` /
|
||||
`tool_groups_changed` SSE events (same payload shape as the GET
|
||||
endpoints), so an open P3RM1SS10NS tab reflects worker-applied changes
|
||||
rebuild-queue worker are also pushed via the `tool_groups_changed`
|
||||
SSE event (same payload shape as the GET endpoint), so an open P3RM1SS10NS tab reflects worker-applied changes
|
||||
without requiring navigation. Tab-activation re-fetches remain as a
|
||||
safety net for reconnect windows.
|
||||
|
||||
**C4P4B1L1T13S** — per-agent capability grants. Capabilities unlock
|
||||
gated MCP tools and system-level access beyond the default agent
|
||||
surface. A saving POST queues a rebuild for the affected agent so the
|
||||
new `HIVE_CAPABILITIES` env var takes effect in the next session.
|
||||
|
||||
The current capabilities are:
|
||||
|
||||
| Name | Effect |
|
||||
|------|--------|
|
||||
| `manage_root_agent` | mounts every agent's state (rw) + config (ro) into this agent's container so it can manage/recover any agent |
|
||||
|
||||
Each row is one agent. Columns are the capability names returned by
|
||||
`GET /api/capabilities` as `caps: Vec<String>`. Checking or unchecking
|
||||
boxes only stages the change in-browser; the browser writes nothing until
|
||||
the operator selects the page-level **save all** button (described below). Row
|
||||
values follow the `effective`/`assignments` rule described above.
|
||||
|
||||
<!-- vale write-good.Passive = NO -->
|
||||
**T00L GR0UPS** — per-agent tool-group permissions. Tool groups are
|
||||
named buckets of MCP tools; each agent starts with a role default
|
||||
|
|
@ -291,10 +274,9 @@ The current tool groups are: `messaging`, `meta`, `inbox`, `lifecycle`,
|
|||
Claude's built-in `WebFetch` and `WebSearch` to `--tools` /
|
||||
`--allowedTools` for that agent session.
|
||||
|
||||
Both tables share the same visual shape: `.cap-table-wrap` /
|
||||
`.tg-table-wrap` outer scroll container, `thead` with a label column
|
||||
(`.cap-agent-col` / `.tg-agent-col`) + one column per permission
|
||||
(`.cap-col` / `.tg-group-col`). Each tbody row is one agent: a name cell
|
||||
The table's shape: a `.tg-table-wrap` outer scroll container, `thead`
|
||||
with a label column (`.tg-agent-col`) + one column per group
|
||||
(`.tg-group-col`). Each tbody row is one agent: a name cell
|
||||
and its checkbox cells, where each checkbox carries `data-baseline` (its
|
||||
render-time state) and the row carries `data-agent` for dirty-tracking.
|
||||
|
||||
|
|
@ -302,18 +284,15 @@ render-time state) and the row carries `data-agent` for dirty-tracking.
|
|||
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. Selecting it diffs every checkbox
|
||||
across *both* matrices and POSTs one batch to `POST /api/permissions` as
|
||||
`{ changes: [ { agent, tool_groups?, capabilities? } ] }` — the batch
|
||||
includes only the perm-types that actually changed for each agent (an
|
||||
omitted field leaves that file untouched; an included array fully
|
||||
replaces it).
|
||||
hive-c0re coalesces an agent's capabilities + tool-groups into a
|
||||
single rebuild, so changing both for one agent is one rebuild, not two.
|
||||
and POSTs one batch to `POST /api/permissions` as
|
||||
`{ changes: [ { agent, tool_groups } ] }` — the batch includes only the
|
||||
agents whose groups changed (an included array fully replaces the
|
||||
agent's groups).
|
||||
The batch is atomic: it validates every change first and on any error
|
||||
rejects the whole POST (`{error}`, nothing applied); a clean 200 (`ok`)
|
||||
flips the bar to a queued→rebuilding state and re-fetches both tables.
|
||||
Live `capabilities_changed` / `tool_groups_changed` events re-render the
|
||||
matrices unless the section has unsaved edits, so an in-progress edit set
|
||||
flips the bar to a queued→rebuilding state and re-fetches the table.
|
||||
Live `tool_groups_changed` events re-render the
|
||||
matrix unless it has unsaved edits, so an in-progress edit set
|
||||
isn't clobbered.
|
||||
|
||||
## ST4TS page (`/stats.html`)
|
||||
|
|
@ -767,31 +746,16 @@ below — some endpoints aren't in it yet.
|
|||
`/var/lib/hyperhive/meta/tool-groups.json` and queues a rebuild so
|
||||
`HIVE_TOOL_GROUPS` takes effect. Agent name validated;
|
||||
`guard_agent_name` applied.
|
||||
- `GET /api/capabilities` — returns `{ caps: Vec<String>,
|
||||
assignments: BTreeMap<String, Vec<String>>,
|
||||
descriptions: BTreeMap<String, String> }`. `caps` is the
|
||||
ordered list of all known capability names; `assignments` is the
|
||||
per-agent grant map (absent agents have no extra capabilities);
|
||||
`descriptions` maps each capability name to a short human-readable
|
||||
string surfaced as a column-header tooltip on hover.
|
||||
- `POST /api/capabilities/{agent}` — body `{ caps: ["name", …] }`.
|
||||
Writes the capability set for `{agent}` to
|
||||
`/var/lib/hyperhive/meta/capabilities.json` and queues a rebuild so
|
||||
`HIVE_CAPABILITIES` takes effect. Agent name validated;
|
||||
unknown capability strings rejected (400). `guard_agent_name`
|
||||
applied.
|
||||
- `POST /api/permissions` — batch perm apply for the save-all
|
||||
permissions button. Body
|
||||
`{ changes: [{ agent, tool_groups?: ["name", …], capabilities?: ["name", …] }] }`.
|
||||
Sparse per agent: an omitted field leaves that perm-type untouched,
|
||||
an empty array clears it, a populated array fully replaces it (same
|
||||
replace semantics as the per-agent endpoints above). Each affected
|
||||
agent gets ONE combined `PermChange` queue entry, so changing both
|
||||
an agent's tool-groups and capabilities triggers a single rebuild,
|
||||
not two. **Atomic**: the endpoint validates every change first (agent names via
|
||||
`guard_agent_name`, group + capability names) and on any validation
|
||||
error writes or enqueues nothing (non-2xx `{ error }`); it skips
|
||||
rows with both fields omitted rather than erroring on them.
|
||||
`{ changes: [{ agent, tool_groups?: ["name", …] }] }`.
|
||||
An omitted `tool_groups` leaves the agent untouched, an empty array
|
||||
clears it, a populated array fully replaces it (same replace
|
||||
semantics as the per-agent endpoint above). Each affected agent gets
|
||||
one `PermChange` queue entry. **Atomic**: the endpoint validates every
|
||||
change first (agent names via `guard_agent_name`, group names) and on
|
||||
any validation error writes or enqueues nothing (non-2xx `{ error }`);
|
||||
it skips rows with the field omitted rather than erroring on them.
|
||||
Returns `200 "ok"` on
|
||||
success.
|
||||
- `POST /api/meta-update` — `nix flake update` the selected
|
||||
|
|
@ -841,13 +805,6 @@ payload):
|
|||
operator cancel / retry (`/api/system/reminders/*`), `cancel_loose_end`
|
||||
with Reminder kind, and the scheduler tick after each delivery
|
||||
batch (`reminder_scheduler`).
|
||||
- `capabilities_changed` (seq, caps: `Vec<str>`, descriptions: map,
|
||||
assignments: `BTreeMap<String, Vec<String>>`) — full snapshot of
|
||||
capability grants. Emitted from the rebuild-queue worker after a
|
||||
`PermChange` / Capabilities entry commits the JSON file. Payload
|
||||
matches `GET /api/capabilities` shape so the client can call
|
||||
`renderCapabilities` directly. P3RM1SS10NS tab subscribes; activation
|
||||
re-fetch still runs as a safety net.
|
||||
- `tool_groups_changed` (seq, groups: `Vec<str>`, descriptions: map,
|
||||
assignments: `BTreeMap<String, Vec<String>>`) — full snapshot of
|
||||
tool-group assignments. Emitted from the rebuild-queue worker after
|
||||
|
|
|
|||
Loading…
Reference in a new issue