per-agent send allow-list via hyperhive.allowedRecipients

new NixOS option in harness-base.nix:
  hyperhive.allowedRecipients = [ 'alice' 'manager' ];  # whitelist
  hyperhive.allowedRecipients = [ ];                    # default = unrestricted

module writes the list as JSON to /etc/hyperhive/send-allow
.json at activation. AgentServer::send reads the file before
issuing the broker request; if the list is non-empty and
`to` isn't on it, the tool returns a claude-readable refusal
string without touching the broker. the manager is always
implicitly permitted regardless of the list — otherwise a
misconfigured allow-list could strand a sub-agent without an
escalation path.

enforcement is in the in-container MCP server (not on the
host's per-agent socket) because the agent's nix config is the
trust boundary anyway — the operator audits agent.nix at
deploy time, the activation-time /etc/hyperhive/send-allow
.json is r/o under /nix/store, so the agent can't tamper at
runtime without going through a new approval.

agent prompt mentions the option + tells claude to route
through the manager when refused. retires the matching TODO
under Permissions / policy.
This commit is contained in:
müde 2026-05-16 03:59:28 +02:00
parent d1c69b134a
commit 67e4242b9f
4 changed files with 78 additions and 12 deletions

View file

@ -5,6 +5,29 @@
# this. The systemd service that actually runs the harness binary
# differs per role and lives in the child module.
options.hyperhive.allowedRecipients = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
example = [ "alice" "manager" ];
description = ''
Names this agent is allowed to `send` to via
`mcp__hyperhive__send`. Empty list (the default) means
unrestricted the agent can message any peer, the
operator, or the manager. Non-empty list constrains the
surface: only the listed names + the manager (always
allowed) get through; anything else returns an error
string to claude without touching the broker. The
operator (`operator`) needs to be in the list if the
agent should be able to surface output on the
dashboard.
Useful for sandboxing untrusted sub-agents set
`[ "manager" ]` to scope them to manager-only chatter.
The manager itself is always exempt; this option only
affects sub-agent `send`.
'';
};
options.hyperhive.extraMcpServers = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule {
options = {
@ -63,6 +86,9 @@
environment.etc."hyperhive/extra-mcp.json".text =
builtins.toJSON config.hyperhive.extraMcpServers;
environment.etc."hyperhive/send-allow.json".text =
builtins.toJSON config.hyperhive.allowedRecipients;
boot.isNspawnContainer = true;
# `claude-code` is unfree. Each per-agent container's nixosConfiguration