diff --git a/docs/approvals.md b/docs/approvals.md index b60fad63..4d27167e 100644 --- a/docs/approvals.md +++ b/docs/approvals.md @@ -358,10 +358,9 @@ queue but skip the approval row plumbing. ### Forge mirror -When the bundled `hive-forge` container is running — on by -default, `hyperhive.forge.enable` — hive-c0re mirrors every -agent's applied repo into a private `agent-configs` Forgejo -org. `forge::push_config()` pushes `applied/main` plus +The bundled `hive-forge` container is mandatory (it deploys with +hyperhive), and hive-c0re mirrors every agent's applied repo into a +private `agent-configs` Forgejo org. `forge::push_config()` pushes `applied/main` plus every tag to `agent-configs/` after each ref mutation: the spawn that seeds `deployed/0`, every `request_apply_commit` (which plants `proposal/`), every approve / deny, and a diff --git a/docs/ci.md b/docs/ci.md index e93dc266..6c34ddb9 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -7,7 +7,7 @@ The `hive-ci` module runs a Forgejo Actions runner in a `hive-ci` nixos-containe Set `services.hyperhive.forge.ci.enable = true` in the host NixOS config. That's it — no manual token provisioning. **Requirements:** -- `services.hyperhive.forge.enable = true` must also be set (the runner registers against hive-forge). +- The internal forge is always present (mandatory), so the runner always has a hive-forge instance to register against — nothing extra to enable. - Optional: tune `services.hyperhive.forge.ci.name` (runner name in forge admin panel), `concurrency` (parallel job capacity), `labels` (workflow targeting), `jobTimeout` (per-job wall-clock cap, default `"1h"`, Go duration string e.g. `"3h"` — a job that exceeds it is killed so a hung or runaway build can't hold the runner's single slot indefinitely). ## Container design diff --git a/hive-c0re/src/bin/hivectl.rs b/hive-c0re/src/bin/hivectl.rs index a7c1c25d..4379f1bf 100644 --- a/hive-c0re/src/bin/hivectl.rs +++ b/hive-c0re/src/bin/hivectl.rs @@ -1019,7 +1019,7 @@ fn choom(name: &str, fresh: bool) -> Result<()> { async fn forge_create_user(name: &str, password: Option<&str>, password_stdin: bool) -> Result<()> { if !hive_c0re::forge::is_present().await { bail!( - "hive-forge container not running — start it (services.hyperhive.forge.enable = true) before provisioning forge users" + "hive-forge container not running — wait for hive-c0re to start it before provisioning forge users" ); } let user_password = resolve_password(password, password_stdin)?; diff --git a/hive-c0re/src/forge.rs b/hive-c0re/src/forge.rs index d0f4f8eb..ef519352 100644 --- a/hive-c0re/src/forge.rs +++ b/hive-c0re/src/forge.rs @@ -407,7 +407,7 @@ pub async fn ensure_user_for(name: &str) -> Result<()> { pub async fn provision_user_token(name: &str, password: Option<&str>) -> Result { if !is_present().await { anyhow::bail!( - "hive-forge container not running — start it (services.hyperhive.forge.enable = true) before provisioning forge users" + "hive-forge container not running — wait for hive-c0re to start it before provisioning forge users" ); } ensure_user_exists(name, false, password).await?; @@ -661,7 +661,7 @@ pub fn core_token() -> Option { /// Push `dir` (the meta repo) to `core/meta` on the local forge. /// Best-effort: returns Err which callers log + ignore. No-op when -/// the core token isn't present (forge not enabled). +/// the core token isn't present yet (forge container not provisioned). pub async fn push_meta(dir: &Path) -> Result<()> { let Some(token) = core_token() else { return Ok(()); diff --git a/nix/docs/default.nix b/nix/docs/default.nix index 90edb099..62e618d8 100644 --- a/nix/docs/default.nix +++ b/nix/docs/default.nix @@ -29,7 +29,6 @@ let boot.loader.grub.enable = false; system.stateVersion = "25.11"; services.hyperhive.enable = lib.mkForce false; - services.hyperhive.forge.enable = lib.mkForce false; services.hyperhive.matrix.enable = lib.mkForce false; } ) diff --git a/nix/modules/hive-c0re.nix b/nix/modules/hive-c0re.nix index 4d440a35..0f9ee5c8 100644 --- a/nix/modules/hive-c0re.nix +++ b/nix/modules/hive-c0re.nix @@ -109,11 +109,11 @@ let if stylixThemeColors != null then themedFrontend stylixThemeColors else cfg.frontend; in { - # The forge is part of the standard install — hive-c0re mirrors - # every agent's applied config repo into it. On by default; opt out - # with `services.hyperhive.forge.enable = false`. hive-matrix is - # opt-in (off by default) and asserts that `services.hyperhive.domain` - # is set before it can be enabled. + # The forge is mandatory — hive-c0re mirrors every agent's applied + # config repo into it and it's the canonical store for the meta flake + # + `internal/*` repos, so there's no enable toggle; it deploys with + # hyperhive itself. hive-matrix is opt-in (off by default) and asserts + # that `services.hyperhive.domain` is set before it can be enabled. imports = [ ./hive-ci.nix ./hive-forge.nix @@ -790,10 +790,11 @@ in // lib.optionalAttrs (config.services.hyperhive.swarmName != null) { HYPERHIVE_SWARM_NAME = config.services.hyperhive.swarmName; } - // lib.optionalAttrs config.services.hyperhive.forge.enable { + // { # In-cluster forge URL — the gateway vhost (`forge.`), which - # nginx proxies to forgejo. Set directly: this env only exists when - # hyperhive is enabled. See `docs/gateway.md::HIVE_FORGE_URL`. + # nginx proxies to forgejo. The forge is mandatory, so this is + # unconditional (the whole env block is already gated on hyperhive + # being enabled). See `docs/gateway.md::HIVE_FORGE_URL`. HIVE_FORGE_URL = "http://${config.services.hyperhive.forge.domain}"; } // lib.optionalAttrs config.services.hyperhive.matrix.enable { @@ -823,19 +824,16 @@ in # dashboard doesn't need to learn the gateway is unconditional. HIVE_GATEWAY_ENABLED = "1"; } - // - lib.optionalAttrs - (config.services.hyperhive.forge.enable && config.services.hyperhive.forge.behindGateway) - { - # Public URL of the forge vhost served by hive-gateway. The - # dashboard uses this to build browser-facing forge links - # instead of hardcoding `:3000`, which breaks when - # the operator accesses the dashboard through the gateway - # (forge sub-domain has no port; direct port URL would be - # wrong). Absent when `behindGateway = false` — dashboard - # falls back to `:3000`. - HIVE_FORGE_PUBLIC_URL = "https://${config.services.hyperhive.forge.domain}"; - } + // lib.optionalAttrs config.services.hyperhive.forge.behindGateway { + # Public URL of the forge vhost served by hive-gateway. The + # dashboard uses this to build browser-facing forge links + # instead of hardcoding `:3000`, which breaks when + # the operator accesses the dashboard through the gateway + # (forge sub-domain has no port; direct port URL would be + # wrong). Absent when `behindGateway = false` — dashboard + # falls back to `:3000`. + HIVE_FORGE_PUBLIC_URL = "https://${config.services.hyperhive.forge.domain}"; + } // lib.optionalAttrs (config.services.hyperhive.swarm.peers != { }) { # Peer hives serialised as a JSON array of {domain, cert_fingerprint, # wireguard_address?} objects. Consumed by hive-ag3nt::identity::peers() diff --git a/nix/modules/hive-ci.nix b/nix/modules/hive-ci.nix index a8f0012c..47f45906 100644 --- a/nix/modules/hive-ci.nix +++ b/nix/modules/hive-ci.nix @@ -193,8 +193,8 @@ in Run a Forgejo Actions runner in a `hive-ci` nixos-container. Grouped under `services.hyperhive.forge` because the runner is tightly coupled to the forge instance it registers against. - Disabled by default; `services.hyperhive.forge.enable = true` is - a prerequisite (enforced by assertion). + Disabled by default; the internal forge it registers against is + always present (mandatory), so enabling this is all that's needed. On first start the container auto-registers against hive-forge using hive-c0re's admin token — no manual token provisioning needed. @@ -265,16 +265,9 @@ in }; config = lib.mkIf cfg.enable { - assertions = [ - { - assertion = forgeCfg.enable; - message = '' - services.hyperhive.forge.ci.enable = true requires - services.hyperhive.forge.enable = true — the runner registers - against the hive-forge Forgejo instance. - ''; - } - ]; + # No forge-presence assertion needed: the internal forge is mandatory + # (deploys with hyperhive), so the runner always has an instance to + # register against. # Create /run/hive-ci/ on the host and seed runner-token with a # placeholder. hive-ci-prefetch.service overwrites it with the real diff --git a/nix/modules/hive-forge.nix b/nix/modules/hive-forge.nix index db63ebd9..ae0bd45c 100644 --- a/nix/modules/hive-forge.nix +++ b/nix/modules/hive-forge.nix @@ -41,19 +41,11 @@ in # `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/` survives # restart. See `docs/gateway.md::hive-forge container shape`. + # The internal forge is mandatory — it's the canonical store for the + # meta flake + every agent's config repo (and the `internal/*` repos), + # so there is no enable/disable toggle. It deploys whenever hyperhive + # itself is enabled (`services.hyperhive.enable`). options.services.hyperhive.forge = { - enable = lib.mkOption { - type = lib.types.bool; - default = true; - description = '' - Run hive-forge — a private Forgejo (in a nixos-container) for - hyperhive agents. On by default: hive-c0re mirrors every - agent's applied config repo into the forge's `agent-configs` - org, so the forge is part of the standard install. Set - `services.hyperhive.forge.enable = false` to opt out. - ''; - }; - httpPort = lib.mkOption { type = lib.types.port; default = 3000; @@ -192,7 +184,7 @@ in }; }; - config = lib.mkIf cfg.enable { + config = lib.mkIf config.services.hyperhive.enable { assertions = [ { assertion = cfg.rootUrl == null || lib.hasSuffix "/" cfg.rootUrl; diff --git a/nix/modules/hive-gateway.nix b/nix/modules/hive-gateway.nix index e88c32ad..fab36d62 100644 --- a/nix/modules/hive-gateway.nix +++ b/nix/modules/hive-gateway.nix @@ -1019,9 +1019,7 @@ in networking.hosts = lib.mkIf (cfg.localHostsEntry && hyperhiveDomain != null) { "127.0.0.1" = lib.unique ( [ hyperhiveDomain ] - ++ lib.optional ( - (config.services.hyperhive.forge.enable or false) - && (config.services.hyperhive.forge.behindGateway or false) + ++ lib.optional (config.services.hyperhive.forge.behindGateway or false ) config.services.hyperhive.forge.domain ++ lib.optional (matrixCfg.enable && matrixCfg.gatewayHost != null) matrixCfg.gatewayHost );