hive_sh4re::Request uses #[serde(tag = "cmd")] so Wake serialises as
{"cmd":"wake",...}. The wake.rs in hive-matrix-mcp was sending
{"kind":"wake",...} — the harness could not deserialise the message
and silently discarded every incoming matrix event.
Also: drain the server's response line instead of just shutting the
write half. Without the drain the server got ECONNRESET writing back,
which logged a spurious error even though the wake itself was now
processed.
Root cause of the matrix notification blackhole reported in #1087.
add QueueKind::PermChange — dashboard tool-group and capability
handlers no longer write the shared JSON files inline. instead they
enqueue a PermChange entry; the FIFO worker applies the file write
then calls rebuild_agent so the updated env var takes effect.
concurrent batch-apply actions for different agents previously raced
on tool-groups.json / capabilities.json (last write wins, earlier
change silently dropped). serialising through the queue prevents this.
dedup check extended with perm-type discriminant so tool-groups and
capabilities changes for the same agent are kept as distinct entries
and never collapse into one slot.
- hive-sh4re: ToolGroup::description() and Capability::description() return
short human-readable strings for each variant
- hive-c0re: ToolGroupsSnapshot and CapabilitiesSnapshot now include a
`descriptions` map (name → description); get_capabilities now iterates
Capability::ALL instead of hardcoding the list
- tabs.js: renderToolGroups + renderCapabilities use descriptions[name] as
the column header title attribute (native browser tooltip on hover)
Argus flagged that since PR #1054 the nginx reload path is
`gateway_systemctl(["reload", "nginx"])` = `systemctl -M hive-gateway reload nginx`.
Replace the stale `systemd-run --machine=hive-gateway` reference.
Four modules were absent: capabilities.rs, build_logs.rs,
gateway_nginx.rs, priv_client.rs. Added at their natural
positions alongside related modules.
Remove web_tools from the C4P4B1L1T13S table in dashboard.md and add
it to the T00L GR0UPS section with a note that it gates Claude built-ins
rather than MCP tools. Fix turn-loop.md to say 'tool-group-gated' and
'web_tools tool group' throughout.
The hardcoded vec was a maintenance hazard — any new capability added
to Capability::ALL would silently be omitted from the permissions UI
column list until get_capabilities was manually updated.
Now both the GET and POST handlers derive their known-capability lists
from the same Capability::ALL source of truth.
Capabilities were added with the permissions tab but conventions.md only
documented tool groups. Adds a full Capabilities section covering: the
known capabilities table, config storage path, HIVE_CAPABILITIES env var
injection, runtime resolution, operator-only grant constraint, and the
pattern for adding a new capability.
/logs.html was undocumented — only mentioned in passing in API
endpoint lists. Adds a proper section covering BUILD / AGENT /
SYSTEM sub-tabs, their API endpoints, and behaviour.
The meta/ section only described the flake. Added entries for
topology.json, tool-groups.json, and capabilities.json — the three
system-level config files also committed there — with their writers,
readers, and injected env vars.
The deny list takes precedence over --allowedTools, so granting the
web_tools group had no effect — both tools were silently blocked.
WebFetch/WebSearch are now gated solely via --tools (builtin_tools_arg):
agents without the web_tools tool group don't get them in --tools so
claude never sees them; agents with the group get them in both --tools
and --allowedTools.
Replaces the earlier capability-based approach (closed#1069) with a
ToolGroup — capabilities are for privileged system access, web egress is
a tool permission.
Add ToolGroup::WebTools to hive-sh4re:
- tools() returns &[] (no MCP tools gated)
- builtin_tools() returns &["WebFetch", "WebSearch"] — new method on ToolGroup
- Present in ALL and as_str() → "web_tools"
In hive-ag3nt/mcp.rs:
- allowed_tools_arg() now iterates group.builtin_tools() to prepend any
group-gated built-ins alongside the base ALLOWED_BUILTIN_TOOLS set
- builtin_tools_arg_for_flavor(flavor) replaces builtin_tools_arg() so
the flavor-correct effective groups are used when building --tools
- builtin_tools_arg() kept as a flavor=Agent convenience alias
- turn.rs updated to call builtin_tools_arg_for_flavor(files.flavor) so
manager sessions also see web tools when web_tools is in their groups
The dashboard T00L GR0UPS table gains a web_tools column automatically
(ToolGroup::ALL drives the columns).
Tuwunel 1.6.1 appends " 💕" to every new user's display name via the
new_user_displayname_suffix config option. Set it to empty string so
agent and operator matrix accounts get clean names.
Existing users (atlas, mara, root) need a one-time display name reset.
Atlas's was already corrected via the client API.
stats.html had a <style> block with all its page-specific CSS inline.
Move to agent.css under a '--- /stats page ---' section, renaming
the generic .grid/.card/.empty-note classes to .stats-grid/.stats-card/
.stats-empty-note to avoid future collisions with other agent pages.
- rename cap-cap-col → cap-col; add CSS rules for .cap-col and .cap-save-col
- drop zero-width-space replace in capability header cells (nowrap makes it a no-op)
- add Capability::ALL to hive-sh4re; validate incoming cap strings in post_capabilities
Add a new P3RM1SS10NS tab to the dashboard that consolidates all
per-agent permission configuration:
Backend:
- GET /api/capabilities returns { caps: [...], assignments: {...} }
driven by Capability::ALL variants (manage_root_agent,
read_host_journal, query_agent_state)
- POST /api/capabilities/{agent} writes capabilities.json and queues
a rebuild so HIVE_CAPABILITIES takes effect
Frontend:
- New 'permissions' entry in TABS, placed after 'system'
- P3RM1SS10NS tab pane with two sections:
C4P4B1L1T13S — agents × capabilities checkbox matrix (.cap-*)
T00L GR0UPS — agents × tool-groups checkbox matrix (.tg-*) moved
from SYST3M tab
- activateTab('permissions') fetches both tables; neither has an SSE
channel so they re-fetch on each activation to stay fresh
- CSS for .cap-* mirrors the .tg-* layout (scrollable, Catppuccin)
hive-ci-register.service now runs unconditionally on every boot (not
just when .runner is absent). Before fetching a registration token it
validates existing .runner credentials via the forge admin API:
- 200: runner still registered, write dummy token and exit
- 404: runner deleted from forge, purge .runner and re-register
- 000: forge unreachable, keep credentials (runner surfaces the error)
- other non-200 or malformed .runner: purge and re-register
Removes ConditionPathExists so stale credentials from a wiped forge
no longer block the runner indefinitely. Updates docs/ci.md to match.
Remove stale htpasswdFile option from nix example (option no longer
exists). Update hivectl command examples to drop --file flag (now
optional with standard default). Add description of the fixed path
and how it's exposed inside the container.
Remove the custom htpasswdFile option and bind-mount. The htpasswd file
now lives at the fixed path /var/lib/hyperhive/gateway/gateway.htpasswd
on the host, which is already exposed inside the container at
/run/hive-state/gateway.htpasswd via the existing gateway state
bind-mount — no extra bind-mount needed.
A tmpfiles rule pre-creates the file so nginx can open it even before
any users exist (empty file → all requests return 401, which is correct).
hivectl gateway commands default --file to the standard path so
`hivectl gateway create-user alice` just works without any flags.
Per argus review: the hardcoded /etc/hyperhive/gateway.htpasswd
example was wrong for operators with a custom htpasswdFile path.
Move the unauthorized.html from the static agentErrorPagesDir derivation
into a pkgs.writeText inside the lib.optionalAttrs guard where
cfg.auth.htpasswdFile is in scope and statically known non-null.
The rendered page now shows the operator's actual configured path.
When HTTP Basic auth is enabled and credentials are absent or rejected,
nginx serves a Catppuccin-styled 401 page that tells the operator which
hivectl command to run to create a user. Uses error_page 401 =401 so
the browser still receives a 401 status (login dialog fires on first
visit) while getting a human-readable body when the dialog is dismissed.
The exact-match location (= /__hive_auth_unauthorized) beats location /
in nginx's prefix ordering so the internal subrequest does not loop back
through auth_basic.
- common.js fetchStateFile: 'HTTP' -> 'http' — last uppercase instance
in the frontend (tabs.js, app.js, logs.js already fixed in prior PRs)
- tabs.js: two redundant arrow wrappers in setTimeout dropped —
setTimeout(() => f(), N) -> setTimeout(f, N) where f takes no args
Per mara's direction: both nixpkgs and nixpkgs-unstable are now
top-level meta flake inputs with explicit store-path URLs. Hyperhive
follows them rather than the other way around:
inputs.nixpkgs.url = "path:${pkgs.path}";
inputs.nixpkgs-unstable.url = "path:${nixpkgs-unstable}";
inputs.hyperhive.url = "...";
inputs.hyperhive.inputs.nixpkgs.follows = "nixpkgs";
inputs.hyperhive.inputs.nixpkgs-unstable.follows = "nixpkgs-unstable";
New NixOS host options (auto-set at build time, overridable):
services.hyperhive.c0re.nixpkgsFlake
default: "path:${pkgs.path}" — host's evaluated nixpkgs.
services.hyperhive.c0re.nixpkgsUnstableFlake
default: "path:${nixpkgs-unstable}" from hyperhive's flake.nix —
the channel that carries claude-code. Operators can override to
track a different unstable snapshot.
Legacy fallback (both args empty) preserved for backward compat.
Two new Rust tests cover the full-URL and fallback paths.
meta flake was using `nixpkgs.follows = "hyperhive/nixpkgs"` but
`hyperhive` is a store-path input, so nix resolves hyperhive's own
pinned lock rather than the host's follows-substituted version.
When an operator sets `inputs.hyperhive.inputs.nixpkgs.follows =
"nixpkgs"` in their host flake, the meta flake was silently ignoring
it and using hyperhive's pinned nixpkgs instead.
Fix: hive-c0re.nix injects `--nixpkgs-flake path:${pkgs.path}` into
the daemon's ExecStart. `pkgs` IS the host's nixpkgs when follows is
set; otherwise it's hyperhive's own pin — so the meta flake gets the
right nixpkgs in both cases. render_flake emits `nixpkgs.url = "..."`
(explicit) when nixpkgs_flake is non-empty, falling back to the old
`follows` form when empty for backward compat.
Add `hivectl gateway {create-user,delete-user,list-users}` subcommands for
managing htpasswd files used by gateway Basic auth. Pure Rust bcrypt
(cost 12, $2y$ prefix nginx accepts). No external htpasswd binary required.
Also fix the NixOS module assertion: `cfg.auth ? htpasswdFile` is always
true in the module system (declared options always exist as keys); switch
to `nullOr path; default = null` + `!= null` check so the assertion
actually fires with a useful error when enable=true but no file is set.
Guard bind-mount and nginx config against null to prevent eval errors.
Update docs/gateway.md to show hivectl commands instead of raw htpasswd.