diff --git a/nix/host-modules/hive-ci.nix b/nix/host-modules/hive-ci.nix index 9907cbdc..adede597 100644 --- a/nix/host-modules/hive-ci.nix +++ b/nix/host-modules/hive-ci.nix @@ -51,30 +51,26 @@ let in { # Forgejo Actions runner in a `hive-ci` nixos-container. - # Uses a private network namespace (bridge-connected, not host netns) - # so CI build scripts cannot reach host-loopback services (dashboard, - # forge internal port, etc.) — a key defence against prompt-injection - # via PR nix builds. The runner reaches the forge via the - # gateway at `http://${forgeCfg.domain}` (resolved to the bridge IP - # via `networking.extraHosts`; gateway port 80 is always open on the - # bridge; `addSSL = true` means HTTP is served alongside HTTPS without - # a redirect). See docs/networking/network.md. - # Container is non-ephemeral: the runner's registered credentials - # survive restarts (gitea-actions-runner writes them to its stateDir - # on first registration and reuses them on every subsequent start). # - # Credential isolation: the forge admin token (`forge-core-token`) - # never enters the hive-ci container. hive-c0re holds it and performs - # all forge API calls (runner validation + registration-token mint, - # in `forge/ci_runner.rs`); via hive-priv it writes only the runner - # registration token to the host env-file `/run/hive-ci/runner-token`, - # which the container bind-mounts read-only. The container never has - # access to the wider admin token. + # ⚠️ Two properties here are load-bearing and neither is visible from the + # config that implements it: # - # Nix builds inside the container use the shared /nix/store (standard - # nixos-container behaviour) with sandbox-fallback = true, because - # nspawn containers can't create the user-namespaces that nix sandboxing - # requires. See docs/process/gotchas.md. + # - The container gets its OWN network namespace, not the host's, so a + # PR's nix build cannot reach host-loopback services. That is a + # prompt-injection boundary; moving this to host netns for convenience + # removes it. Reachability of the forge from inside: + # `docs/networking/network.md::Container isolation`. + # - The forge ADMIN token never enters this container — hive-c0re holds + # it and hands in only a registration token. Full flow: + # `docs/scheduler/ci.md::Container design`. + # + # Non-ephemeral on purpose: gitea-actions-runner writes its registered + # credentials to its stateDir on first registration and reuses them, so an + # ephemeral container would re-register on every restart. + # + # Nix builds here use the shared /nix/store with `sandbox-fallback = true` + # — nspawn containers cannot create the user namespaces nix sandboxing + # wants: `docs/process/gotchas.md`. options.services.hyperhive.deploy.forgejo.ci = { enable = lib.mkOption {