{ description = "hyperhive — multi-Claude-Code-agent orchestration on nixos-containers"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; naersk = { url = "github:nix-community/naersk"; inputs.nixpkgs.follows = "nixpkgs"; }; treefmt-nix = { url = "github:numtide/treefmt-nix"; inputs.nixpkgs.follows = "nixpkgs"; }; }; outputs = inputs@{ self, nixpkgs, nixpkgs-unstable, naersk, treefmt-nix, }: let inherit (nixpkgs) lib; systems = [ "aarch64-linux" "x86_64-linux" ]; treefmt-config = { projectRootFile = "flake.nix"; programs = { keep-sorted.enable = true; nixfmt.enable = true; rustfmt.enable = true; taplo.enable = true; }; }; forAllSystems = f: lib.genAttrs systems ( system: f rec { inherit system; pkgs = nixpkgs.legacyPackages.${system}; treefmt-eval = treefmt-nix.lib.evalModule pkgs treefmt-config; naersk-lib = pkgs.callPackage naersk { }; } ); in { packages = forAllSystems ( { naersk-lib, ... }: { default = naersk-lib.buildPackage { src = ./.; meta.description = "hyperhive workspace (hive-c0re, hive-ag3nt, hive-m1nd)"; }; } ); overlays = { default = final: prev: { hyperhive = self.packages.${prev.stdenv.hostPlatform.system}.default; }; claude-unstable = final: prev: let # The overlay imports its own nixpkgs-unstable instance to # pin claude-code there. That instance has its own config # (independent from the user's prev.config), so we have to # set allowUnfreePredicate inline to whitelist claude-code # specifically — otherwise the unstable import itself # refuses to evaluate. This is scoped: only claude-code # bypasses unfree, nothing else. unstable = import nixpkgs-unstable { inherit (prev.stdenv.hostPlatform) system; config.allowUnfreePredicate = pkg: builtins.elem (prev.lib.getName pkg) [ "claude-code" ]; }; in { inherit (unstable) claude-code; }; }; nixosModules = { agent-base = ./nix/templates/agent-base.nix; manager = ./nix/templates/manager.nix; # The hive-c0re module wants `pkgs.hyperhive` for its default # `services.hive-c0re.package`. To avoid making operators apply an # overlay (which would also pollute their host pkgs with our # build), we thread the package straight from this flake's # `packages..default` via a `hyperhivePackage` argument. # The `claude-unstable` overlay only matters inside our container # builds (already applied internally in `nixosConfigurations`). hive-c0re = import ./nix/modules/hive-c0re.nix { hyperhivePackage = system: self.packages.${system}.default; hyperhiveFlake = "${self}"; }; }; nixosConfigurations = let mkContainer = module: nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ module { nixpkgs.overlays = [ self.overlays.default self.overlays.claude-unstable ]; } ]; }; in { agent-base = mkContainer self.nixosModules.agent-base; manager = mkContainer self.nixosModules.manager; }; devShells = forAllSystems ( { pkgs, ... }: { default = pkgs.mkShell { packages = with pkgs; [ cargo clippy pkg-config rust-analyzer rustc rustfmt sqlite ]; }; } ); formatter = forAllSystems ({ treefmt-eval, ... }: treefmt-eval.config.build.wrapper); checks = forAllSystems ( { treefmt-eval, pkgs, naersk-lib, ... }: { formatting = treefmt-eval.config.build.check self; # Clippy as a check: reuse naersk's vendored-deps environment but # replace the build phase with `cargo clippy --workspace --all-targets # -- -D warnings`. Naersk's own `mode = "clippy"` mangles the `--` # separator, so we go through overrideAttrs instead. clippy = (naersk-lib.buildPackage { src = ./.; # Skip the actual build; we only care about the clippy lint. doCheck = false; copyTarget = false; }).overrideAttrs (old: { name = "${old.name}-clippy"; nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ pkgs.clippy ]; buildPhase = '' runHook preBuild cargo clippy --workspace --all-targets -- -D warnings runHook postBuild ''; installPhase = '' runHook preInstall mkdir -p $out touch $out/.clippy-passed runHook postInstall ''; }); } ); }; }