Watch
0
0
Fork
You've already forked hyperhive
0

move the c0re host options to deploy.hive-controller

This commit is contained in:
atlas 2026-10-29 01:02:45 +01:00
commit 74f463c40e
28 changed files with 257 additions and 94 deletions

View file

@ -521,7 +521,7 @@ leftover `.git` here; `migrate::run` deletes it at startup. Contents:
the capped party never sets them. It sees only the resulting memory cap, the capped party never sets them. It sees only the resulting memory cap,
as a byte count or `infinity` in its runtime dir (below). Fallback is per as a byte count or `infinity` in its runtime dir (below). Fallback is per
*field*: an absent file, absent agent, or absent field falls back to the *field*: an absent file, absent agent, or absent field falls back to the
hive-wide `services.hyperhive.c0re.agentCpuQuota` / `agentMemoryMax`, hive-wide `services.hyperhive.deploy.hive-controller.agentCpuQuota` / `agentMemoryMax`,
so an agent can have only its memory capped and still track the hive so an agent can have only its memory capped and still track the hive
default for CPU. Every value passes the validators first, and one bad default for CPU. Every value passes the validators first, and one bad
value makes the worker change nothing here. The `CPUWeight=` / `IOWeight=` value makes the worker change nothing here. The `CPUWeight=` / `IOWeight=`

View file

@ -324,7 +324,7 @@ c0re process never leaves a partial or unparseable file behind.
### Per-agent static frontend split ### Per-agent static frontend split
hive-c0re's environment sets `HIVE_AGENT_FRONTEND_DIR` to the `agent/` hive-c0re's environment sets `HIVE_AGENT_FRONTEND_DIR` to the `agent/`
subdirectory of `services.hyperhive.c0re.servedFrontend`. subdirectory of `services.hyperhive.deploy.hive-controller.servedFrontend`.
The nginx include generator (`gateway_nginx::write`) reads The nginx include generator (`gateway_nginx::write`) reads
this variable and, when set, emits split location blocks per agent this variable and, when set, emits split location blocks per agent
instead of a single proxy block. instead of a single proxy block.
@ -386,7 +386,7 @@ agent gets a single proxy block and nginx forwards all traffic to the
agent daemon. agent daemon.
**`extraFiles`**: per-agent `services.hyperhive.agent.frontend.extraFiles` sit in **`extraFiles`**: per-agent `services.hyperhive.agent.frontend.extraFiles` sit in
`mergedDist`, not in the base `services.hyperhive.c0re.frontend` dist. They're not under `mergedDist`, not in the base `services.hyperhive.deploy.hive-controller.frontend` dist. They're not under
the nix-store `alias` path, so requests for them fall through the nix-store `alias` path, so requests for them fall through
`try_files` to `@<name>_dynamic`, and the agent daemon serves them. `try_files` to `@<name>_dynamic`, and the agent daemon serves them.

View file

@ -537,7 +537,7 @@ The fix (`nix/docs/default.nix`):
file contents only. Docs drvs only change when a `.nix` file changes. file contents only. Docs drvs only change when a `.nix` file changes.
2. The package options the modules consume (`services.hyperhive.agent.packages.*`, 2. The package options the modules consume (`services.hyperhive.agent.packages.*`,
`services.hyperhive.c0re.*`) carry no in-module defaults and every `services.hyperhive.deploy.hive-controller.*`) carry no in-module defaults and every
default that references them has a `defaultText`, so the doc walk default that references them has a `defaultText`, so the doc walk
never forces a package — no stubs needed, and the Rust/frontend never forces a package — no stubs needed, and the Rust/frontend
build closure stays out of the eval. build closure stays out of the eval.

View file

@ -172,7 +172,7 @@ resources are free. Resources:
<!-- vale write-good.Passive = YES --> <!-- vale write-good.Passive = YES -->
1. **Build slots** — `services.hyperhive.c0re.buildSlots` permits (default 1), 1. **Build slots** — `services.hyperhive.deploy.hive-controller.buildSlots` permits (default 1),
held by nix-heavy nodes for the node's duration. held by nix-heavy nodes for the node's duration.
2. **Per-agent lifecycle lease** — keyed on the **node's** agent (agent is 2. **Per-agent lifecycle lease** — keyed on the **node's** agent (agent is
per-node; a DAG can span agents) and globally exclusive per agent across per-node; a DAG can span agents) and globally exclusive per agent across
@ -391,7 +391,7 @@ or the flake root directly — none of which exist in the rendered meta flake.
## Host-level resource + performance options ## Host-level resource + performance options
A handful of `services.hyperhive.c0re.*` options tune container resource A handful of `services.hyperhive.deploy.hive-controller.*` options tune container resource
limits, build parallelism, and first-spawn latency. limits, build parallelism, and first-spawn latency.
### Build slots ### Build slots
@ -498,12 +498,12 @@ v2 **relative shares** that only decide who yields _under contention_.
A low-weight container still gets the whole machine when nothing else A low-weight container still gets the whole machine when nothing else
wants it. wants it.
| Option | Default | Description | | Option | Default | Description |
| ---------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | | ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `services.hyperhive.c0re.agentCpuQuota` | `"200%"` | CPU cap per agent, as a percentage of one core (`"200%"` = 2 cores). Raise if agents hit CPU limits during builds or heavy tool use. | | `services.hyperhive.deploy.hive-controller.agentCpuQuota` | `"200%"` | CPU cap per agent, as a percentage of one core (`"200%"` = 2 cores). Raise if agents hit CPU limits during builds or heavy tool use. |
| `services.hyperhive.c0re.agentMemoryMax` | `"4G"` | Memory cap per agent. Raise for agents that run large nix builds or hold big in-memory data. | | `services.hyperhive.deploy.hive-controller.agentMemoryMax` | `"4G"` | Memory cap per agent. Raise for agents that run large nix builds or hold big in-memory data. |
| `services.hyperhive.c0re.agentCpuWeight` | `80` | `cpu.weight` share per agent, `1`–`10000` or `null` to omit the setting. Kernel default is `100`, so `80` makes agents yield. | | `services.hyperhive.deploy.hive-controller.agentCpuWeight` | `80` | `cpu.weight` share per agent, `1`–`10000` or `null` to omit the setting. Kernel default is `100`, so `80` makes agents yield. |
| `services.hyperhive.c0re.agentIoWeight` | `80` | `io.weight` share per agent, same range and `null` handling. See the caveat below — it's a no-op on many hosts. | | `services.hyperhive.deploy.hive-controller.agentIoWeight` | `80` | `io.weight` share per agent, same range and `null` handling. See the caveat below — it's a no-op on many hosts. |
Two things to know about the weights: Two things to know about the weights:

View file

@ -136,7 +136,7 @@ with the `hive-meta-flake` renderer, all over `https`.
Each custom agent that a hive places pins to its `agent-configs/<agent>` Each custom agent that a hive places pins to its `agent-configs/<agent>`
`main`; the controller omits and logs one whose repo has no commit on `main`, so it `main`; the controller omits and logs one whose repo has no commit on `main`, so it
doesn't block the rest. Hyperhive pins to one rev, a ratchet on the controller's own doesn't block the rest. Hyperhive pins to one rev, a ratchet on the controller's own
running rev (`services.hyperhive.c0re.hyperhiveRev`, from `self.rev`): running rev (`services.hyperhive.deploy.hive-controller.hyperhiveRev`, from `self.rev`):
- the first commit pins hyperhive's forge `main`; - the first commit pins hyperhive's forge `main`;
- later, the pin moves to the controller's rev only when the forge's compare - later, the pin moves to the controller's rev only when the forge's compare

View file

@ -4,7 +4,7 @@
host (not inside any container). It talks to `hive-c0re` over the host host (not inside any container). It talks to `hive-c0re` over the host
admin socket `/run/hyperhive/host.sock`, which is root-only by default — admin socket `/run/hyperhive/host.sock`, which is root-only by default —
so it needs `sudo` unless you grant sudoless access by listing your so it needs `sudo` unless you grant sudoless access by listing your
login user in `services.hyperhive.c0re.adminUsers` (adds you to the login user in `services.hyperhive.deploy.hive-controller.adminUsers` (adds you to the
`hive-admin` group that owns the socket; see `hive-admin` group that owns the socket; see
[`docs/trust-boundary/boundary.md`](../trust-boundary/boundary.md#host-admin-socket-access-hivectl)). [`docs/trust-boundary/boundary.md`](../trust-boundary/boundary.md#host-admin-socket-access-hivectl)).
Available via the `hive-c0re` package in the host NixOS config. Available via the `hive-c0re` package in the host NixOS config.

View file

@ -158,7 +158,7 @@ boundary.
By default the socket is `0660` group-owned by **`hive-admin`**, an By default the socket is `0660` group-owned by **`hive-admin`**, an
empty group — so it's effectively **root-only** until an operator is empty group — so it's effectively **root-only** until an operator is
explicitly granted access. Grant sudoless `hivectl` by listing login explicitly granted access. Grant sudoless `hivectl` by listing login
users in `services.hyperhive.c0re.adminUsers`; the option adds each to users in `services.hyperhive.deploy.hive-controller.adminUsers`; the option adds each to
`hive-admin`, and members connect without `sudo`. The runtime dir `hive-admin`, and members connect without `sudo`. The runtime dir
`/run/hyperhive` is `0751` (traverse-only, no listing) so the group can `/run/hyperhive` is `0751` (traverse-only, no listing) so the group can
reach the socket path; the socket's own `0660 hive-admin` mode gates reach the socket path; the socket's own `0660 hive-admin` mode gates

View file

@ -234,7 +234,7 @@ library.
The cost figure is a deliberately rough estimate from a per-model The cost figure is a deliberately rough estimate from a per-model
price table (`est_cost_usd`); it drifts with list pricing and the page price table (`est_cost_usd`); it drifts with list pricing and the page
labels it accordingly. The table is operator-tunable via the labels it accordingly. The table is operator-tunable via the
`services.hyperhive.c0re.modelPrices` nix option — each key is a `services.hyperhive.deploy.hive-controller.modelPrices` nix option — each key is a
model-family short name (matched case-insensitively as a substring of model-family short name (matched case-insensitively as a substring of
the model id, longest match wins) mapping to the model id, longest match wins) mapping to
`{ input, output, cache_read, cache_write }` USD-per-million-token `{ input, output, cache_read, cache_write }` USD-per-million-token

View file

@ -115,7 +115,7 @@
]; ];
# The full host stack (nix/host-modules/default.nix aggregator) plus # The full host stack (nix/host-modules/default.nix aggregator) plus
# the package/source wiring from this flake. The wiring is a # the package/source wiring from this flake. The wiring is a
# plain config module setting the `services.hyperhive.c0re.*` # plain config module setting the `services.hyperhive.deploy.hive-controller.*`
# package options via `lib.mkDefault` — no overlay involved, and # package options via `lib.mkDefault` — no overlay involved, and
# an operator override still wins. Intended usage: # an operator override still wins. Intended usage:
# #
@ -126,16 +126,16 @@
{ lib, pkgs, ... }: { lib, pkgs, ... }:
{ {
imports = [ ./nix/host-modules ]; imports = [ ./nix/host-modules ];
services.hyperhive.c0re = { services.hyperhive.c0re.hyperhiveFlake = lib.mkDefault "${sources.hyperhiveFlakeSource}";
services.hyperhive.deploy.hive-controller = {
package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.default; package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.default;
frontend = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.frontend; frontend = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.frontend;
assets = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.assets; assets = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.assets;
xdgIcons = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.xdg-icons; xdgIcons = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.xdg-icons;
hyperhiveFlake = lib.mkDefault "${sources.hyperhiveFlakeSource}";
hyperhiveRev = lib.mkDefault (self.rev or null); hyperhiveRev = lib.mkDefault (self.rev or null);
# The agent-base toplevel every container builds from — wired into # The agent-base toplevel every container builds from — wired into
# `system.extraDependencies` when # `system.extraDependencies` when
# `services.hyperhive.c0re.preBuildAgentTemplates` is on so # `services.hyperhive.deploy.hive-controller.preBuildAgentTemplates` is on so
# the host system closure pre-fetches the heavy build # the host system closure pre-fetches the heavy build
# inputs. x86_64-linux only (nixosConfigurations are # inputs. x86_64-linux only (nixosConfigurations are
# hardcoded to that system); the gate keeps aarch64 hosts # hardcoded to that system); the gate keeps aarch64 hosts

View file

@ -231,7 +231,7 @@ pub struct ServeConfig {
/// Number of concurrent nix-heavy job-queue nodes (prebuild / /// Number of concurrent nix-heavy job-queue nodes (prebuild /
/// profile-swap / create / meta lock). hive-c0re-local like /// profile-swap / create / meta lock). hive-c0re-local like
/// `model_prices` — never injected into containers. Set via /// `model_prices` — never injected into containers. Set via
/// `services.hyperhive.c0re.buildSlots`. /// `services.hyperhive.deploy.hive-controller.buildSlots`.
pub build_slots: usize, pub build_slots: usize,
} }

View file

@ -117,7 +117,7 @@ enum Cmd {
model_prices: Option<String>, model_prices: Option<String>,
/// Override: number of concurrent nix-heavy job-queue nodes /// Override: number of concurrent nix-heavy job-queue nodes
/// (prebuild / profile-swap / create / meta lock). Set via the /// (prebuild / profile-swap / create / meta lock). Set via the
/// `services.hyperhive.c0re.buildSlots` NixOS option. /// `services.hyperhive.deploy.hive-controller.buildSlots` NixOS option.
#[arg(long)] #[arg(long)]
build_slots: Option<usize>, build_slots: Option<usize>,
}, },

View file

@ -38,7 +38,7 @@ pub struct AgentPin {
pub fn pinned_git_url(forge_git_url: &str, rev: Option<&str>) -> Result<String> { pub fn pinned_git_url(forge_git_url: &str, rev: Option<&str>) -> Result<String> {
let rev = rev.filter(|r| !r.is_empty()).context( let rev = rev.filter(|r| !r.is_empty()).context(
"hyperhive rev unknown: the host was built from a dirty tree \ "hyperhive rev unknown: the host was built from a dirty tree \
(`self.rev` unset) and `services.hyperhive.c0re.hyperhiveRev` is not set", (`self.rev` unset) and `services.hyperhive.deploy.hive-controller.hyperhiveRev` is not set",
)?; )?;
Ok(format!("git+{forge_git_url}?rev={rev}")) Ok(format!("git+{forge_git_url}?rev={rev}"))
} }

View file

@ -60,7 +60,7 @@ bare string `.tmp` in whatever the caller's cwd happens to be.
action_ in an exclusive `flock` before calling it, but `update` used to action_ in an exclusive `flock` before calling it, but `update` used to
call it with no lock whatsoever — so `create`'s lock never protected call it with no lock whatsoever — so `create`'s lock never protected
against a concurrent `update` clobbering the same `.tmp`. hive-priv never against a concurrent `update` clobbering the same `.tmp`. hive-priv never
sets a per-call cwd, so with `services.hyperhive.c0re.buildSlots > 1`, sets a per-call cwd, so with `services.hyperhive.deploy.hive-controller.buildSlots > 1`,
two concurrent calls (any mix of `create`/`update`) could share that one two concurrent calls (any mix of `create`/`update`) could share that one
`.tmp`: one's `readlink(".tmp")` resolving to the _other's_ build `.tmp`: one's `readlink(".tmp")` resolving to the _other's_ build
output, handing an agent's container the wrong agent's closure — the output, handing an agent's container the wrong agent's closure — the

View file

@ -27,7 +27,8 @@ fn connect_hint(kind: ErrorKind, socket: &Path) -> String {
ErrorKind::PermissionDenied => format!( ErrorKind::PermissionDenied => format!(
"permission denied opening the host admin socket {path} — reaching it requires \ "permission denied opening the host admin socket {path} — reaching it requires \
membership in the `hive-admin` group. Add your user to \ membership in the `hive-admin` group. Add your user to \
`services.hyperhive.adminUsers`, then log out and back in: secondary group \ `services.hyperhive.deploy.hive-controller.adminUsers`, then log out and back \
in: secondary group \
membership is applied at login, so a shell opened before the change still \ membership is applied at login, so a shell opened before the change still \
cannot connect. `id -nG` shows what the running shell actually has; \ cannot connect. `id -nG` shows what the running shell actually has; \
`newgrp hive-admin` picks the group up without a full re-login" `newgrp hive-admin` picks the group up without a full re-login"
@ -128,7 +129,10 @@ mod tests {
fn permission_denied_names_the_group_and_the_relogin() { fn permission_denied_names_the_group_and_the_relogin() {
let h = hint(ErrorKind::PermissionDenied); let h = hint(ErrorKind::PermissionDenied);
assert!(h.contains("hive-admin"), "{h}"); assert!(h.contains("hive-admin"), "{h}");
assert!(h.contains("adminUsers"), "{h}"); assert!(
h.contains("services.hyperhive.deploy.hive-controller.adminUsers"),
"{h}"
);
assert!(h.contains("log out"), "{h}"); assert!(h.contains("log out"), "{h}");
assert!( assert!(
!h.contains("not running"), !h.contains("not running"),

View file

@ -35,7 +35,7 @@ let
# heavy build inputs (matrix container, forge, etc.) stay out of # heavy build inputs (matrix container, forge, etc.) stay out of
# the eval — only option *declarations* matter for the doc walk. # the eval — only option *declarations* matter for the doc walk.
# Import the host-module aggregator from the content-addressed # Import the host-module aggregator from the content-addressed
# nixSrc; the package options (`services.hyperhive.c0re.package` # nixSrc; the package options (`services.hyperhive.deploy.hive-controller.package`
# etc.) carry no in-module defaults, but with hyperhive disabled # etc.) carry no in-module defaults, but with hyperhive disabled
# nothing reads them, so no stubs are needed. # nothing reads them, so no stubs are needed.
hostEval = nixosSystem { hostEval = nixosSystem {

View file

@ -195,6 +195,65 @@ in
[ "services" "hyperhive" "c0re" "dashboardPort" ] [ "services" "hyperhive" "c0re" "dashboardPort" ]
[ "services" "hyperhive" "deploy" "hive-controller" "dashboardPort" ] [ "services" "hyperhive" "deploy" "hive-controller" "dashboardPort" ]
) )
# The c0re daemon is the hive-controller service, so its knobs and package
# options sit under it. Same types, same meanings.
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "package" ]
[ "services" "hyperhive" "deploy" "hive-controller" "package" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "frontend" ]
[ "services" "hyperhive" "deploy" "hive-controller" "frontend" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "assets" ]
[ "services" "hyperhive" "deploy" "hive-controller" "assets" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "xdgIcons" ]
[ "services" "hyperhive" "deploy" "hive-controller" "xdgIcons" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "hyperhiveRev" ]
[ "services" "hyperhive" "deploy" "hive-controller" "hyperhiveRev" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "agentBaseToplevel" ]
[ "services" "hyperhive" "deploy" "hive-controller" "agentBaseToplevel" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "preBuildAgentTemplates" ]
[ "services" "hyperhive" "deploy" "hive-controller" "preBuildAgentTemplates" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "modelPrices" ]
[ "services" "hyperhive" "deploy" "hive-controller" "modelPrices" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "agentCpuQuota" ]
[ "services" "hyperhive" "deploy" "hive-controller" "agentCpuQuota" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "agentMemoryMax" ]
[ "services" "hyperhive" "deploy" "hive-controller" "agentMemoryMax" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "agentCpuWeight" ]
[ "services" "hyperhive" "deploy" "hive-controller" "agentCpuWeight" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "agentIoWeight" ]
[ "services" "hyperhive" "deploy" "hive-controller" "agentIoWeight" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "buildSlots" ]
[ "services" "hyperhive" "deploy" "hive-controller" "buildSlots" ]
)
(lib.mkRenamedOptionModule
[ "services" "hyperhive" "c0re" "adminUsers" ]
[ "services" "hyperhive" "deploy" "hive-controller" "adminUsers" ]
)
(lib.mkRenamedOptionModule (lib.mkRenamedOptionModule
[ "services" "hyperhive" "otel" "extraResourceAttributes" ] [ "services" "hyperhive" "otel" "extraResourceAttributes" ]
[ "services" "hyperhive" "deploy" "hive-controller" "otel" "extraResourceAttributes" ] [ "services" "hyperhive" "deploy" "hive-controller" "otel" "extraResourceAttributes" ]

View file

@ -102,12 +102,12 @@ let
else else
import ../../claude-code-no-alsa.nix lib controllerCfg.claudeCodePackage; import ../../claude-code-no-alsa.nix lib controllerCfg.claudeCodePackage;
dashboard_port = controllerCfg.dashboardPort; dashboard_port = controllerCfg.dashboardPort;
agent_cpu_quota = cfg.agentCpuQuota; agent_cpu_quota = controllerCfg.agentCpuQuota;
agent_memory_max = cfg.agentMemoryMax; agent_memory_max = controllerCfg.agentMemoryMax;
agent_cpu_weight = cfg.agentCpuWeight; agent_cpu_weight = controllerCfg.agentCpuWeight;
agent_io_weight = cfg.agentIoWeight; agent_io_weight = controllerCfg.agentIoWeight;
model_prices = cfg.modelPrices; model_prices = controllerCfg.modelPrices;
build_slots = cfg.buildSlots; build_slots = controllerCfg.buildSlots;
}; };
in in
{ {
@ -167,11 +167,11 @@ in
]; ];
environment.systemPackages = [ environment.systemPackages = [
cfg.package controllerCfg.package
pkgs.git pkgs.git
# XDG icons + .desktop entries so desktop environments can match # XDG icons + .desktop entries so desktop environments can match
# hyperhive processes to their icon (task managers, CPU monitors, etc.). # hyperhive processes to their icon (task managers, CPU monitors, etc.).
cfg.xdgIcons controllerCfg.xdgIcons
]; ];
# Serve config at a stable /etc path so hive-c0re's ExecStart # Serve config at a stable /etc path so hive-c0re's ExecStart
@ -185,8 +185,8 @@ in
# builds them, they end up in /nix/store, and the first # builds them, they end up in /nix/store, and the first
# nixos-container update + start for an agent has nothing left to # nixos-container update + start for an agent has nothing left to
# do. Gated because the closure is sizeable and pinned to x86_64. # do. Gated because the closure is sizeable and pinned to x86_64.
system.extraDependencies = lib.optionals cfg.preBuildAgentTemplates [ system.extraDependencies = lib.optionals controllerCfg.preBuildAgentTemplates [
cfg.agentBaseToplevel controllerCfg.agentBaseToplevel
]; ];
# Unprivileged coordinator user. hive-c0re runs as this user; # Unprivileged coordinator user. hive-c0re runs as this user;
@ -205,7 +205,7 @@ in
# hive control (spawn/kill/destroy/deploy, docs/trust-boundary/boundary.md), so this is # hive control (spawn/kill/destroy/deploy, docs/trust-boundary/boundary.md), so this is
# an explicit opt-in allowlist — empty by default (root-only). # an explicit opt-in allowlist — empty by default (root-only).
users.groups.hive-admin = { users.groups.hive-admin = {
members = cfg.adminUsers; members = controllerCfg.adminUsers;
}; };
# Let `hive-admin` members run `hivectl agent <name> choom` # Let `hive-admin` members run `hivectl agent <name> choom`
@ -259,7 +259,7 @@ in
]; ];
environment = import ./environment.nix { inherit lib config pkgs; }; environment = import ./environment.nix { inherit lib config pkgs; };
serviceConfig = { serviceConfig = {
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --config /etc/hyperhive/serve.json"; ExecStart = "${controllerCfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --config /etc/hyperhive/serve.json";
SyslogIdentifier = "hive-c0re"; SyslogIdentifier = "hive-c0re";
# Migrate hive-c0re's *own* state to the service user after an # Migrate hive-c0re's *own* state to the service user after an
# upgrade from a root-run install (systemd's StateDirectory only # upgrade from a root-run install (systemd's StateDirectory only
@ -378,7 +378,7 @@ in
# Must match the `--socket` arg passed to `hive-c0re serve`. # Must match the `--socket` arg passed to `hive-c0re serve`.
ListenStream = "/run/hyperhive/host.sock"; ListenStream = "/run/hyperhive/host.sock";
# `0660 root:hive-admin` — group-owned by `hive-admin` so operators in # `0660 root:hive-admin` — group-owned by `hive-admin` so operators in
# that group (services.hyperhive.c0re.adminUsers) drive `hivectl` # that group (services.hyperhive.deploy.hive-controller.adminUsers) drive `hivectl`
# without root; an empty adminUsers list leaves the group memberless, # without root; an empty adminUsers list leaves the group memberless,
# so it stays effectively root-only. See docs/trust-boundary/boundary.md. # so it stays effectively root-only. See docs/trust-boundary/boundary.md.
SocketMode = "0660"; SocketMode = "0660";

View file

@ -8,7 +8,7 @@
pkgs, pkgs,
}: }:
let let
cfg = config.services.hyperhive.c0re; controllerCfg = config.services.hyperhive.deploy.hive-controller;
baoDeploy = config.services.hyperhive.deploy.bao; baoDeploy = config.services.hyperhive.deploy.bao;
baoCfg = config.services.hyperhive.swarm.bao; baoCfg = config.services.hyperhive.swarm.bao;
# A reader is defined by holding a certificate the store accepts, not by # A reader is defined by holding a certificate the store accepts, not by
@ -64,7 +64,7 @@ in
# nix store directly; dynamic API paths still proxied to the # nix store directly; dynamic API paths still proxied to the
# agent daemon. nginx runs on the host, which is where this # agent daemon. nginx runs on the host, which is where this
# store path already is, so it is reachable as written. # store path already is, so it is reachable as written.
HIVE_AGENT_FRONTEND_DIR = "${cfg.servedFrontend}/agent"; HIVE_AGENT_FRONTEND_DIR = "${controllerCfg.servedFrontend}/agent";
} }
// { // {
# Identity env vars threaded into c0re's own service env and # Identity env vars threaded into c0re's own service env and

View file

@ -1,11 +1,12 @@
# Option declarations for `services.hyperhive.c0re.*` — the c0re # Option declarations for the c0re daemon. `services.hyperhive.c0re.*`
# daemon's knobs plus the package/source options the flake's # keeps the two flake-source options the swarm builder also reads
# `nixosModules.default` wires to its own outputs (they carry no # (`hyperhiveFlake`, `nixpkgsFlake`); every other knob and the
# in-module defaults; see ../../../flake.nix). The read-only # package/asset options the flake's `nixosModules.default` wires to its
# own outputs (they carry no in-module defaults; see ../../../flake.nix)
# live under `services.hyperhive.deploy.hive-controller`, alongside the
# values the meta-flake render hands the hive's agents. The read-only
# `servedFrontend` option lives in ./theme.nix with the stylix wiring # `servedFrontend` option lives in ./theme.nix with the stylix wiring
# that computes it. The hive's own values the meta-flake render hands # that computes it.
# its agents are declared here too, under
# `services.hyperhive.deploy.hive-controller`.
{ {
pkgs, pkgs,
lib, lib,
@ -23,6 +24,33 @@
]; ];
options.services.hyperhive.c0re = { options.services.hyperhive.c0re = {
hyperhiveFlake = lib.mkOption {
type = lib.types.str;
defaultText = lib.literalMD "the hyperhive flake's own filtered source store path";
description = ''
Store path of the hyperhive flake source (no fragment). hive-c0re
reads it to tell which revision is running, and the swarm builder
builds on it. Wired by `nixosModules.default` to this flake's own
filtered source.
'';
};
nixpkgsFlake = lib.mkOption {
type = lib.types.str;
default = "path:${pkgs.path}";
defaultText = lib.literalMD "`\"path:\${pkgs.path}\"`";
description = ''
Store-path URL of the nixpkgs the swarm builder's check flake
builds on. The swarm meta flake does not use it: its `nixpkgs`
follows `hyperhive/nixpkgs`.
Defaults to `"path:''${pkgs.path}"`, the nixpkgs the host NixOS
module was evaluated with.
'';
};
};
# The hive-controller's deployment options.
options.services.hyperhive.deploy.hive-controller = {
package = lib.mkOption { package = lib.mkOption {
type = lib.types.package; type = lib.types.package;
defaultText = lib.literalExpression "hyperhive.packages.\${system}.default"; defaultText = lib.literalExpression "hyperhive.packages.\${system}.default";
@ -71,16 +99,6 @@
processes to their icon. processes to their icon.
''; '';
}; };
hyperhiveFlake = lib.mkOption {
type = lib.types.str;
defaultText = lib.literalMD "the hyperhive flake's own filtered source store path";
description = ''
Store path of the hyperhive flake source (no fragment). hive-c0re
reads it to tell which revision is running, and the swarm builder
builds on it. Wired by `nixosModules.default` to this flake's own
filtered source.
'';
};
hyperhiveRev = lib.mkOption { hyperhiveRev = lib.mkOption {
type = lib.types.nullOr lib.types.str; type = lib.types.nullOr lib.types.str;
default = null; default = null;
@ -105,19 +123,6 @@
enabled. enabled.
''; '';
}; };
nixpkgsFlake = lib.mkOption {
type = lib.types.str;
default = "path:${pkgs.path}";
defaultText = lib.literalMD "`\"path:\${pkgs.path}\"`";
description = ''
Store-path URL of the nixpkgs the swarm builder's check flake
builds on. The swarm meta flake does not use it: its `nixpkgs`
follows `hyperhive/nixpkgs`.
Defaults to `"path:''${pkgs.path}"`, the nixpkgs the host NixOS
module was evaluated with.
'';
};
preBuildAgentTemplates = lib.mkOption { preBuildAgentTemplates = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
@ -325,12 +330,7 @@
socket root-only, as before. socket root-only, as before.
''; '';
}; };
};
# What this hive hands its own agents at container start, through
# `/etc/hyperhive-hive-facts`. Per host, so two hives may set them
# differently.
options.services.hyperhive.deploy.hive-controller = {
claudeCodePackage = lib.mkOption { claudeCodePackage = lib.mkOption {
type = lib.types.nullOr lib.types.package; type = lib.types.nullOr lib.types.package;
default = null; default = null;

View file

@ -6,7 +6,7 @@
# the entire swap contract — `theme.css` derives every semantic var # the entire swap contract — `theme.css` derives every semantic var
# from the 16 base16 slots (see docs/web-ui/css-vars.md). The guarded # from the 16 base16 slots (see docs/web-ui/css-vars.md). The guarded
# access makes this a clean no-op when stylix isn't imported into the # access makes this a clean no-op when stylix isn't imported into the
# host config. Exposed as the read-only `c0re.servedFrontend` option. # host config. Exposed as the read-only `deploy.hive-controller.servedFrontend` option.
# #
# Detection + CSS-generation itself lives in `../stylix-theme.nix` # Detection + CSS-generation itself lives in `../stylix-theme.nix`
# (shared with `swarm-ui.nix`) — this file is the c0re-specific overlay # (shared with `swarm-ui.nix`) — this file is the c0re-specific overlay
@ -18,7 +18,7 @@
... ...
}: }:
let let
cfg = config.services.hyperhive.c0re; cfg = config.services.hyperhive.deploy.hive-controller;
stylixTheme = import ../stylix-theme.nix { inherit lib config pkgs; }; stylixTheme = import ../stylix-theme.nix { inherit lib config pkgs; };
inherit (stylixTheme) stylixThemeColors themedColorsCss; inherit (stylixTheme) stylixThemeColors themedColorsCss;
# Overlay the generated colors.css onto both dist subtrees. Both the # Overlay the generated colors.css onto both dist subtrees. Both the
@ -42,7 +42,7 @@ let
''; '';
in in
{ {
options.services.hyperhive.c0re.servedFrontend = lib.mkOption { options.services.hyperhive.deploy.hive-controller.servedFrontend = lib.mkOption {
type = lib.types.package; type = lib.types.package;
internal = true; internal = true;
readOnly = true; readOnly = true;

Some files were not shown because too many files have changed in this diff Show more