diff --git a/docs/conventions.md b/docs/conventions.md index 3282cffd..cbd3e46b 100644 --- a/docs/conventions.md +++ b/docs/conventions.md @@ -365,6 +365,7 @@ that allows the underlying resource access. | `manage_root_agent` | may lifecycle-manage the root/manager agent via `kill`/`start`/`restart` | | `read_host_journal` | `get_host_journal` MCP tool is registered + `GET /journal-host` requests are served | | `query_agent_state` | may call `get_loose_ends` / `CountPendingReminders` targeting non-child agents | +| `infra_admin` | may call `restart(name)` on hive infrastructure containers (`hive-ci`, `hive-forge`, `hive-matrix` — **not** `hive-gateway`, which is the host's nginx and is operator-only); each restart is logged to the dashboard AUDIT trail | **Config storage** — per-agent capabilities live in `/var/lib/hyperhive/meta/capabilities.json` alongside `tool-groups.json`. diff --git a/docs/web-ui/dashboard.md b/docs/web-ui/dashboard.md index 6cad964d..eaca8607 100644 --- a/docs/web-ui/dashboard.md +++ b/docs/web-ui/dashboard.md @@ -106,7 +106,7 @@ Passive / rare-interaction state. No longer a dashboard tab — it's a standalone page reached from the **Core** tile on the H0M3 hub (served at `/core.html`), with the same minimal chrome as `/logs.html`: a `← home` back-link + a `createTabStrip` sub-tab nav (**K3PT ST4T3** default, -then **C0NT41N3R L04D**). The page is its own esbuild bundle (`core.js`) +then **C0NT41N3R L04D**, then **1NFR4**). 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 @@ -159,10 +159,27 @@ omitted — agents share the host netns, so there is no per-container net counter (per-agent network needs the netns-isolation roadmap in `docs/network.md`). -Hive infrastructure services (`hive-ci`, `hive-forge`, `hive-gateway`, -`hive-matrix`) have no dashboard panel — `hivectl stop`/`start`/`restart` -is the only control surface, a separate host-admin-socket path with no -HTTP route and no agent-facing equivalent. +**1NFR4** — start / stop / restart the four hive infrastructure services +(`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`) directly from the +dashboard, without needing an `infra_admin` agent's `restart` MCP tool. +Three are containers; `hive-gateway` is the host's `nginx.service`, and is +the one an agent may **not** restart — this panel is the way it gets +bounced. One row per service: name, a `badge-ok`/`badge-fail` +running/stopped dot, and `↺ R3ST4RT` + `■ ST0P` (running) or `▶ ST4RT` +(stopped) buttons, same themed-confirm pattern as the K3PT ST4T3 +tombstone actions. Backed by +`POST /api/infra-container/{name}/{action}` (`action` ∈ +`start|stop|restart`), which calls the same +`priv_client::control_infra_container` helper the agent-facing +`infra_admin` path uses — no new privileged-helper surface, no +capability check (the dashboard is already operator-authenticated). +Every attempt is written to the audit log (actor `"operator"`, action +`start_infra`/`stop_infra`/`restart_infra`) alongside agent-driven infra +restarts. Status rows ride the `infra_containers` field on +`GET /api/state`'s `StateSnapshot` (`{name, running}`, live +`systemctl is-active container@.service` read); `core.js` polls +`/api/state` every 5 s only while the 1NFR4 sub-tab is active, same +cadence/lifecycle as C0NT41N3R L04D's polling. ## BU1LDS page (`/builds.html`) @@ -423,6 +440,7 @@ The current capabilities are: | `manage_root_agent` | allows the `set_status` / lifecycle tools on the root agent | | `read_host_journal` | unlocks `get_host_journal` to read journald from inside a container | | `query_agent_state` | allows `get_loose_ends(agent: "")` calls targeting other agents | +| `infra_admin` | allows `restart` on hive infrastructure containers (`hive-ci`, `hive-forge`, `hive-matrix`; the gateway is operator-only); each restart is logged to the AUDIT trail | Each row is one agent. Columns are the capability names returned by `GET /api/capabilities` as `caps: Vec`. Checking or unchecking @@ -712,7 +730,7 @@ navigation. A dedicated log-viewer page (not a tab pane — a separate HTML page), reachable from the Logs tile on the H0M3 hub. Minimal chrome: a `← home` back link and a three-item sub-tab strip. Tab -routing is hash-based (`#agent`, `#infra`, `#system`); default is +routing is hash-based (`#agent`, `#system`, `#audit`); default is `#agent`. (Build log history has moved to the BU1LDS page — see above.) **AGENT sub-tab** — per-container journald viewer. Two selects: agent @@ -728,16 +746,6 @@ deep-link directly to a specific agent's journal. A "fetched N ago" chip appears after the `↻ refresh` button following each successful fetch and ticks every 30 s. -**INFRA sub-tab** — journald viewer for the four hive infrastructure -containers (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`), a -fixed client-side list (`INFRA_NAMES` in `logs.js` — no dashboard API -exposes just the name list). No unit filter (infra containers don't run -the per-agent hive daemons) — always the full machine journal (or, for -the gateway, the host journal filtered to its own unit). Fetches -`GET /api/journal/{name}?lines=500`, same "fetched N ago" ticker as -AGENT. A `?agent=` deep-link routes here instead of AGENT when -the name is one of the four infra containers. - **SYSTEM sub-tab** — host-side service logs. Unit selector (`hive-c0re.service` / `hive-priv.service`). Fetches `GET /api/journal-host?unit=&lines=500` on activation @@ -746,6 +754,24 @@ chip ticks every 30 s. Available to the operator unconditionally (not capability-gated — the endpoint lives on the hive-c0re dashboard, behind the gateway). +**AUDIT sub-tab** — operator-visible trail of agent-initiated +privileged actions (e.g. infra-container restarts via `infra_admin`). +Lazy-fetched on tab show (like SYSTEM) from `GET /api/audit-log`, which +returns `{ entries, total }` — `entries` newest-first, server-clamped to +the latest 500; `total` drives a "latest 500 of N" count so the clamp is +never silent. Rendered as a filterable table (when / agent / action / +target / outcome / detail); the filter box is a client-side substring +match over the cached rows. The outcome badge colours `ok` green and +`err` red, with an `err` whose `detail` starts `denied:` (a capability +refusal) shown amber and labelled `denied` so it reads apart from an +execution failure. `ts_unix` is an RFC 3339 string; a 30 s ticker keeps the +relative "ago" column honest while the tab is in view. The backing +`audit_log` store records every privileged-action attempt (ok / err / +denied). New entries live-append without a refresh: an `audit_entry_added` +event on `/api/dashboard/stream` (the flattened row) is prepended to the table +and the "latest N of M" count bumped, de-duped by id against the cold +fetch. + ## Container row A full-height **square agent icon** (5em, capped) on the left. The @@ -1182,6 +1208,12 @@ below — some endpoints aren't in it yet. build logs. - `GET /api/journal/{name}?unit=&lines=` — journalctl viewer for a managed container; rendered in the side panel. +- `GET /api/audit-log` — agent-initiated privileged-action audit + trail. Returns `{ entries, total }`: `entries` is a `Vec` + (`id`, `ts_unix` as RFC 3339, `agent`, `action`, `target`, `outcome` + `"ok"`/`"err"`, `detail` nullable), newest first, server-clamped to + 500; `total` is the full row count for a "latest 500 of N" header. + Backs the LOGS page AUDIT sub-tab. - `GET /static/marked.js` serves the vendored `marked` bundle used for markdown previews. - `GET /api/state-file?path=` — bounded @@ -1222,6 +1254,16 @@ below — some endpoints aren't in it yet. a background `du -sxb` of the agent's state dir + container writable rootfs every ~5 min, `-x` excluding the shared read-only nix store. `null` until the first sample lands. +- `POST /api/infra-container/{name}/{action}` — start / stop / restart a + hive infra service (C0R3 › 1NFR4 panel). `name` parses into the + `InfraContainer` allowlist (`hive-ci`/`hive-forge`/`hive-gateway`/ + `hive-matrix`, 400 on unknown), and the variant decides the unit — + `container@.service`, or `nginx.service` for the gateway. + `action` ∈ `start|stop|restart`. Calls + the same `priv_client::control_infra_container` helper the + `infra_admin` agent path uses; records an `audit_log` entry + (`start_infra`/`stop_infra`/`restart_infra`, actor `"operator"`) either + way. - `POST /api/cancel-reminder/{id}` — hard-delete a pending reminder. - `POST /api/retry-reminder/{id}` — re-arm a reminder whose delivery failed (clears the failure state so the scheduler retries). @@ -1387,6 +1429,10 @@ payload): - `meta_update_running` (running: bool) — emitted when a `nix flake update` ripple starts or completes. BU1LDS M3T4 1NPUTS tab uses this to show/hide the "⏳ meta-update running" banner. +- `audit_entry_added` (flattened `AuditEntry` fields: id, ts_unix, + agent, action, target, outcome, detail) — a single new audit-log + row. L0GS AUDIT sub-tab live-prepends the row and bumps the + "latest N of M" count, de-duped by id against the cold fetch. `/api/state` is **only fetched on cold-load and on the few forms that mutate non-event-derived state** (PURG3 + diff --git a/frontend/packages/dashboard/build.mjs b/frontend/packages/dashboard/build.mjs index f4e16c3b..b18e14ea 100644 --- a/frontend/packages/dashboard/build.mjs +++ b/frontend/packages/dashboard/build.mjs @@ -5,7 +5,7 @@ // dist/dashboard.html the operator dashboard SPA — served // at GET /dashboard.html // dist/flow.html served at GET /flow.html -// dist/logs.html served at GET /logs.html (AGENT/INFRA/SYSTEM) +// dist/logs.html served at GET /logs.html (AGENT/SYSTEM/AUDIT) // dist/core.html served at GET /core.html (C0R3: kept // state / container load) // dist/builds.html served at GET /builds.html (BU1LDS: @@ -18,7 +18,7 @@ // tab routing + refreshState // dist/static/flow.js /flow.html entry — broker terminal + // @-mention composer -// dist/static/logs.js /logs.html entry — agent/infra/system +// dist/static/logs.js /logs.html entry — agent/system/audit // log viewer sub-tabs // dist/static/builds.js /builds.html entry — rebuild queue, // meta inputs, build log history diff --git a/frontend/packages/dashboard/src/core.css b/frontend/packages/dashboard/src/core.css index df95e008..15fad5e8 100644 --- a/frontend/packages/dashboard/src/core.css +++ b/frontend/packages/dashboard/src/core.css @@ -22,11 +22,11 @@ body.core-shell { panes; ensure it wins over any inherited display. */ .core-pane[hidden] { display: none; } -/* ─── K3PT ST4T3 container cards ──────────────────────────────────── +/* ─── K3PT ST4T3 + 1NFR4 container cards ──────────────────────────── core.html doesn't load dashboard.css (that's the operator SPA), so the .container-row card styles aren't inherited. Redefine them - here so tombstone entries look like proper cards (similar to agent - cards on the main dashboard) rather than bare list items. */ + here so tombstone and infra entries look like proper cards (similar + to agent cards on the main dashboard) rather than bare list items. */ .containers { list-style: none; padding: 0; diff --git a/frontend/packages/dashboard/src/core.html b/frontend/packages/dashboard/src/core.html index e2dcaf1a..b1ee18c8 100644 --- a/frontend/packages/dashboard/src/core.html +++ b/frontend/packages/dashboard/src/core.html @@ -16,14 +16,12 @@ /core.html) carved out of the dashboard's old SYST3M tab so the dashboard tab strip stays lean. Same minimal chrome as /logs.html — a `← home` back-link to the H0M3 hub + a - sub-tab nav. Two sub-tabs: kept state (tombstones) - and container load. Rebuild queue + meta inputs have moved to + sub-tab nav. Three sub-tabs: kept state (tombstones), + container load, and infra containers (start/stop/restart the four + hive infra containers). Rebuild queue + meta inputs have moved to /builds.html (the build lifecycle hub). Default tab: K3PT ST4T3. - The section
ids (tombstones-section, container-load-section) - match what core.js's renderers target. Hive infra containers - (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`) have no - dashboard panel — `hivectl stop`/`start`/`restart` is the only - control surface. --> + The section
ids (tombstones-section, container-load-section, + infra-containers-section) match what core.js's renderers target. -->