hyperhive/flake.nix
atlas fbffccbbb2 feat(nix): a flake check that actually covers nix
Every other check in nix/checks.nix is a Rust derivation, so a
.nix-only diff moves no hash, the whole set is cache hits, and
`nix flake check` reports green without evaluating what changed.

`checks.module-eval` is one derivation holding a table of cases, each
named by the PROPERTY it defends. Its builder text embeds the evaluated
results, so the derivation's hash is a function of them: a nix change
that flips a property rebuilds the check and fails in the builder,
naming that property.

PROVEN, not assumed — the mechanism was executed before the cases were
written. Same expression with one property true vs false:

  drvPath true  -> 5v11mnbv…-module-eval.drv
  drvPath false -> ivm3dvv8…-module-eval.drv        (differs)
  build false   -> FAILS, stderr names the property

and the table itself was mutation-tested: inverting one case's
expectation gives `FAILED: a hive that has not opted into all-local
runs no swarm controller / module-eval: 1 of 5 properties broke`. A
check that cannot go red on a broken tree is not evidence.

Cases are named by property and never by ticket: a case named after the
ticket that prompted it has that ticket's lifetime; one named after the
property lives as long as the property does.

⚠️ It evaluates, it does not execute. Where the artifact is a command
line, a request or a certificate, a value assertion cannot stand in —
that is written into the file's header, because the gap is exactly what
made two earlier outages evaluable-but-broken.
2026-08-13 17:26:58 +02:00

211 lines
7.7 KiB
Nix

{
description = "hyperhive multi-Claude-Code-agent orchestration on nixos-containers";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# Crane is stateless — no nixpkgs input to follow; `crane.mkLib
# pkgs` returns the lib at whatever pkgs we pass it (we use the
# project's pinned nixpkgs).
crane.url = "github:ipetkov/crane";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
# Thin entry point — the real logic lives under nix/:
# nix/sources.nix filtered source views (meta-flake + docs inputs)
# nix/rust.nix shared crane wiring (cleanSrc, cargoArtifacts)
# nix/packages/ every package output
# nix/checks.nix flake checks
# nix/devshell.nix dev shell
# nix/treefmt.nix formatter config
# nix/host-modules/, nix/agent-modules/, nix/templates/ the NixOS module trees
outputs =
inputs@{
self,
nixpkgs,
crane,
treefmt-nix,
}:
let
inherit (nixpkgs) lib;
systems = [
"aarch64-linux"
"x86_64-linux"
];
sources = import ./nix/sources.nix { inherit lib; };
forAllSystems =
f:
lib.genAttrs systems (
system:
f rec {
inherit system;
pkgs = nixpkgs.legacyPackages.${system};
treefmt-eval = treefmt-nix.lib.evalModule pkgs (import ./nix/treefmt.nix);
craneLib = crane.mkLib pkgs;
rust = import ./nix/rust.nix { inherit pkgs craneLib; };
}
);
in
{
packages = forAllSystems (
{
pkgs,
craneLib,
rust,
...
}:
import ./nix/packages {
inherit
pkgs
craneLib
rust
self
nixpkgs
;
}
);
nixosModules =
let
# Package wiring for agent containers — the harness modules
# consume hyperhive's own packages via the `hyperhive.packages`
# option (see nix/agent-modules/packages.nix); no overlay.
# The `mkDefault` is applied PER KEY (`mapAttrs`), not to the
# whole attrset: definition-level priority filtering runs
# before `attrsOf`'s per-key merge, so a whole-set `mkDefault`
# would be discarded entirely the moment an agent.nix
# overrides a single key. Per-key priorities make an
# individual override win while every other key keeps the
# flake default.
agentPackages =
{ lib, pkgs, ... }:
{
hyperhive.packages = lib.mapAttrs (_: lib.mkDefault) {
inherit (self.packages.${pkgs.stdenv.hostPlatform.system})
hive-agent
hive-agent-mcp
hive-bash-daemon
hive-forge
hive-forge-notify
hive-github-notify
hive-matrix-daemon
hive-metric
hive-screen-mcp
assets
frontend
reference-docs
claude-plugins
;
};
};
in
{
agent-base.imports = [
./nix/templates/agent.nix
agentPackages
];
ruth.imports = [
./nix/templates/ruth.nix
agentPackages
];
# The full host stack (nix/host-modules/default.nix aggregator) plus
# the package/source wiring from this flake. The wiring is a
# plain config module setting the `services.hyperhive.c0re.*`
# package options via `lib.mkDefault` — no overlay involved, and
# an operator override still wins. Intended usage:
#
# imports = [ hyperhive.nixosModules.default ];
# services.hyperhive.enable = true;
#
default =
{ lib, pkgs, ... }:
{
imports = [ ./nix/host-modules ];
services.hyperhive.c0re = {
package = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.default;
frontend = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.frontend;
assets = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.assets;
xdgIcons = lib.mkDefault self.packages.${pkgs.stdenv.hostPlatform.system}.xdg-icons;
hyperhiveFlake = lib.mkDefault "${sources.hyperhiveFlakeSource}";
# Narrow docs/ source, threaded as its own meta-flake input
# so doc edits don't re-hash the whole flake source.
hyperhiveDocs = lib.mkDefault "${sources.hyperhiveDocsSource}";
# Per-container toplevels — wired into
# `system.extraDependencies` when
# `services.hyperhive.c0re.preBuildAgentTemplates` is on so
# the host system closure pre-fetches the heavy build
# inputs. x86_64-linux only (nixosConfigurations are
# hardcoded to that system); the gate keeps aarch64 hosts
# from pulling them in via cross-build.
agentBaseToplevel = lib.mkDefault self.packages.x86_64-linux.agent-base-toplevel;
managerToplevel = lib.mkDefault self.packages.x86_64-linux.ruth-toplevel;
};
services.hyperhive.swarm.controller.package =
lib.mkDefault
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-controller;
services.hyperhive.swarm.controller.swarmctlPackage =
lib.mkDefault
self.packages.${pkgs.stdenv.hostPlatform.system}.swarmctl;
services.hyperhive.swarm.ui.package =
lib.mkDefault
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-ui;
services.hyperhive.gateway.swaggerUiTheme =
lib.mkDefault
self.packages.${pkgs.stdenv.hostPlatform.system}.swagger-ui-theme;
};
hive-ci = ./nix/host-modules/hive-ci.nix;
hive-forge = ./nix/host-modules/hive-forge;
};
nixosConfigurations =
let
# These two configs are what hive-c0re extends per agent
# (meta.rs's `mkAgent` does `<base>.extendModules { … }`, so
# the built base and every container stay on one version).
# Nothing deployment-specific may be set here: it would be
# inherited by every container on the hive and collide with
# the per-agent values hive-c0re renders. The agent modules
# are written so a bare evaluation needs no such values —
# service URLs default to `null`, meaning "not configured",
# and the units that would use them simply aren't generated.
mkContainer =
module:
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [ module ];
};
in
{
agent-base = mkContainer self.nixosModules.agent-base;
ruth = mkContainer self.nixosModules.ruth;
};
devShells = forAllSystems ({ pkgs, rust, ... }: import ./nix/devshell.nix { inherit pkgs rust; });
formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper);
checks = forAllSystems (
{
pkgs,
system,
treefmt-eval,
craneLib,
rust,
...
}:
import ./nix/checks.nix {
inherit
pkgs
craneLib
rust
self
system
treefmt-eval
;
inherit (nixpkgs.lib) nixosSystem;
}
);
};
}