Compare commits

..
11 changed files with 58 additions and 46 deletions

View file

@ -139,7 +139,7 @@
inherit cargoArtifacts nativeBuildInputs; inherit cargoArtifacts nativeBuildInputs;
pname = "hyperhive-workspace"; pname = "hyperhive-workspace";
version = "0.1.0"; version = "0.1.0";
meta.description = "hyperhive workspace (hive-c0re, hive-ag3nt, hive-root)"; meta.description = "hyperhive workspace (hive-c0re, hive-ag3nt, hive-m1nd)";
doCheck = false; doCheck = false;
}; };
# Bundled browser assets — see ./nix/frontend.nix. Output is # Bundled browser assets — see ./nix/frontend.nix. Output is
@ -173,7 +173,7 @@
# they're plain derivations, but `nix build` from a non-x86 # they're plain derivations, but `nix build` from a non-x86
# host would only succeed via a remote x86 builder. # host would only succeed via a remote x86 builder.
agent-base-toplevel = self.nixosConfigurations.agent-base.config.system.build.toplevel; agent-base-toplevel = self.nixosConfigurations.agent-base.config.system.build.toplevel;
root-toplevel = self.nixosConfigurations.root.config.system.build.toplevel; manager-toplevel = self.nixosConfigurations.manager.config.system.build.toplevel;
# Auto-generated nix options reference for hyperhive (#616). # Auto-generated nix options reference for hyperhive (#616).
# `docs` bundles host + agent pages into one tree; the split # `docs` bundles host + agent pages into one tree; the split
@ -223,7 +223,7 @@
nixosModules = { nixosModules = {
agent-base = ./nix/templates/agent-base.nix; agent-base = ./nix/templates/agent-base.nix;
root = ./nix/templates/manager.nix; manager = ./nix/templates/manager.nix;
# The hive-c0re module wants `pkgs.hyperhive` for its default # The hive-c0re module wants `pkgs.hyperhive` for its default
# `services.hyperhive.c0re.package`. To avoid making operators apply an # `services.hyperhive.c0re.package`. To avoid making operators apply an
# overlay (which would also pollute their host pkgs with our # overlay (which would also pollute their host pkgs with our
@ -244,7 +244,7 @@
# extra deps gated so aarch64 hosts don't accidentally pull # extra deps gated so aarch64 hosts don't accidentally pull
# them in via cross-build. # them in via cross-build.
agentBaseToplevel = self.packages.x86_64-linux.agent-base-toplevel; agentBaseToplevel = self.packages.x86_64-linux.agent-base-toplevel;
managerToplevel = self.packages.x86_64-linux.root-toplevel; managerToplevel = self.packages.x86_64-linux.manager-toplevel;
}; };
hive-ci = ./nix/modules/hive-ci.nix; hive-ci = ./nix/modules/hive-ci.nix;
hive-forge = ./nix/modules/hive-forge.nix; hive-forge = ./nix/modules/hive-forge.nix;
@ -277,7 +277,7 @@
in in
{ {
agent-base = mkContainer self.nixosModules.agent-base; agent-base = mkContainer self.nixosModules.agent-base;
root = mkContainer self.nixosModules.root; manager = mkContainer self.nixosModules.manager;
}; };
devShells = forAllSystems ( devShells = forAllSystems (

View file

@ -8,7 +8,7 @@ You are the hyperhive manager `{label}` (qualified: `{qualified_label}`){hive_id
Tools (hyperhive surface): Tools (hyperhive surface):
- `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. `max` (default 1, cap 32) drains several queued messages in one call — the wake prompt tells you the pending count. - `mcp__hyperhive__recv(wait_seconds?, max?)` — drain inbox messages (returns `(empty)` if nothing pending). Without `wait_seconds` (or with `0`) it returns immediately — a cheap "anything pending?" peek you can sprinkle between tool calls. To **wait** for work when you have nothing else useful to do this turn, call with a long wait (e.g. `wait_seconds: 180`, the max) — incoming messages wake you instantly, otherwise the call returns empty at the timeout. That's strictly better than a fixed `sleep` shell command: lower latency on new work, no busy-loop. `max` (default 1, cap 32) drains several queued messages in one call — the wake prompt tells you the pending count.
- `mcp__hyperhive__send(to, body, in_reply_to?)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Use `to: "<parent>"` to address your structural parent without hardcoding their name — hive-c0re rewrites it at delivery time per `topology.json`, falling back to `operator` if you're a root agent. Use `to: "<children>"` to fan-out to every direct child of yours per `topology.json` (no-op for leaf agents). Both sentinels let the operator reparent at runtime with zero change on your side. Optional `in_reply_to: <message-id>` threads this message under a prior one — the dashboard and per-agent inbox render it with a `↳ reply` link. Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "root", …)`) which is always reachable. - `mcp__hyperhive__send(to, body, in_reply_to?)` — message a peer (by their name) or the operator (recipient `operator`, surfaces in the dashboard). Use `to: "*"` to broadcast to all agents (they receive a hint that it's a broadcast and may not need action). Use `to: "<parent>"` to address your structural parent without hardcoding their name — hive-c0re rewrites it at delivery time per `topology.json`, falling back to `operator` if you're a root agent. Use `to: "<children>"` to fan-out to every direct child of yours per `topology.json` (no-op for leaf agents). Both sentinels let the operator reparent at runtime with zero change on your side. Optional `in_reply_to: <message-id>` threads this message under a prior one — the dashboard and per-agent inbox render it with a `↳ reply` link. Some agents have a per-agent allow-list (`hyperhive.allowedRecipients` in their `agent.nix`) — if so the tool refuses recipients outside the list with a clear error; route through the manager (`send(to: "manager", …)`) which is always reachable.
<!-- role:agent --> <!-- role:agent -->
- (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time. - (some agents only) **extra MCP tools** surfaced as `mcp__<server>__<tool>` — these are agent-specific (matrix client, scraper, db connector, etc.) declared in your `agent.nix` under `hyperhive.extraMcpServers`. Treat them as first-class tools alongside the hyperhive surface; the operator already auto-approved them at deploy time.
<!-- /role:agent --> <!-- /role:agent -->
@ -43,7 +43,7 @@ Tools (hyperhive surface):
<!-- role:agent --> <!-- role:agent -->
- `mcp__hyperhive__request_next_turn()` — ask the harness to start another turn immediately after this one ends, even if the inbox is empty. Use for multi-turn tasks (long builds, sequential steps) where you want to continue without waiting for an external message. The next turn starts with `from: "self"` and `body: "continue"`. No-op if new inbox messages arrive before this turn ends (the harness already loops immediately on pending messages). No args. - `mcp__hyperhive__request_next_turn()` — ask the harness to start another turn immediately after this one ends, even if the inbox is empty. Use for multi-turn tasks (long builds, sequential steps) where you want to continue without waiting for an external message. The next turn starts with `from: "self"` and `body: "continue"`. No-op if new inbox messages arrive before this turn ends (the harness already loops immediately on pending messages). No args.
Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `root`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config. Need new packages, env vars, or other NixOS config for yourself? You can't edit your own config directly — message the manager (recipient `manager`) describing what you need + why. The manager evaluates the request (it doesn't rubber-stamp), edits `/agents/{label}/config/agent.nix` on your behalf, commits, and submits an approval that the operator can accept on the dashboard; on approve hive-c0re rebuilds your container with the new config.
Your config repo is mounted **read-only** at `/agents/{label}/config/``agent.nix` plus whatever extra files the manager has split the config into. Read it to see exactly what defines you (declared packages, env vars, MCP servers) before asking the manager for a change, so you can point at the precise file and line. You cannot write here; all changes flow through the manager. Your config repo is mounted **read-only** at `/agents/{label}/config/``agent.nix` plus whatever extra files the manager has split the config into. Read it to see exactly what defines you (declared packages, env vars, MCP servers) before asking the manager for a change, so you can point at the precise file and line. You cannot write here; all changes flow through the manager.
<!-- /role:agent --> <!-- /role:agent -->

View file

@ -154,8 +154,8 @@ pub async fn rebuild_agent(
} }
/// Auto-create the manager container on startup if it isn't already there. /// Auto-create the manager container on startup if it isn't already there.
/// hive-c0re manages `root` end-to-end: operators no /// hive-c0re manages hm1nd end-to-end (Phase 8 follow-up): operators no
/// longer declare `containers.root` in their host NixOS config. Bypasses /// longer declare `containers.hm1nd` in their host NixOS config. Bypasses
/// the approval queue — manager is required infrastructure. Idempotent. /// the approval queue — manager is required infrastructure. Idempotent.
pub async fn ensure_manager(coord: &Arc<Coordinator>) -> Result<()> { pub async fn ensure_manager(coord: &Arc<Coordinator>) -> Result<()> {
let existing = lifecycle::list().await.unwrap_or_default(); let existing = lifecycle::list().await.unwrap_or_default();

View file

@ -1157,8 +1157,9 @@ async fn get_journal(
]) ])
.arg(lines.to_string()); .arg(lines.to_string());
if let Some(u) = q.unit.as_deref().filter(|s| !s.is_empty()) { if let Some(u) = q.unit.as_deref().filter(|s| !s.is_empty()) {
// accept hive-ag3nt[.service] — anything else refused. // accept hive-ag3nt[.service] / hive-m1nd[.service] — anything
let allowed = ["hive-ag3nt.service"]; // else we refuse, again to keep the shell-out tight.
let allowed = ["hive-ag3nt.service", "hive-m1nd.service"];
let unit = if u.ends_with(".service") { let unit = if u.ends_with(".service") {
u.to_owned() u.to_owned()
} else { } else {

View file

@ -11,10 +11,10 @@ use tokio::process::Command;
pub const AGENT_PREFIX: &str = "h-"; pub const AGENT_PREFIX: &str = "h-";
pub const MAX_AGENT_NAME: usize = 9; pub const MAX_AGENT_NAME: usize = 9;
/// Container name of the manager. Lives in the same path scheme as sub-agents /// Container name of the manager. Lives in the same path scheme as sub-agents
/// (`/var/lib/hyperhive/agents/root/`, `/var/lib/hyperhive/applied/root/`), /// (`/var/lib/hyperhive/agents/hm1nd/`, `/var/lib/hyperhive/applied/hm1nd/`),
/// but its container has no `h-` prefix and extends a different /// but its container has no `h-` prefix and extends a different
/// nixosConfiguration (`root`, not `agent-base`). /// nixosConfiguration (`manager`, not `agent-base`).
pub const MANAGER_NAME: &str = "root"; pub const MANAGER_NAME: &str = "hm1nd";
/// Mount point of the per-agent runtime directory inside the container. /// Mount point of the per-agent runtime directory inside the container.
pub const CONTAINER_RUNTIME_MOUNT: &str = "/run/hive"; pub const CONTAINER_RUNTIME_MOUNT: &str = "/run/hive";
@ -23,7 +23,7 @@ pub const CONTAINER_RUNTIME_MOUNT: &str = "/run/hive";
/// container. The harness service runs as a non-root unix user /// container. The harness service runs as a non-root unix user
/// whose home is `/home/<agent>/`, so the mount path varies per /// whose home is `/home/<agent>/`, so the mount path varies per
/// agent — `container_claude_mount(name)` returns /// agent — `container_claude_mount(name)` returns
/// `/home/<name>/.claude` for sub-agents and `/home/root/.claude` /// `/home/<name>/.claude` for sub-agents and `/home/hm1nd/.claude`
/// for the manager. `claude` inside the container reads /// for the manager. `claude` inside the container reads
/// `$HOME/.claude` and the service environment sets `HOME` to the /// `$HOME/.claude` and the service environment sets `HOME` to the
/// same path, so the OAuth session survives container restarts. /// same path, so the OAuth session survives container restarts.
@ -1035,7 +1035,7 @@ async fn systemd_daemon_reload() -> Result<()> {
/// `systemd-nspawn` command. /// `systemd-nspawn` command.
/// Where in the container's filesystem the manager sees its agents tree. /// Where in the container's filesystem the manager sees its agents tree.
/// Matches the `/agents` path that pre-Phase-8 hosts declared via /// Matches the `/agents` path that pre-Phase-8 hosts declared via
/// `containers.root.bindMounts."/agents"`. /// `containers.hm1nd.bindMounts."/agents"`.
pub const CONTAINER_MANAGER_AGENTS_MOUNT: &str = "/agents"; pub const CONTAINER_MANAGER_AGENTS_MOUNT: &str = "/agents";
/// Where the manager sees the applied trees of every agent, read-only. /// Where the manager sees the applied trees of every agent, read-only.
@ -1252,7 +1252,7 @@ async fn run(args: &[&str]) -> Result<()> {
// Convention: `nixos-container <verb> <container> ...` — the // Convention: `nixos-container <verb> <container> ...` — the
// verb is `args[0]` (kind) and the container is `args[1]` // verb is `args[0]` (kind) and the container is `args[1]`
// (h-<name> | root | hive-matrix | ...) for every long-running // (h-<name> | hm1nd | hive-matrix | ...) for every long-running
// case we care about. Strip the `h-` prefix for sub-agents so the // case we care about. Strip the `h-` prefix for sub-agents so the
// build_logs row's `agent` column matches the agent's bare name // build_logs row's `agent` column matches the agent's bare name
// (`alice` rather than `h-alice`) — that's what the dashboard // (`alice` rather than `h-alice`) — that's what the dashboard

View file

@ -23,7 +23,7 @@ pub fn start(coord: Arc<Coordinator>) -> Result<()> {
} }
let listener = UnixListener::bind(&socket) let listener = UnixListener::bind(&socket)
.with_context(|| format!("bind manager socket {}", socket.display()))?; .with_context(|| format!("bind manager socket {}", socket.display()))?;
// 0666 so the in-container root user (non-root) can connect; // 0666 so the in-container hm1nd user (non-root) can connect;
// the bind source dir is manager-only on host. See agent_server.rs. // the bind source dir is manager-only on host. See agent_server.rs.
use std::os::unix::fs::PermissionsExt as _; use std::os::unix::fs::PermissionsExt as _;
std::fs::set_permissions(&socket, std::fs::Permissions::from_mode(0o666)) std::fs::set_permissions(&socket, std::fs::Permissions::from_mode(0o666))
@ -404,9 +404,11 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
let n = lines.unwrap_or(50); let n = lines.unwrap_or(50);
// `journalctl -M` wants the *machine* name, not the // `journalctl -M` wants the *machine* name, not the
// logical agent name: `gui` → `h-gui`. `container_name` // logical agent name: `gui` → `h-gui`. `container_name`
// does that and passes the manager name through unprefixed. // does that and passes `hm1nd` through unprefixed — but
// The explicit check here keeps parity with the MANAGER_AGENT // it doesn't know the broker-logical manager name
// constant so the two never diverge. // `"manager"` (it'd wrongly produce `h-manager`), so
// handle that alias explicitly. Either manager spelling
// resolves to the unprefixed `hm1nd` machine.
let machine = if agent == MANAGER_AGENT { let machine = if agent == MANAGER_AGENT {
crate::lifecycle::MANAGER_NAME.to_owned() crate::lifecycle::MANAGER_NAME.to_owned()
} else { } else {

View file

@ -448,10 +448,10 @@ where
out.push_str( out.push_str(
r#" let r#" let
base = if isManager base = if isManager
then hyperhive.nixosConfigurations.root then hyperhive.nixosConfigurations.manager
else hyperhive.nixosConfigurations.agent-base; else hyperhive.nixosConfigurations.agent-base;
input = inputs."agent-${name}"; input = inputs."agent-${name}";
service = "hive-ag3nt"; service = if isManager then "hive-m1nd" else "hive-ag3nt";
parentEnv = if parent == null then {} else { HIVE_PARENT = parent; }; parentEnv = if parent == null then {} else { HIVE_PARENT = parent; };
toolGroupsEnv = if toolGroups == null then {} else { HIVE_TOOL_GROUPS = toolGroups; }; toolGroupsEnv = if toolGroups == null then {} else { HIVE_TOOL_GROUPS = toolGroups; };
in in
@ -461,7 +461,7 @@ where
{ {
# The harness service inside the container runs as a # The harness service inside the container runs as a
# non-root unix user named after the agent (`damocles`, # non-root unix user named after the agent (`damocles`,
# `iris`, `root`, ). UID auto-assigned by NixOS; the # `iris`, `hm1nd`, ). UID auto-assigned by NixOS; the
# per-agent override here is what makes # per-agent override here is what makes
# `hyperhive.user.name` match the agent's identity # `hyperhive.user.name` match the agent's identity
# instead of the harness-base default of `"agent"`. # instead of the harness-base default of `"agent"`.

View file

@ -249,14 +249,21 @@ mod tests {
#[test] #[test]
fn manager_uses_container_name_prefix() { fn manager_uses_container_name_prefix() {
// Manager's container view of its state is at `/agents/root/state/`. // Manager's container view of its state is at
assert_eq!(container_state_prefix("root"), "/agents/root/state/"); // `/agents/<MANAGER_NAME>/state/` (= `/agents/hm1nd/state/`),
let p = resolve_host_path("root", "/agents/root/state/reminders/x.md").unwrap(); // same as every other agent.
assert_eq!(container_state_prefix("manager"), "/agents/hm1nd/state/");
let p = resolve_host_path("manager", "/agents/hm1nd/state/reminders/x.md").unwrap();
// NB: the host path still resolves under `agents/manager/`
// (Coordinator::agent_notes_dir takes the broker LOGICAL name).
// That's a pre-existing manager-logical-vs-container-name
// discrepancy tracked separately; out of scope here.
assert_eq!( assert_eq!(
p, p,
PathBuf::from("/var/lib/hyperhive/agents/root/state/reminders/x.md") PathBuf::from("/var/lib/hyperhive/agents/manager/state/reminders/x.md")
); );
assert!(resolve_host_path("root", "/state/x.md").is_err()); // And the legacy `/state/` prefix must NOT be accepted anymore.
assert!(resolve_host_path("manager", "/state/x.md").is_err());
} }
#[test] #[test]

View file

@ -578,7 +578,7 @@ fn default_true() -> bool {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/// Logical name the broker uses for the manager. /// Logical name the broker uses for the manager.
pub const MANAGER_AGENT: &str = "root"; pub const MANAGER_AGENT: &str = "manager";
/// Logical name the broker uses for the human operator. Messages with /// Logical name the broker uses for the human operator. Messages with
/// `to = OPERATOR_RECIPIENT` accumulate in sqlite and surface on the /// `to = OPERATOR_RECIPIENT` accumulate in sqlite and surface on the
@ -693,7 +693,7 @@ pub enum HelperEvent {
/// (the dispatcher re-parses it on approve and inserts the schedule). /// (the dispatcher re-parses it on approve and inserts the schedule).
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SchedulePromptPayload { pub struct SchedulePromptPayload {
/// Names of recipient agents. Operator + `root` allowed. /// Names of recipient agents. Operator + `hm1nd` allowed.
pub targets: Vec<String>, pub targets: Vec<String>,
/// Message body delivered to each target's inbox at fire time. /// Message body delivered to each target's inbox at fire time.
/// Same size budget as `Send.body` — soft cap at the broker level. /// Same size budget as `Send.body` — soft cap at the broker level.

View file

@ -110,8 +110,8 @@ in
example = "manager"; example = "manager";
description = '' description = ''
Whether this container runs as a sub-agent (`"agent"`, the Whether this container runs as a sub-agent (`"agent"`, the
default) or as the swarm's manager (`"manager"` both invoke default invokes `hive-ag3nt serve`) or as the swarm's
`hive serve`; manager (`"manager"` invokes `hive-m1nd serve` and
defaults the forge notification surface to mentions-only). defaults the forge notification surface to mentions-only).
meta.rs flips this to `"manager"` for the manager container meta.rs flips this to `"manager"` for the manager container
@ -690,7 +690,7 @@ in
} }
]; ];
# Per-agent unix user. Runs the hive harness + # Per-agent unix user. Runs the hive-ag3nt / hive-m1nd harness +
# co-process daemons under a non-root principal. UID auto-assigned by # co-process daemons under a non-root principal. UID auto-assigned by
# NixOS. The container activation script (hive-agent-user-migrate) # NixOS. The container activation script (hive-agent-user-migrate)
# chowns the bind-mounted state dir — including credential files # chowns the bind-mounted state dir — including credential files
@ -1277,12 +1277,14 @@ in
]; ];
}; };
# Harness systemd unit. Unit shape (PATH wrapper-dir trick, env vars, # Role-driven harness systemd unit: one binary, two unit names
# RuntimeDirectory, User=, standalone-eval fallbacks): # for log/ExecStartPre stability. Unit shape (PATH wrapper-dir
# docs/agent-hierarchy.md::Harness systemd unit shape. PATH /bin # trick, env vars, RuntimeDirectory, User=, standalone-eval
# auto-append behaviour: docs/gotchas.md::systemd.services.*.path # fallbacks): docs/agent-hierarchy.md::Harness systemd unit
# appends /bin to every entry. # shape (per-role). PATH /bin auto-append behaviour:
systemd.services.hive-ag3nt = # docs/gotchas.md::systemd.services.*.path appends /bin to
# every entry.
systemd.services.${if config.hyperhive.role == "manager" then "hive-m1nd" else "hive-ag3nt"} =
let let
isManager = config.hyperhive.role == "manager"; isManager = config.hyperhive.role == "manager";
binary = "hive"; binary = "hive";
@ -1317,9 +1319,9 @@ in
} }
// lib.optionalAttrs isManager { // lib.optionalAttrs isManager {
# Standalone-eval fallbacks; meta.rs overrides at deploy time. # Standalone-eval fallbacks; meta.rs overrides at deploy time.
# HIVE_PORT = FNV-1a("root") % 900 + 8100. # HIVE_PORT = FNV-1a("hm1nd") % 900 + 8100.
HIVE_PORT = "8273"; HIVE_PORT = "8875";
HIVE_LABEL = "root"; HIVE_LABEL = "hm1nd";
# Manager always uses a unix socket so the gateway can route # Manager always uses a unix socket so the gateway can route
# /agent/<name>/ to it the same way it routes sub-agents. # /agent/<name>/ to it the same way it routes sub-agents.
HIVE_WEB_SOCKET = "/run/hive-agent/${userName}/web.sock"; HIVE_WEB_SOCKET = "/run/hive-agent/${userName}/web.sock";

View file

@ -2,13 +2,13 @@
{ {
imports = [ ./harness-base.nix ]; imports = [ ./harness-base.nix ];
# Manager role: the `systemd.services.hive-ag3nt` unit plus # Manager role: the role-driven `systemd.services.hive-m1nd` plus
# the manager-only forge defaults (`keepSubscriptions = false`, # the manager-only forge defaults (`keepSubscriptions = false`,
# `skipNotifyReasons = [ "subscribed" "participating" ]`) live in # `skipNotifyReasons = [ "subscribed" "participating" ]`) live in
# `harness-base.nix` under `lib.mkIf (config.hyperhive.role == # `harness-base.nix` under `lib.mkIf (config.hyperhive.role ==
# "manager")`. This file is the bare entry-point referenced from # "manager")`. This file is the bare entry-point referenced from
# `flake.nix` (`nixosConfigurations.root`) and the meta-flake's # `flake.nix` (`nixosConfigurations.manager`) and the meta-flake's
# `applied/root/flake.nix`. HIVE_PORT / HIVE_LABEL are injected by # `applied/hm1nd/flake.nix`. HIVE_PORT / HIVE_LABEL are injected by
# the meta-flake at deploy time and have manager-only standalone-eval # the meta-flake at deploy time and have manager-only standalone-eval
# fallbacks in `harness-base.nix`. # fallbacks in `harness-base.nix`.
hyperhive.role = "manager"; hyperhive.role = "manager";