Commit graph

1,280 commits

Author SHA1 Message Date
damocles
dc8a4e2baf feat(#1004,#1006): capability system + read_host_journal / get_host_journal MCP tool 2026-06-01 20:57:36 +02:00
iris
6c75030420 fix(#1015,#1016): declare overflow vars before syncTabFromHash() call
overflowBtn/overflowDrop/overflowWrap were declared after syncTabFromHash()
was invoked. activateTab() calls updateTabbarOverflow() which closes over
these consts — hitting them in the TDZ threw ReferenceError on every page
load, also preventing fetchAndRenderToolGroups() from running (tool-groups
section stuck at loading).
2026-06-01 20:42:42 +02:00
iris
912f9c5ed2 feat(#1007): select-as-action — picking a parent directly triggers M0V3
Selecting any option in the M0V3 dropdown now fires confirm+POST
immediately; no separate button needed. On cancel or after the
request completes, the select resets to the placeholder. Removes
the now-redundant btn-move button and its CSS rule.
2026-06-01 20:31:35 +02:00
iris
1f5197a0a5 feat(#1007): unify M0V3 → ROOT and reparent into single picker
Remove the separate '⇡ M0V3 → ROOT' button from the selection bar.
Add '(no parent)' as the first real option in the existing M0V3
dropdown — selecting it submits an empty new_parent, which the backend
already treats as 'promote to root'.

The select placeholder label changes to '⇢ M0V3 →' so the combined
control reads naturally without the old standalone button. The submit
button enables as soon as any option past the placeholder is chosen
(selectedIndex > 0), which correctly covers both '(no parent)' and
named-parent selections.
2026-06-01 20:31:35 +02:00
iris
a7d31046a6 refactor(#1005): rename capabilities → tool-groups throughout UI
Section heading, element id, CSS classes, and JS functions all renamed
from 'capabilities'/'cap-*' to 'tool-groups'/'tg-*' to accurately
describe what the UI manages (tool-group permissions, not a capabilities
system).
2026-06-01 20:27:28 +02:00
iris
86a1591cfc feat(#1005): capabilities UI — per-agent tool-group table in SYST3M tab
Backend (hive-c0re/src/dashboard.rs):
  GET /api/tool-groups  — returns { groups: [...], assignments: {...} };
    groups list comes from ToolGroup::ALL so the UI needs no change when
    a new group is added (satisfies the 'no extend ui' requirement)
  POST /api/tool-groups/{agent} — accepts { groups: [...] }, calls
    set_groups() then enqueues a rebuild so the new HIVE_TOOL_GROUPS
    env var takes effect immediately

hive-sh4re/src/lib.rs:
  Added ToolGroup::ALL const (ordered slice of every group)
  Added ToolGroup::as_str() — snake_case wire name, matches serde

Frontend:
  SYST3M tab: new C4P4B1L1T13S section above K3PT ST4T3 with
    #capabilities-section placeholder
  tabs.js: fetchAndRenderCapabilities() + renderCapabilities() —
    columns are built from the groups array returned by the API;
    each row has one checkbox per group and a save button that POSTs
    and re-fetches after 800ms; agents without explicit assignments
    show a (default) label; triggered on each SYST3M tab activation
  dashboard.css: .cap-table-wrap/.cap-table/.cap-row/.cap-agent-*
    styles for the scrollable matrix table
2026-06-01 20:19:11 +02:00
iris
81607aca26 docs: add CSS custom properties reference (docs/css-vars.md)
Lists all variables declared in base.css with their hex values,
Catppuccin Mocha names, and intended use. Includes a common-mistakes
table mapping the wrong names (--text, --mauve, --surface0/1/2, etc.)
to the correct ones, plus a usage guide for the most common patterns
(dropdowns, hover states, active tabs, badges).
2026-06-01 19:55:01 +02:00
iris
e8d391be13 fix(#1001): replace var(--mauve) with var(--purple) throughout dashboard.css
--mauve is not declared in base.css; the palette exposes the Catppuccin
Mocha mauve colour as --purple (#cba6f7). Affected rules:
  .btn-move colour + border
  .logs-back colour (back-link on logs page)
  .logs-tab.logs-tab-active text colour (issue #1001 — tab shows no state)
  border colour on a schedule input

Combined with the --surface1/2 fix from the previous commit, the active
log tab now shows a solid background (--border) with purple text (--purple).
2026-06-01 19:52:21 +02:00
iris
e045a29508 fix(#994,#996): replace undefined CSS vars; guard overflow rebuild when open
dashboard.css used var(--surface0/1/2) and var(--text) throughout the
agent context menu, tabbar overflow dropdown, and logs toolbar — none
of which are declared in base.css (the palette only defines --bg-elev,
--border, --purple-dim, --fg etc). This caused all three dropdowns to
render with a transparent background (issue #996).

Replacements:
  --surface0  → --bg-elev   (dropdown background)
  --surface1  → --border    (hover / active state)
  --surface2  → --purple-dim (border / separator)
  --text      → --fg        (label colour)

Also addresses two argus yellows from the PR #997 review:
- Skip updateTabbarOverflow() early when the dropdown is open; avoids
  DOM flicker from the 1s badge-update tick while the menu is visible.
- Fall back to 32px when overflowBtn.offsetWidth === 0 (hidden on the
  first render) so the initial width measurement is not off.
2026-06-01 19:49:43 +02:00
atlas
994f53d06f fix(#978): stage roles.json in sync_agents alongside topology.json
topology::reconcile_roles writes roles.json to the meta dir, but
sync_agents never staged it. After #978 merged, roles.json showed up
as an untracked file in the meta repo (visible in `git status`) because
it followed the same pattern as topology.json and tool-groups.json but
was missed in the git add list.

Add the same conditional stage for roles.json: git add is a no-op when
the file is unchanged or absent, matching the existing pattern.
2026-06-01 19:45:20 +02:00
iris
e9d22ef2d3 feat(#994): tabbar overflow menu — settings and logs in ⋮ by default
Add a ⋮ overflow button at the right end of the dashboard tab strip.

SETTINGS and LOGS always live in the overflow dropdown (marked
data-overflow="default" in index.html — never rendered in the main bar).

Dynamic overflow: when the bar is too narrow to fit all remaining tabs,
rightmost tabs spill into the dropdown right-to-left. Implemented via
JS measurement + ResizeObserver; no CSS-only relayout trick needed.

The ⋮ button:
- hidden when the dropdown is empty (wide screens with only the default
  tabs overflowed, which are always there anyway → button always shown)
- gets .tabbar-overflow-active when the current hash tab is inside
- dropdown items clone the tab label + count badge from the original
- closes on outside click / Escape

MutationObserver on the tabbar catches P33RS/M4TR1X hidden-attribute
changes so the overflow recalculates when those tabs are shown/hidden.
2026-06-01 19:39:17 +02:00
atlas
f56b5c5a7b fix(#991): start nginx when unit is in failed state, not just reload
nginx -s reload signals a running master process. When nginx enters
failed state (start-limit-hit from repeated nginx -t failures on a
bad agents.conf), there is no master and the reload is a silent no-op.
c0re kept re-firing the same no-op reload forever via RELOAD_PENDING.

Fix: probe the nginx unit's ActiveState before sending the reload:
- active    → nginx -s reload (existing zero-downtime path)
- failed    → systemctl reset-failed nginx + systemctl start nginx
- other     → systemctl start nginx

This makes c0re self-healing: once a corrected agents.conf is published,
the next reload_gateway_nginx call clears the start-limit and restarts
nginx automatically without operator intervention.

New helpers: nginx_active_state() (systemctl show --property=ActiveState
--value) and gateway_systemctl() (host-side systemctl --machine=hive-gateway).
2026-06-01 19:31:30 +02:00
atlas
e3b4d38565 fix(#922): fall back to m.login.password when matrix user already exists
When the token file is deleted but the homeserver account still exists,
register_user returns M_USER_IN_USE (HTTP 400) and the provisioning
sweep hard-fails, leaving the agent without a working matrix token.

Fix: persist the random password to matrix-password alongside the
access token on first registration. On subsequent attempts where
M_USER_IN_USE is returned, fall back to login_user (m.login.password)
using the stored password. If both files are gone, the error message
guides the operator to `hivectl matrix create-user <name> --password`.
2026-06-01 19:28:50 +02:00
iris
ee61eac663 fix(989): correct four live build-log viewer bugs in logs.js
- live detection: use !h.status (null while running) not === 'running'
- SSE: replace named addEventListener('chunk'/'done') with onmessage +
  frame.done check — backend sends generic message events, no named type
- SSE payload: use frame.stdout_append/stderr_append not frame.text
- timestamps: started_at/finished_at are unix seconds, multiply by 1000
  was already done implicitly — actually use unix arithmetic directly
  to avoid new Date() confusion entirely

Also move the misplaced .flow-main-slim rule from the logs section to
the flow-shell section of dashboard.css.
2026-06-01 19:20:54 +02:00
iris
0b15cad93f feat(#986): dedicated logs page with build/agent/system sub-tabs
Add /logs.html as a standalone page (same back-link pattern as flow.html):
- BUILD tab: all-agents build log history via new GET /api/build-logs endpoint
- AGENT tab: per-container journald viewer with agent selector + unit filter
- SYSTEM tab: host-side hive-c0re.service logs via new GET /api/journal-host endpoint

Remove inline log drill-ins from SW4RM container rows (buildJournalTrigger
and buildBuildLogsTrigger) — log viewing now lives on the dedicated page.

flow.html: strip the full dashboard tabbar, replace with a simple back link
matching the new logs page chrome.

index.html: add L0GS tab link to /logs.html in the tab strip.

Backend additions:
- build_logs::list_recent_all — cross-agent query (newest first, cap 100)
- GET /api/build-logs — all-agents variant backed by list_recent_all
- GET /api/journal-host — host journald (no -M container flag), restricted
  to allow-listed units (hive-c0re.service)
2026-06-01 19:20:54 +02:00
atlas
fd87cf9924 fix(#962): top_level_agents = parentless agents, not children of manager
Per mara's design: the role grants access to every agent with no parent
in the topology (parent=None), derived purely from structure. No agent
name is hardcoded. In normal operation this is just the manager; any
additional parentless agents the operator creates are also covered.

Update ROLE docstring, lifecycle.rs comment, and unit tests accordingly.
Add a multi-root test to document the behaviour with multiple parentless
agents.
2026-06-01 19:19:24 +02:00
atlas
aae4be19bd fix(#962): remove hardcoded "root" string from docstrings and tests
Replace literal "root" with MANAGER_NAME constant in role tests;
update ROLE_CAN_MANAGE_TOP_LEVEL_AGENTS docstring to say "manager"
not "root/manager agent".
2026-06-01 19:19:24 +02:00
atlas
a4e0628ba1 fix(#962): top_level_agents_in delegates to children_of(MANAGER_NAME)
The previous parent=None heuristic was wrong — the manager is not
required to be the structural topology root (it can have a parent).
Delegate to children_of_in(MANAGER_NAME) directly; topology.rs is the
right place for this knowledge. Update comment in lifecycle.rs to say
"direct child of the manager" instead of "direct child of root".
2026-06-01 19:19:24 +02:00
atlas
828be8e2c8 refactor(#962): replace children_of(MANAGER_NAME) with top_level_agents()
Add topology::top_level_agents_in(topo) and top_level_agents() which
find the topology root by structure (parent=None) rather than by name,
then return its children. Lifecycle.rs role logic now uses this instead
of children_of(MANAGER_NAME), removing the hardcoded manager-name
reference from the bind-mount logic.

Also adds two unit tests for top_level_agents_in.
2026-06-01 19:19:24 +02:00
atlas
d79df752d6 fix(#962): keep empty role entries as tombstone; add role unit tests
set_role previously removed empty entries after revoke, causing
reconcile_roles to re-seed the role on the next tick (absent key =
never seen = seed). Fix: never remove empty entries; an empty list
is a tombstone meaning "explicitly revoked".

Also adds unit tests for has_role_in, set_role revoke semantics, and
the reconcile_roles seed/no-seed distinction (pure in-memory, no disk).
2026-06-01 19:19:24 +02:00
atlas
6d2d0ed847 feat(#962): topology-driven child bind mounts + can_manage_top_level_agents role
Removes the MANAGER_NAME special-case from set_nspawn_flags in favour of
two general mechanisms:

1. Topology-driven child mounts: every agent now gets its direct children's
   state, harness, and config dirs bind-mounted (RW). Root's children are
   the top-level agents, so root gets the same access it did before via the
   old /agents blob bind — but derived from topology, not a hardcoded name
   check.

2. can_manage_top_level_agents role: agents holding this role additionally
   get every top-level agent treated as a virtual child (same RW mounts)
   plus /applied and /meta as RO. Designed for recovery: a role holder can
   update a top-level agent's config even when that agent is down.
   Root receives this role by default on first reconcile_roles call.
   Operator can revoke it with set_role.

Every agent (including root) now gets its own state/harness/config dirs via
the standard path. Roles are stored in meta/roles.json (same dir as
topology.json); reconcile_roles is called from reconcile so both files stay
in sync.
2026-06-01 19:19:24 +02:00
iris
ba43b5f869 fix(988): hide inapplicable actions in agent context menu
Per mara feedback: don't show disabled actions at all.
- restart/stop only rendered when agent is running
- start only rendered when agent is stopped
- removed .agent-menu-item:disabled CSS rule (no longer needed)

Also switch Escape handler to stopImmediatePropagation so the
selection-clear keydown listener doesn't co-fire when closing a menu.
2026-06-01 19:18:18 +02:00
iris
28e0d1b7fa feat(987): add per-agent three-dot context menu on SW4RM tab
Each agent card in the SW4RM tab now has a ⋮ button that reveals a
dropdown with single-agent lifecycle actions: R3ST4RT, ST0P, ST4RT,
R3BU1LD, DESTR0Y, PURG3. Items are state-aware (disabled when action
doesn't apply). The button is CSS-invisible until the row is hovered
or the menu is open — quiet rows stay clean. DESTR0Y and PURG3 are
hidden for the manager container.

Click-outside and Escape close the menu. A tree-rebuild (on any SSE
state update) also closes it to avoid stale DOM references.
2026-06-01 19:18:18 +02:00
iris
0162a3f108 fixup: remove manager config path mention from ManagerServer instructions
Every agent's config lives at /agents/<name>/config/agent.nix — there
is no special case for the manager. Drop the sentence.
2026-06-01 19:18:02 +02:00
iris
0dc9d66880 rename(#162): drop non-special-case agent name examples from MCP docs 2026-06-01 19:18:02 +02:00
iris
ab278affc7 rename(#162): scrub remaining hm1nd references from comments and MCP instructions
All functional renames (MANAGER_NAME, MANAGER_AGENT → "root") were done in
earlier commits. This cleans up the stale `hm1nd` strings that remained in
doc comments, test fixture labels, and the manager MCP server instructions
(which told the manager its config lived at /agents/hm1nd/config/agent.nix).
2026-06-01 19:18:02 +02:00
müde
f9de183f4e fix: strip /agent/<name>/ via rewrite in named location
nginx forbids a URI part on proxy_pass inside named locations, so the
split-mode `@<name>_dynamic` fallback (introduced when the gateway
started serving static dist directly) failed config-test with:

  "proxy_pass" cannot have URI part in location given by regular
  expression, or inside named location

Render two upstream forms: prefix locations keep the trailing slash so
nginx auto-strips the location prefix; the named-location path strips
`/agent/<name>/` via `rewrite ... break` and uses a bare upstream.
2026-06-01 19:10:38 +02:00
atlas
fe62db8917 fix(#981): enable Forgejo Actions so runner registration token API works
Without [actions] ENABLED = true in forgejo's config, the
GET /api/v1/admin/runners/registration-token endpoint returns
"runner registration token not found" regardless of token scopes,
causing hive-ci-register.service to fail on first boot.
2026-06-01 18:38:44 +02:00
damocles
4d934748f7 fix(#977): abort phase 5 if stop fails; clean up old root.conf after rename 2026-06-01 18:32:19 +02:00
damocles
68cc433ac9 fix(#977): add MANAGER_CONTAINER=h-root, migrate root container name 2026-06-01 18:32:19 +02:00
atlas
286a7c8fd3 fix(#975): use hyperhive.local email domain to pass Forgejo validation
`@hyperhive` (no dot) is rejected by Forgejo's PATCH /admin/users API
with 422 Unprocessable Entity. Switch to `@hyperhive.local` across all
four files that construct hive-c0re/agent git emails and Forgejo account
emails, so both stay in sync and pass RFC-valid domain validation.
2026-06-01 18:28:22 +02:00
damocles
b83edc40c6 fix(#970,#973): retry gateway reload on failure; always enable HIVE_WEB_SOCKET 2026-06-01 18:18:45 +02:00
atlas
5cf18ad7c7 fix(#971): remove duplicate BindMount re-export in priv_client
BindMount was imported at line 10 via the priv_proto glob-style import,
then re-exported again with `pub use` at line 69. E0252: two definitions
of BindMount in the same type namespace. Remove the redundant pub use —
callers that need BindMount should import from hive_sh4re::priv_proto.
2026-06-01 18:05:43 +02:00
damocles
9a7c917f36 rename(#162): fix MANAGER_AGENT and @manager in docs 2026-06-01 17:58:09 +02:00
atlas
2d754eceea fix: add listenfd to Cargo.lock
listenfd was declared as a direct dep in hive-c0re/Cargo.toml but was
never resolved into Cargo.lock. The nix crane build vendors deps from
the lock, so the missing entry caused a build failure.
2026-06-01 17:55:40 +02:00
iris
f39015e41d fix(961): remove stale manager/hm1nd references from dashboard tabs.js
Three post-rename artifacts:

1. journalUnit: was 'hive-m1nd.service' for the manager branch.
   Both roles use 'hive-ag3nt.service' since the harness unification —
   always use 'hive-ag3nt.service'.

2. buildTargetChips: hardcoded ['operator', 'manager'] candidates +
   filter by n !== 'manager'. After the rename the manager's c.name
   is 'root', so it was excluded from the filter but not from the
   hardcoded initial list — the manager could appear twice or be listed
   as 'manager' when its real broker name is 'root'. Fix: derive
   the manager's name from c.is_manager on containersState; filter
   sub-agents by !c.is_manager rather than by hardcoded string.

3. schedulesTableAgentSet: same hardcoded 'manager' + filter pattern
   as buildTargetChips. Same fix applied.
2026-06-01 17:55:15 +02:00
damocles
68fd7e08a5 fix(#948): pre-enqueue topology-sorted cascades on meta-update approval 2026-06-01 17:55:01 +02:00
damocles
11d74e1e17 fix(#947): include manager in gateway agents.conf routing 2026-06-01 17:46:10 +02:00
damocles
aa96d76e73 build-logs: extract compute_runtime_secs helper; clamp runtime in js 2026-06-01 17:42:35 +02:00
damocles
008609d0e7 rename(162): drop HIVE_PORT fallback; sweep remaining hm1nd in docs 2026-06-01 17:40:47 +02:00
damocles
26c9c13cf5 build-logs: surface runtime_secs in header; show duration in dashboard row 2026-06-01 17:36:29 +02:00
iris
e99330ef0f docs(gateway): document per-agent static frontend split mode 2026-06-01 17:35:28 +02:00
iris
e162c1a1fa feat(#955): split agent page serving — statics from nix store, API proxied
gateway_nginx.rs reads HIVE_AGENT_FRONTEND_DIR (injected by hive-c0re.nix
as ${cfg.frontend}/agent). When set, agents.conf emits per-agent split
blocks instead of the old single proxy_pass:

  # Compiled assets — immutable nix store path, cache 1y
  location ^~ /agent/<name>/static/ {
      alias <frontend>/static/;
      expires 1y; add_header Cache-Control "public, immutable, ...";
  }
  # Static dist + proxy fallback
  location /agent/<name>/ {
      alias <frontend>/;
      try_files $uri $uri.html $uri/index.html @<name>_dynamic;
  }
  location @<name>_dynamic {
      proxy_pass <upstream>;   # api, events, icon, login, …
      …proxy headers unchanged…
  }

try_files path resolution (nginx applies alias mapping first):
  $uri           — exact file (/static/app.js → static/app.js)
  $uri.html      — bare-path fallback (/stats → stats.html)
  $uri/index.html — directory index (/ → index.html)
  @<name>_dynamic — proxy catchall for anything not in the dist

Adding pages to the frontend dist works automatically — no generator
change needed. Per-agent extraFiles (in mergedDist, not in the base
nix-store path) continue to proxy to the agent daemon.

frontend is a nix store path injected at build time — only [a-z0-9/._-],
no shell metacharacters — safe to interpolate without sanitization;
comment added documenting this assumption.

Without HIVE_AGENT_FRONTEND_DIR the existing single-proxy block is
emitted unchanged — backward-compatible for deployments without the env.

render() takes frontend_dir as a parameter so tests exercise both code
paths safely in parallel. 13 tests: 7 legacy, 6 split-mode. No clippy
warnings in changed files.

nix/modules/hive-c0re.nix: inject HIVE_AGENT_FRONTEND_DIR = "${cfg.frontend}/agent".
2026-06-01 17:35:28 +02:00
atlas
84f5c2722e nix(#702): add hive-priv systemd socket + service units
Nix side of the hive-priv phase 1 work. hive-priv is socket-activated
exclusively: systemd holds /run/hive/priv.sock (mode 0660) and starts
the service on first connection. LISTEN_FDS + LISTEN_PID are passed;
hive-priv reads them in socket_listener() to accept the pre-bound fd.

Phase 2 note (comment in file): when hive-c0re drops to a non-root
user, add SocketGroup = hive-core to the socket unit so the
unprivileged caller can still connect. No code change needed in
hive-priv itself.
2026-06-01 17:33:32 +02:00
damocles
e7e0675d22 rename: role badge stays 'manager'; unify harness unit name to hive-ag3nt 2026-06-01 17:33:09 +02:00
damocles
77622c0585 rename(162): update manager recipient to root in system.md 2026-06-01 17:33:09 +02:00
damocles
829cea1a26 rename(162): nixosConfigurations/modules manager -> root, update package name 2026-06-01 17:33:09 +02:00
damocles
1d0d52b85f rename(162): update manager.nix comments for root 2026-06-01 17:33:09 +02:00
damocles
7772e29ec2 rename(162): hive-m1nd -> hive-root, HIVE_LABEL/PORT for root 2026-06-01 17:33:09 +02:00
damocles
1b4e9d0e2b rename(162): nixosConfigurations.manager -> root, hive-m1nd -> hive-root 2026-06-01 17:33:09 +02:00