hyperhive/docs/ci.md
lexis 7f91d52569 docs: add ci.md to CLAUDE.md + clarify runner details
Follow-up to #905: adds CLAUDE.md index entries (file map + reading path)
for the new docs/ci.md. Also clarifies runner details in ci.md:
- curl/jq use absolute nix store paths (no systemPackages needed)
- .runner credential reuse: script writes dummy token on subsequent boots,
  runner ignores it when .runner file exists
2026-06-01 15:52:01 +02:00

3 KiB

hive-ci: Forgejo Actions Runner

The hive-ci module runs a Forgejo Actions runner in a hive-ci nixos-container, executing CI jobs from .forgejo/workflows/ci.yml (e.g., nix flake check on every PR).

Operator bootstrap

Set services.hyperhive.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).
  • Optional: tune services.hyperhive.ci.name (runner name in forge admin panel), concurrency (parallel job capacity), labels (workflow targeting).

Container design

  • Shared host netns: container reaches hive-forge at http://127.0.0.1:<httpPort> (same as hive-gateway).
  • Non-ephemeral: runner credentials persist across restarts (written to container's stateDir on first registration, reused thereafter).
  • Sandbox fallback: nspawn containers can't create user-namespaces, so nix's sandboxing would always fail. Module sets nix.settings.sandbox-fallback = true in the container — nix builds run unsandboxed (safe because the container is already isolated). See docs/gotchas.md.

Auto-registration flow

  1. On first container boot, systemd runs the ExecStartPre script (from hive-ci.nix:autoRegisterScript).
  2. Script checks for existing runner credentials (/var/lib/gitea-runner/hive/.runner):
    • If found: container was previously registered — write dummy token to satisfy module's path check, exit.
    • If not found: first boot — proceed to registration.
  3. Script reads hive-c0re's admin token from /run/hive-ci/core-token (bind-mounted from /var/lib/hyperhive/forge-core-token).
  4. Calls POST /api/v1/admin/runners/registration-token on the forge using ${pkgs.curl}/bin/curl and ${pkgs.jq}/bin/jq (absolute nix store paths — the container doesn't need these in systemPackages). Retries for 30s in case forge is still starting.
  5. Writes real token to /run/hive-ci/runner-token.
  6. gitea-actions-runner reads the token and registers itself, persisting credentials (.runner file) to stateDir.
  7. On subsequent boots, runner reuses the .runner credentials — the preStart script detects the existing .runner file and writes a dummy token instead. The runner ignores the token file when .runner already exists.

CI workflow

The single CI job is defined in .forgejo/workflows/ci.yml:

name: CI
on:
  pull_request:
    branches: ["**"]
jobs:
  check:
    name: nix flake check
    runs-on: [hive-ci]
    steps:
      - uses: actions/checkout@v3
      - name: check
        run: nix flake check

This runs on every PR, executing all flake checks (treefmt, rustfmt, cargo test, cargo clippy, module evaluation). No --no-build: the checks' derivations are the canonical source of truth.

References

  • nix/modules/hive-ci.nix: runner configuration, auto-registration script, container setup.
  • .forgejo/workflows/ci.yml: workflow definition.
  • docs/gotchas.md: nix sandboxing limitations in containers.