diff --git a/flake.nix b/flake.nix index 1161ae21..f0804f7a 100644 --- a/flake.nix +++ b/flake.nix @@ -115,6 +115,13 @@ nativeBuildInputs, ... }: + let + docsAttrs = import ./nix/docs.nix { + inherit pkgs self; + inherit (nixpkgs) lib; + inherit (nixpkgs.lib) nixosSystem; + }; + in { # Build the workspace binaries without running tests. Tests # are run as a separate check (`checks.cargo-test`) that @@ -167,6 +174,15 @@ # host would only succeed via a remote x86 builder. agent-base-toplevel = self.nixosConfigurations.agent-base.config.system.build.toplevel; manager-toplevel = self.nixosConfigurations.manager.config.system.build.toplevel; + + # Auto-generated nix options reference for hyperhive (#616). + # `docs` bundles host + agent pages into one tree; the split + # outputs are useful when consumers only want one surface. + # All three 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-agent = docsAttrs.agent; } ); @@ -328,6 +344,19 @@ cargoTestExtraArgs = "--workspace"; HIVE_ASSETS_DIR = "${self.packages.${system}.assets}/share/hyperhive"; }; + # Nix options docs evaluation (#616). Cheap: pulls in + # `nixosOptionsDoc` + the host module's stub eval, no rust or + # frontend deps. CI fails fast if a module change breaks + # option declarations or the doc rendering. `docs-host` + + # `docs-agent` are exposed as `packages` outputs (not checks) + # — they share this eval, so building the bundle here covers + # both surfaces in one shot. + docs = + (import ./nix/docs.nix { + inherit pkgs self; + inherit (nixpkgs) lib; + inherit (nixpkgs.lib) nixosSystem; + }).bundle; } ); }; diff --git a/nix/docs.nix b/nix/docs.nix new file mode 100644 index 00000000..ba222889 --- /dev/null +++ b/nix/docs.nix @@ -0,0 +1,177 @@ +{ + pkgs, + lib, + self, + nixosSystem, +}: +# Options documentation for hyperhive's NixOS module surfaces. +# Closes #616. +# +# Two output trees: +# docs-host — operator-facing options exposed by the meta module +# (`hyperhive.nixosModules.default`). Covers +# `hyperhive.*` (domain, enable, c0re, forge, matrix) +# plus the deprecated `services.hive-c0re.*` alias. +# docs-agent — per-agent options declared by the shared harness +# module (`nix/templates/harness-base.nix`, transitively +# imported by `agent-base.nix` and `manager.nix`). +# Covers `hyperhive.*` (model, allowedRecipients, +# extraMcpServers, frontend, forge, matrix, gui, …). +# +# Both render as CommonMark via `pkgs.nixosOptionsDoc`. `docs` bundles +# them into one derivation alongside a small `README.md` index so it +# can be published verbatim. +let + # Evaluate the host module under a stub NixOS system. Stubs satisfy + # the few hard-required options (filesystems, stateVersion) without + # actually enabling the hive — we only want the option *declarations* + # to evaluate, not the config. + hostEval = nixosSystem { + system = pkgs.stdenv.hostPlatform.system; + modules = [ + self.nixosModules.default + ( + { lib, ... }: + { + nixpkgs.overlays = [ self.overlays.default ]; + fileSystems."/" = { + device = "/dev/null"; + fsType = "tmpfs"; + }; + boot.loader.grub.enable = false; + system.stateVersion = "25.11"; + # Force-disable every hyperhive subsystem so config evaluation + # doesn't pull in heavy build inputs (matrix container, forge, + # etc.). Options are still fully declared either way — that's + # what nixosOptionsDoc traverses. + services.hive-c0re.enable = lib.mkForce false; + hyperhive.forge.enable = lib.mkForce false; + hyperhive.matrix.enable = lib.mkForce false; + } + ) + ]; + }; + + # Agent options live in the already-evaluated `agent-base` container + # config. Reusing it avoids re-evaluating the harness module against + # a fresh stub — the options tree is identical to what a real agent + # container sees. + agentEval = self.nixosConfigurations.agent-base; + + # Strip the nix-store prefix from option declaration paths and rewrite + # them as forge URLs so the rendered docs link back to the source. + forgeRoot = "https://forge.darkest.space/hyperhive/hyperhive/src/branch/main"; + storePrefix = toString self + "/"; + transformOptions = + opt: + opt + // { + declarations = map ( + decl: + let + declStr = toString decl; + relPath = + if lib.hasPrefix storePrefix declStr then + lib.removePrefix storePrefix declStr + else + baseNameOf declStr; + in + { + url = "${forgeRoot}/${relPath}"; + name = relPath; + } + ) opt.declarations; + }; + + # Filter an evaluated `options` tree down to a set of top-level + # subtrees we care about. Anything outside the listed roots is + # dropped — keeps the rendered docs focused on hyperhive's surface + # instead of NixOS's 10k+ default options. + pickSubtrees = + options: roots: + let + walk = + path: tree: + if path == [ ] then + lib.getAttrFromPath path options + else + lib.setAttrByPath path (lib.getAttrFromPath path tree); + pick = + path: + let + exists = lib.hasAttrByPath path options; + in + if exists then lib.setAttrByPath path (lib.getAttrFromPath path options) else { }; + in + lib.foldl' lib.recursiveUpdate { } (map pick roots); + + hostOptions = pickSubtrees hostEval.options [ + [ "hyperhive" ] + [ + "services" + "hive-c0re" + ] + ]; + + agentOptions = pickSubtrees agentEval.options [ + [ "hyperhive" ] + ]; + + hostDoc = pkgs.nixosOptionsDoc { + options = hostOptions; + inherit transformOptions; + }; + + agentDoc = pkgs.nixosOptionsDoc { + options = agentOptions; + inherit transformOptions; + }; + + mkPage = + name: title: doc: + pkgs.runCommand "hyperhive-${name}-options.md" { } '' + { + echo "# ${title}" + echo + echo "" + echo + cat ${doc.optionsCommonMark} + } > $out + ''; +in +{ + host = mkPage "docs-host" "hyperhive — host options" hostDoc; + agent = mkPage "docs-agent" "hyperhive — per-agent options" agentDoc; + + # Bundle both pages plus a thin index so the whole thing can be + # published as a single static tree. + bundle = pkgs.runCommand "hyperhive-options-docs" { } '' + mkdir -p $out + cp ${mkPage "docs-host" "hyperhive — host options" hostDoc} $out/host.md + cp ${mkPage "docs-agent" "hyperhive — per-agent options" agentDoc} $out/agent.md + cat > $out/README.md <<'EOF' + # hyperhive — nix options reference + + auto-generated from the hyperhive flake. + + - [host.md](./host.md) — options exposed by `hyperhive.nixosModules.default` + to operator host configurations (`services.hive-c0re.*`, + `hyperhive.domain`, `hyperhive.forge.*`, `hyperhive.matrix.*`). + - [agent.md](./agent.md) — per-agent options declared in + `nix/templates/harness-base.nix` and visible from every `agent.nix` + (`hyperhive.model`, `hyperhive.allowedRecipients`, + `hyperhive.extraMcpServers`, `hyperhive.frontend.*`, + `hyperhive.forge.*`, `hyperhive.matrix.*`, `hyperhive.gui.*`). + + regenerate: + + ```sh + nix build .#docs # bundled tree + nix build .#docs-host # host page only + nix build .#docs-agent # agent page only + ``` + EOF + ''; +}