Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/packages/default.nix
atlas 0f7b403803 agents get a todo when assigned to an issue or PR, rewritten when unassigned and cleared on close
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
2026-10-22 07:28:53 +02:00

306 lines
15 KiB
Nix

# All flake package outputs. Imported per system from flake.nix; the
# shared rust build wiring (cleanSrc / cargoArtifactsBinOnly /
# nativeBuildInputs) comes in via `rust` (see ../rust.nix).
{
pkgs,
craneLib,
rust,
self,
nixpkgs,
}:
let
inherit (pkgs) lib;
inherit (rust) cleanSrc cargoArtifactsBinOnly nativeBuildInputs;
docsAttrs = import ../docs {
inherit pkgs self;
inherit (nixpkgs) lib;
inherit (nixpkgs.lib) nixosSystem;
};
# Plain vendored Swagger UI dist (./swagger-ui-dist.nix) + the
# hyperhive-themed overlay on top (./swagger-ui-theme.nix) — bound
# here (not just inline in the attrset below) so the theme
# derivation can take the dist derivation as an explicit input.
swagger-ui-dist = pkgs.callPackage ./swagger-ui-dist.nix { };
swagger-ui-theme = pkgs.callPackage ./swagger-ui-theme.nix { inherit swagger-ui-dist; };
# Every per-binary package: name → description. The single source of
# truth for the bin list — it drives the per-bin extractor packages
# and the `default` bundle, so adding a binary is one entry here.
daemonBins = {
hive-c0re = "hyperhive host coordinator daemon";
hive-priv = "hyperhive privileged root helper";
hive-agent = "hyperhive in-container agent harness serve loop";
hive-agent-mcp = "hyperhive agent-surface MCP server";
hive-bash-daemon = "hyperhive per-agent bash-task runner daemon (serves its MCP tools directly over streamable-http)";
hive-subagent-daemon = "hyperhive per-agent claude-subagent task runner daemon (serves its MCP tools directly over streamable-http)";
hive-matrix-daemon = "hyperhive per-agent matrix-sdk daemon (serves its MCP tools directly over streamable-http)";
hive-metric = "hyperhive agent-emitted custom metrics CLI";
# The read half of what `hive-metric` writes, from the same seat: an
# agent's CLI for the swarm log store. In this list rather than beside
# `swarmctl` below because it is run *by an agent, inside a container*,
# which is exactly what this list's per-bin packages are for — see
# `nix/agent-modules/logs.nix` for what puts it on PATH there.
swarm-logs = "hyperhive agent-facing swarm log store query CLI";
# The same for the metrics store; `nix/agent-modules/metrics.nix` puts it on PATH.
swarm-metrics = "hyperhive agent-facing swarm metrics store query CLI";
hive-screen-mcp = "hyperhive screen MCP bridge (screenshot + input for GUI agents)";
hive-forge = "hyperhive Forgejo CLI";
hive-forge-notify = "hyperhive per-agent Forgejo notification poller daemon";
hive-github-notify = "hyperhive per-agent github.com notification poller daemon";
};
# ONE compile of the whole workspace (every bin, sharing the
# prebuilt `cargoArtifactsBinOnly` dep cache). The per-bin packages below
# are cheap copy-extractors over this, so workspace lib crates
# (hive-sh4re, hive-claude, …) compile exactly once instead of once
# per bin derivation.
#
# Tests are kept in the separate `checks.cargo-test` derivation
# (carries the hyperhive-assets build input for the prompt-template
# assertions in hive-agent::prompt::tests). Keeping them out of this
# derivation means a prompt edit doesn't bust the cargo cache.
#
# …and the dep cache underneath is the one WITHOUT test targets. This
# derivation never runs a test — `doCheck = false` below says so — so it
# sits on a cache that skips every dev-dependency and test harness, work
# whose only consumers are the checks. Compiling them here would make a
# deploy pay for it on every toolchain or dependency change.
workspaceBuild = craneLib.buildPackage {
src = cleanSrc;
cargoArtifacts = cargoArtifactsBinOnly;
inherit nativeBuildInputs;
pname = "hyperhive-workspace";
version = "0.1.0";
doCheck = false;
};
# Per-bin extractor: COPIES one binary out of the workspace build.
# A copy, not a symlink — a symlink would keep the whole workspace
# output (and thus every other binary) in the consumer's runtime
# closure, defeating the point of the per-bin split. The copied
# binary's RPATH references only the libs it links, so nix's
# reference scan gives each package a narrow closure.
mkBinPackage =
bin: description:
pkgs.runCommand bin
{
meta = {
inherit description;
mainProgram = bin;
};
}
''
install -Dm755 ${workspaceBuild}/bin/${bin} $out/bin/${bin}
'';
# Operator CLI — extractor plus shell completions and the `wg`
# wrapper. Suitable for `nix profile install .#hivectl` /
# `environment.systemPackages = [ …packages.<system>.hivectl ]`
# when the operator only wants the admin CLI.
# Completions come from the binary's own `completions <shell>` verb
# (the single source of truth, so they never drift from the actual
# verbs). The wireguard-tools wrap makes `hivectl wg` subcommands
# work before `deploy.wireguard.enable` is set (wg init is the very
# first setup step). Completion generation runs before wrapProgram
# since wrapProgram renames the real binary.
hivectlPkg =
pkgs.runCommand "hivectl"
{
nativeBuildInputs = [
pkgs.installShellFiles
pkgs.makeWrapper
];
meta = {
description = "hyperhive operator CLI";
mainProgram = "hivectl";
};
}
''
install -Dm755 ${workspaceBuild}/bin/hivectl $out/bin/hivectl
installShellCompletion --cmd hivectl \
--bash <("$out/bin/hivectl" completions bash) \
--zsh <("$out/bin/hivectl" completions zsh) \
--fish <("$out/bin/hivectl" completions fish)
wrapProgram "$out/bin/hivectl" \
--prefix PATH : ${pkgs.wireguard-tools}/bin
'';
# Per-bin split packages. Agent containers depend on the individual
# bins they actually exec/PATH-need (see nix/agent-modules/packages.nix)
# instead of the `default` bundle — that keeps `hivectl` (dials the *host*
# admin socket, unreachable from inside a container, drags in
# `wireguard-tools`) and redundant binaries out of every agent's
# closure.
binPkgs = lib.mapAttrs mkBinPackage daemonBins // {
hivectl = hivectlPkg;
};
in
{
# All workspace binaries in one derivation — a symlinkJoin over the
# per-bin packages, pure assembly with no extra compilation. The
# host module's `services.hyperhive.c0re.package` and `nix build .#`
# both land here.
default = pkgs.symlinkJoin {
name = "hyperhive";
paths = lib.attrValues binPkgs;
};
}
// binPkgs
// {
# Swarm-level controller daemon. Deliberately NOT in `daemonBins`:
# that list is the core stack — the binaries hive-c0re and the agent
# harness are made of — and it drives the `default` bundle that
# `services.hyperhive.c0re.package` points at. This daemon is a
# separate swarm-scoped service with its own module and its own
# `package` option, and one hive in a swarm runs it, so folding it
# into the core bundle would put it in every hive's closure to no end.
# Uses the same per-bin extractor, just bound on its own.
swarm-controller = mkBinPackage "swarm-controller" "hyperhive swarm-level controller daemon";
# The queue's auth-callout responder. Out of `daemonBins` for the same
# reason as the two above and one more: it runs *inside* the swarm-nats
# container, not on the host, so it belongs in that container's closure
# rather than every hive's.
swarm-nats-auth = mkBinPackage "swarm-nats-auth" "hyperhive swarm queue auth-callout responder";
# The log store gateway's `auth_request` responder for agents. Out of
# `daemonBins` like the responder above: it runs on the log store's host only.
swarm-logs-auth = mkBinPackage "swarm-logs-auth" "hyperhive swarm log store agent auth responder";
# The metrics store gateway's `auth_request` responder for Grafana's bearer. Out
# of `daemonBins` like the two above: it runs on the metrics store's host only.
swarm-metrics-auth = mkBinPackage "swarm-metrics-auth" "hyperhive swarm metrics store Grafana auth responder";
# The matrix container's control binary; `mint` publishes the sender token. Out of `daemonBins` for
# "runs *inside* a container, not on the host" reason as the responder
# above, and with a second one: putting it in the core bundle would place
# the binary that reads the appservice token on every hive's filesystem,
# which is the arrangement it exists to end.
swarm-matrix-ctl = mkBinPackage "swarm-matrix-ctl" "hyperhive matrix container control binary";
# The review classifier's daemon. Out of `daemonBins` for the responder's
# reason: it runs inside its own container, not on the host.
swarm-review-classifier = mkBinPackage "swarm-review-classifier" "hyperhive review classifier try-out daemon";
swarm-builder = mkBinPackage "swarm-builder" "hyperhive swarm builder: agent config build checks";
swarm-ci-metrics = mkBinPackage "swarm-ci-metrics" "hyperhive swarm CI run poller";
swarm-claude-login = mkBinPackage "swarm-claude-login" "hyperhive claude login minter";
swarm-notify-router = mkBinPackage "swarm-notify-router" "hyperhive swarm notification router";
# The only process allowed to write swarm-authelia's users database —
# same "runs *inside* a container, not on the host" placement as
# `swarm-nats-auth` above (this one lives in `swarm-authelia`'s
# container, as authelia's own user, not the host's closure).
swarm-authelia-bridge = mkBinPackage "swarm-authelia-bridge" "hyperhive swarm-authelia users-database write bridge";
# The swarm operator's CLI, out of `daemonBins` for the same reason as
# the daemon above: it is installed by the swarm-controller module on
# the one host that runs the controller, and belongs in that hive's
# closure only. Kept a separate derivation rather than a second binary
# in the daemon's package so a hive can pin one without the other.
#
# Not `mkBinPackage`, because of the completions: they come from the
# binary's own `completions <shell>` verb, which walks the live clap
# tree, so they cannot drift from the actual verbs. Same shape as
# `hivectlPkg` above.
swarmctl =
pkgs.runCommand "swarmctl"
{
nativeBuildInputs = [ pkgs.installShellFiles ];
meta = {
description = "hyperhive swarm-level operator CLI";
mainProgram = "swarmctl";
};
}
''
install -Dm755 ${workspaceBuild}/bin/swarmctl $out/bin/swarmctl
installShellCompletion --cmd swarmctl \
--bash <("$out/bin/swarmctl" completions bash) \
--zsh <("$out/bin/swarmctl" completions zsh) \
--fish <("$out/bin/swarmctl" completions fish)
'';
# Static build of the swarm-level UI shell — see ./swarm-ui.nix. Same
# "swarm-scoped, not core-bundle" reasoning as swarm-controller/swarmctl
# above, kept a separate package for the same reason: not every hive
# runs the swarm controller, so this shouldn't ride along in
# `packages.default`'s closure.
swarm-ui = pkgs.callPackage ./swarm-ui.nix {
branding-svg = ../../branding/hyperhive.svg;
branding-svg-maskable = ../../branding/hyperhive-maskable.svg;
};
# Bundled browser assets — see ./frontend.nix. Output is
# $out/{dashboard,agent}/ which the Rust binaries serve via
# tower_http::ServeDir.
frontend = pkgs.callPackage ./frontend.nix {
branding-svg = ../../branding/hyperhive.svg;
};
# Static runtime assets the rust binaries read via
# `hive_sh4re::assets::*`: branding/* + prompts/*, plus the
# rendered agent-configs.png. Split out of the rust derivation so
# a tweak to e.g. system.md doesn't bust the cargo cache. Build
# input of the `cargo-test` check but NOT of `packages.default`,
# so the binary derivation stays cached when a prompt edit ripples
# through.
assets = pkgs.callPackage ./assets.nix { };
# hyperhive-authored Claude Code plugin marketplace (source tree at
# ../../claude-plugins) — a local-path marketplace spliced into the
# default `services.hyperhive.agent.claudeMarketplaces`/`claudePlugins` lists. See
# ./claude-plugins.nix for the "why a store path, not a repo" rationale.
claude-plugins = pkgs.callPackage ./claude-plugins.nix { };
# The repo docs/ markdown tree as a standalone derivation —
# agents read it in-container (added as a claude additional
# directory) and the website repo reuses it as a flake input,
# neither of which needs the branding/prompt assets. See
# ./reference-docs.nix. `self` is needed to read each workspace
# crate's own README.md into the virtual `crates/` subdir;
# `docsAttrs.bundle` (the same tree `docs` below exposes standalone)
# is projected into the virtual `options/` subdir the same way.
reference-docs = pkgs.callPackage ./reference-docs.nix {
inherit self;
optionsMd = docsAttrs.bundle;
};
# XDG icon set + .desktop entries for hyperhive processes.
# Narrow input: only the branding SVG, so unrelated source changes
# don't bust this derivation's cache.
xdg-icons = pkgs.callPackage ./hive-xdg-icons.nix {
hyperhiveSvg = ../../branding/hyperhive.svg;
};
# Swagger UI: plain vendored dist + the hyperhive-themed overlay —
# see ./swagger-ui-dist.nix / ./swagger-ui-theme.nix (both computed
# above, in `let`). The gateway serves `swagger-ui-theme`'s full
# tree straight from the store at /api/docs/; a theme tweak is a
# gateway config change, not a hive-c0re rebuild. `swagger-ui-dist`
# is exposed too since it's independently useful/inspectable.
inherit swagger-ui-dist swagger-ui-theme;
# Pre-built system closure every agent container builds from. Exposed as a package
# so operators can `nix build .#agent-base-toplevel` (or wire
# them into their host system closure via the
# `preBuildAgentTemplates` option on the hive-c0re module —
# see nix/host-modules/hive-c0re/options.nix). Speeds up the first agent
# spawn dramatically because the heavy lifting (nixpkgs +
# claude-code + hive-agent binary) is already in the store
# when the meta evaluator goes to build the container.
#
# nixosConfigurations are pinned to x86_64-linux (nixos-
# containers only run native arch), so this toplevel is
# only useful on an x86_64-linux host — flake check across
# systems still tolerates evaluating it on aarch64 because
# it's a plain derivation, but `nix build` from a non-x86
# host would only succeed via a remote x86 builder.
agent-base-toplevel = self.nixosConfigurations.agent-base.config.system.build.toplevel;
# Auto-generated nix options reference for hyperhive.
# `docs` bundles host + swarm + deploy + agent pages into one tree;
# the split outputs are useful when consumers only want one surface.
# All five are pure markdown — no rust or frontend deps in
# the closure, so `nix build .#docs` is cheap.
docs = docsAttrs.bundle;
docs-host = docsAttrs.host;
docs-swarm = docsAttrs.swarm;
docs-deploy = docsAttrs.deploy;
docs-agent = docsAttrs.agent;
}