diff --git a/hive-c0re/src/meta.rs b/hive-c0re/src/meta.rs index a7d833f3..6ae393f2 100644 --- a/hive-c0re/src/meta.rs +++ b/hive-c0re/src/meta.rs @@ -1041,6 +1041,14 @@ where out.push_str(" hyperhive.otel.debug = true;\n"); } } + // GitHub integration is on by default in every agent + // (`hyperhive.github.enable`); the host turns it off hive-wide via + // `services.hyperhive.github.enable = false`, surfaced here as the + // `HYPERHIVE_GITHUB_DISABLED` env on hive-c0re's unit. Only the OFF + // override is propagated — the enabled default needs no per-agent line. + if std::env::var_os("HYPERHIVE_GITHUB_DISABLED").is_some() { + out.push_str(" hyperhive.github.enable = false;\n"); + } out.push_str( r#" # The harness service inside the container runs as a # non-root unix user named after the agent (`damocles`, @@ -1745,4 +1753,44 @@ mod tests { "no otel lines when disabled:\n{off}" ); } + + #[test] + fn render_flake_injects_github_disable_only_when_signalled() { + // services.hyperhive.github.enable = false -> HYPERHIVE_GITHUB_DISABLED + // on hive-c0re's unit -> `hyperhive.github.enable = false` injected into + // every agent. On by default, so nothing is emitted unless disabled. + // + // SAFETY: single-threaded mutation of an env var no other test asserts + // on; restored before returning. + let render = || { + render_flake( + "github:example/hyperhive", + "path:/nix/store/bbbb-hyperhive-docs-source", + "path:/nix/store/aaaa-nixpkgs-source", + 8000, + "she/her", + &std::collections::HashMap::new(), + &[sample_spec("alice", false, 9001)], + ) + }; + unsafe { + std::env::remove_var("HYPERHIVE_GITHUB_DISABLED"); + } + let on_default = render(); + unsafe { + std::env::set_var("HYPERHIVE_GITHUB_DISABLED", "1"); + } + let disabled = render(); + unsafe { + std::env::remove_var("HYPERHIVE_GITHUB_DISABLED"); + } + assert!( + !on_default.contains("hyperhive.github.enable"), + "github.enable must not be emitted by default (agents keep the true default):\n{on_default}" + ); + assert!( + disabled.contains("hyperhive.github.enable = false;"), + "github.enable = false must be injected when the host disables it:\n{disabled}" + ); + } } diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 184c63a8..0bdaaa5c 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -215,6 +215,22 @@ in ''; }; + options.services.hyperhive.github.enable = lib.mkOption { + type = lib.types.bool; + default = true; + example = false; + description = '' + Hive-wide switch for the per-agent GitHub integration (the `gh` CLI + wrapper + git credential helper, per `hyperhive.github.enable`). On by + default: every agent gets the integration, inert until a PAT is + provisioned via the dashboard credentials tab or `hivectl github + set-token`. Set `false` to turn it off for the whole hive --- the + meta-flake renderer (`hive-c0re/src/meta.rs`) then injects + `hyperhive.github.enable = false` into every agent. Exposed to hive-c0re + as `HYPERHIVE_GITHUB_DISABLED` (set only when the integration is off). + ''; + }; + # Hive-wide OTEL stats export. Set ONCE here at host level; the # meta-flake renderer (`hive-c0re/src/meta.rs::otel_config`) reads the # HYPERHIVE_OTEL_* env exported below off hive-c0re's unit and injects @@ -956,6 +972,12 @@ in // lib.optionalAttrs (config.services.hyperhive.swarmName != null) { HYPERHIVE_SWARM_NAME = config.services.hyperhive.swarmName; } + // lib.optionalAttrs (!config.services.hyperhive.github.enable) { + # GitHub integration is on by default; only signal the OFF override to + # meta.rs, which then injects `hyperhive.github.enable = false` into + # every agent. See services.hyperhive.github.enable. + HYPERHIVE_GITHUB_DISABLED = "1"; + } // lib.optionalAttrs config.services.hyperhive.otel.enable ( # Hive-wide OTEL config -> read by meta.rs::otel_config and # injected as build-time `hyperhive.otel.*` into every agent.