A new swarm service, swarm-notify-router, in its own container on the host
that sets deploy.notify-router.enable (defaulted on by allSwarmServices where
the host has the router's store leaf, like the claude login minter).
- swarm-controller registers instance-wide hooks for `issues` +
`issue_assign` and `pull_request` + `pull_request_assign`, updates the
event list of a hook already registered with fewer (leaving its active flag as it is), and keeps each
verified body unchanged in the SWARM_EVENTS stream on
$SWARM.events.forge.<X-Forgejo-Event-Type>, deduped by X-Forgejo-Delivery.
A delivery the stream did not keep answers 503.
- swarm-notify-router drains its durable consumer on that stream, runs its
flows (source -> match -> todo sink, stored as nodes plus edges and checked
to be a tree) and queues each op on $SWARM.router.todo.<agent> in the
work-queue stream ROUTER_TODOS. Built-in flows: assigned, unassigned (same
key, rewritten) and closed (prefix-clears every holder's router todos). A
per-issue snapshot in its notify-router-state bucket holds the last
assignees and the todo holders; only agents in the agent-status bucket get
ops, and an assignee that was not one is left out of the snapshot so the
next assign event retries it. After the first event that must be retried,
the rest of the batch is nakked so redelivery keeps stream order.
- hive-agent creates its own durable consumer todo-<agent> and applies each
op to its todo store as subsystem `router`, then acks; a refused op stops
the rest of its batch the same way. The store's (subsystem, key) dedup
makes a redelivery harmless. New Todos::clear_prefix.
- swarm-nats-auth: a --notify-router-client arm; the controller may publish
$SWARM.events.forge.*; each agent gets three {agent} templates (extended
consumer create whose filter the server checks, MSG.NEXT, ACK).
- Credentials: a notify-router store leaf minted by glue-bao-tls, a
swarm-notify-router bao policy (read on its own queue client secret), an
authelia machine client, the secret publisher entry, a journal forwarder
route. No manual step.
hive-forge-notify is unchanged, so agents also still get forge's own
notifications.
Refs #5571
246 lines
9.9 KiB
Nix
246 lines
9.9 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 (the builder's hyperhive input)
|
|
# 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/container-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, ... }:
|
|
{
|
|
services.hyperhive.agent.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
|
|
hive-subagent-daemon
|
|
swarm-logs
|
|
swarm-metrics
|
|
assets
|
|
frontend
|
|
reference-docs
|
|
claude-plugins
|
|
;
|
|
};
|
|
};
|
|
in
|
|
{
|
|
agent-base.imports = [
|
|
./nix/templates/agent.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.deploy.hive-controller.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}";
|
|
hyperhiveRev = lib.mkDefault (self.rev or null);
|
|
# The agent-base toplevel every container builds from — 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;
|
|
};
|
|
services.hyperhive.deploy.swarm-controller.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-controller;
|
|
services.hyperhive.deploy.swarm-controller.swarmctlPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarmctl;
|
|
services.hyperhive.deploy.swarm-controller.ui.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-ui;
|
|
services.hyperhive.deploy.victorialogs.authPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-logs-auth;
|
|
services.hyperhive.deploy.victoriametrics.authPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-metrics-auth;
|
|
services.hyperhive.deploy.victorialogs.grafanaAuthPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-metrics-auth;
|
|
services.hyperhive.deploy.victoriametrics.agentAuthPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-logs-auth;
|
|
services.hyperhive.deploy.nats.authPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-nats-auth;
|
|
services.hyperhive.deploy.matrix.ctlPackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-matrix-ctl;
|
|
services.hyperhive.deploy.review-classifier.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-review-classifier;
|
|
services.hyperhive.deploy.builder.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-builder;
|
|
services.hyperhive.deploy.ci-metrics.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-ci-metrics;
|
|
services.hyperhive.deploy.claude-login.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-claude-login;
|
|
services.hyperhive.deploy.notify-router.package =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-notify-router;
|
|
services.hyperhive.deploy.authelia.bridgePackage =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swarm-authelia-bridge;
|
|
services.hyperhive.gateway.swaggerUiTheme =
|
|
lib.mkDefault
|
|
self.packages.${pkgs.stdenv.hostPlatform.system}.swagger-ui-theme;
|
|
};
|
|
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;
|
|
};
|
|
|
|
# Agent archetypes, `<name> = { module; meta; }`. Not per system: `meta`
|
|
# is plain data, and `module` takes its `pkgs` from the agent's system.
|
|
hyperhiveArchetypes = import ./nix/archetypes;
|
|
|
|
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;
|
|
}
|
|
);
|
|
};
|
|
}
|