Builder Contract

Linux Builder and Publication Contract

Defines builder classes, cache responsibilities, derivation publication policy, and the canary validation lane for GloriousFlywheel CI.

Builder Classes

Class Runner Image Capabilities Use Cases
nix-builder gh-nix ghcr.io/tinyland-inc/actions-runner-nix Determinate Nix, Attic client, cache/runtime hints Flake builds, checks, NixOS tests, OCI image builds
general gh-docker ubuntu-latest equiv Standard CI tooling, pnpm, Node.js Unit tests, linting, type checking, markdown validation
container gh-dind docker:dind Docker BuildKit, multi-stage builds OCI image builds, container integration tests
kernel linux-xr-docker rockylinux:10 GCC toolchain, kernel headers linux-xr kernel module builds

Builder Responsibilities

nix-builder (gh-nix):

  • Evaluate and build Nix derivations
  • Push build results to Attic binary cache
  • Run nix flake check for formatting, linting, dead code analysis
  • Build OCI images via nix2container
  • Publish flake to FlakeHub on release

general (gh-docker):

  • Run SvelteKit app tests (vitest), type checks, linting
  • Run MCP server tests
  • Validate OpenTofu modules (tofu validate)
  • Check documentation links and markdown formatting

container (gh-dind):

  • Build and push container images with BuildKit registry cache
  • Run container-based integration tests
  • Produce OCI artifacts for deployment

kernel (linux-xr-docker):

  • Compile linux-xr kernel modules against specific kernel versions
  • Restricted to max 2 concurrent runners (resource-heavy builds)

Cache Hierarchy

Three independent cache systems serve different build ecosystems. Each has a clear authority boundary.

+-- FlakeHub (public publication)
|   Authority: public flake distribution
|   Content: released flake versions (tagged + main)
|   Consumers: external users, downstream flake inputs
|   Populated by: .github/workflows/flakehub-publish.yml
|   Auth: OIDC (no secrets)
|
+-- Attic (private binary cache)
|   Authority: internal Nix build artifacts
|   Content: all CI-built derivations, dev shell closures
|   Consumers: CI runners, dev machines
|   Populated by: nix-builder post-build push
|   Auth: Attic signing key per machine/runner
|
+-- Bazel Remote Cache (build action cache)
    Authority: Bazel action results and CAS
    Content: compiled outputs, test results, validated configs
    Consumers: CI runners, local dev (via remote cache)
    Populated by: any Bazel build/test invocation
    Auth: mTLS or token (cluster-internal)

What Goes Where

Artifact Cache Reason
Nix derivation (any) Attic Internal build cache, fast CI rebuilds
Released flake FlakeHub Public distribution, semantic versioning
Bazel action output Bazel Remote Cache Action-level caching, content-addressed
Container layer cache BuildKit Registry Cache Docker-specific layer dedup
Final OCI image Container Registry (GHCR/GitLab CR) Deployment artifact, not cache

Cache Miss Behavior

Cache On Miss Impact
Attic Build from source, push result Slow first build (~5-10 min for full closure)
FlakeHub N/A (publication, not pull-through) Consumer gets older published version
Bazel Re-execute action, cache result Moderate slowdown (~30s-2min per action)
BuildKit Rebuild all layers Slow container build (~10-30 min)

Clean Derivation Policy

What Gets Published

A derivation is publishable (pushed to Attic or FlakeHub) when:

  1. It builds without --impure flag
  2. It passes nix flake check in CI
  3. It does not reference /nix/store paths from the local machine
  4. It does not embed secrets, tokens, or machine-specific paths

Publication Triggers

Event FlakeHub Attic Notes
Push to main Yes (rolling) Yes (all outputs) FlakeHub gets stable channel
Tag (v*) Yes (versioned) Yes (all outputs) FlakeHub gets semver release
PR CI run No Yes (test outputs only) Attic caches to speed up re-runs
Local dev build No Optional (manual push) Developer opts in with attic push

Exposure Policy

Consumer Can Access FlakeHub? Can Access Attic? Can Access Bazel Cache?
External user Yes (public) No No
CI runner No (uses Attic) Yes (direct) Yes (cluster-internal)
Dev machine Yes (as flake input) Yes (with signing key) Yes (with remote config)
Dashboard app No No No

Canary Validation Lane

A dedicated CI job validates builder health before production workloads run.

Canary Job: validate-builders

Runs on every push to main, before deployment workflows:

validate-builders:
  name: Validate Builder Health
  runs-on: [self-hosted, nix]
  steps:
    - uses: actions/checkout@v4

    # Verify Nix builder can evaluate and build
    - name: Nix eval smoke test
      run: nix eval .#packages.x86_64-linux --json | head -c 100

    # Verify Attic cache is reachable
    - name: Attic connectivity check
      run: attic cache info gloriousflywheel || echo "WARN: Attic unreachable"

    # Verify the runner image exposes the Nix toolchain
    - name: Nix toolchain health
      run: |
        command -v nix
        nix --version

    # Build a minimal derivation to verify end-to-end
    - name: Canary build
      run: nix build .#packages.x86_64-linux.default --no-link

Canary Failure Response

If validate-builders fails:

  1. Deployment workflows are blocked (GitHub Actions needs: dependency)
  2. Alert fires via Prometheus (ARCRunnerColdStartSlow if it’s a timing issue)
  3. Operator investigates via dashboard or MCP tools
  4. Fix the builder, re-run the canary

Downstream Consumer Guide

“How do I use GloriousFlywheel packages?”

As a Nix flake input (external consumer):

{
  inputs.gloriousflywheel.url = "https://flakehub.com/f/tinyland-inc/GloriousFlywheel/*";
}

FlakeHub resolves to the latest published version. No Attic access needed.

As a CI job (internal consumer):

runs-on: [self-hosted, nix] # Routes to gh-nix runner
steps:
  - run: nix build .#myPackage
    # Runner image carries Nix; Attic acceleration is automatic via runtime hints

As a dev machine (internal consumer):

# One-time: configure Attic
attic login gloriousflywheel https://attic.tinyland.dev
attic use gloriousflywheel

# Then: nix builds automatically check Attic before building from source
nix build .#myPackage

GloriousFlywheel