Adoption Quickstart
Deploy GloriousFlywheel on your cluster and enroll your first repo in 5 steps.
Prerequisites
- A Kubernetes cluster (RKE2, EKS, GKE, k3s, etc.)
kubectlconfigured for your cluster- OpenTofu >= 1.6.0
- A GitHub App (for GitHub adapter) or runner registration token (GitLab/Forgejo)
Step 1: Deploy Core Substrate
Clone the repo and deploy the ARC controller:
git clone https://github.com/tinyland-inc/GloriousFlywheel.git cd GloriousFlywheel
Copy and edit the example config
cp config/organization.example.yaml config/organization.yaml
Edit with your cluster details
Deploy the ARC controller (one per cluster)
just tofu-init arc-runners just tofu-plan arc-runners just tofu-apply arc-runners
Step 2: Enroll Your Forge
Before installing forge credentials, decide where the implementation overlay lives. The core GloriousFlywheel repo should stay reusable. Your overlay owns deployment values: GitHub App installation IDs, private keys, tfvars, backend state config, cache namespaces, and private endpoint names.
GitHub (Primary)
-
Create a GitHub App at your org level
-
Install it on the repos or org you want to serve
-
Store the app credentials in your implementation overlay secret path:
GITHUB_APP_ID=… GITHUB_APP_INSTALLATION_ID=… GITHUB_APP_PRIVATE_KEY_PATH=…
-
Configure the overlay-owned tfvars with your org URL:
github_config_url = ”https://github.com/your-org”
For a personal GitHub account, GitHub does not provide an org-style personal-account runner group. A repo URL may be required as a private ARC registration anchor, but keep that anchor in the implementation overlay and keep workflow labels capability-shaped.
GitLab (Compatibility)
Forgejo/Codeberg (Proof Path)
Step 3: Choose Shared Capability Classes
Default capability classes:
| Label | CPU/Mem | Use Case |
|---|---|---|
your-org-docker |
2/4Gi | General CI |
your-org-nix |
4/8Gi | Nix builds |
your-org-nix-heavy |
8/16Gi | Rust + Nix |
Configure these as shared capability classes in your tfvars. Additive classes should represent real runtime, privilege, architecture, or bounded resource differences, not repo identity.
Do not treat repo-specific labels as the normal design. If a repo cannot reach the shared lane you want, solve that as control-plane or enrollment debt underneath the shared contract.
Step 4: Attach Caches
Caches are first-class substrate components. They should be wired once and then shared by local development, CI, and runner jobs through the same variables. They are not publication authority, and they should not require repo-specific runner labels.
Attic (Nix binary cache)
just tofu-init attic just tofu-plan attic just tofu-apply attic
Nix-capable runner jobs should receive ATTIC_SERVER and ATTIC_CACHE.
Credentialed write paths may also provide an Attic token, but read-side cache
attachment and publication/discovery are separate concerns.
For local direnv/flake sessions, use just cache-contract-nix-strict after
the shell loads. It fails unless NIX_CONFIG carries both the configured Attic
substituter and the public trust key.
Bazel Remote Cache
Set BAZEL_REMOTE_CACHE through the runner/bootstrap environment so Bazel work
participates in the same shared substrate as Nix and CI. Do not hard-code
deployment-specific endpoints into downstream repositories.
Current internal truth for this repo:
- self-hosted runners inject
BAZEL_REMOTE_CACHEwhen the cache service is reachable from the runner context - developer machines enter shared-cache-backed mode only when an
operator-provided routable
BAZEL_REMOTE_CACHEis set and the strict cache-contract check passes - if
BAZEL_REMOTE_CACHEis unset, Bazel stays incompatibility-local-onlymode and is not proving the intended small-machine heavy-work story
Step 5: Use In Workflows
In your repo’s .github/workflows/*.yml:
jobs: build: runs-on: your-org-nix # or another shared capability class steps:
- uses: actions/checkout@v6
- uses: tinyland-inc/GloriousFlywheel/.github/actions/nix-job@main env: ATTIC_TOKEN: ${{ secrets.ATTIC_TOKEN || ” }} GF_BAZEL_REMOTE_UPLOAD: ${{ github.event_name == ‘push’ && github.ref == ‘refs/heads/main’ && ‘true’ || ‘false’ }} with: attic-public-key: ${{ vars.ATTIC_PUBLIC_KEY || ” }} command: | test -n ”${BAZEL_REMOTE_CACHE:-}” test ”${GF_BAZEL_SUBSTRATE_MODE:-}” = “shared-cache-backed” nix build .#default nix develop -c bash ./scripts/gloriousflywheel-bazel.sh build //… push-cache: ${{ github.event_name == ‘push’ && github.ref == ‘refs/heads/main’ && secrets.ATTIC_TOKEN != ” && ‘true’ || ‘false’ }}
For the shared public-read main cache, ATTIC_PUBLIC_KEY is enough to attach
the Nix substituter. ATTIC_TOKEN is still required for trusted push writes
and private-cache reads. The composite action maps the token into an ephemeral
Nix netrc-file; do not model Attic substituter reads as Nix
access-tokens. Set attic-enabled: "false" only when the cache should not
be attached at all.
The current pilot shape keeps pull requests read-only and only enables Attic
publication on trusted default-branch pushes when ATTIC_TOKEN is present.
GloriousFlywheel’s own broad proof workflows still keep push-cache: "false"
unless they are a named publication probe or an explicitly restored write path.
Prefer making the bootstrap contract explicit in workflows. Raw nix build
and raw Bazel commands are only truthful if the runner image or earlier steps
have already installed the required toolchain and exposed the shared
cache-backed endpoint. Prefer the nix-job composite action or an equivalent
bootstrap step that proves BAZEL_REMOTE_CACHE and
GF_BAZEL_SUBSTRATE_MODE=shared-cache-backed before Bazel runs. Bazel rc
files do not expand shell environment variables, so pass
--remote_cache="$BAZEL_REMOTE_CACHE" from a repo-managed wrapper such as
scripts/gloriousflywheel-bazel.sh. Package pull requests should use
read-only remote-cache behavior; trusted default-branch pushes may opt into
Bazel cache uploads with GF_BAZEL_REMOTE_UPLOAD=true.
For local development, enter through direnv or nix develop, run
just info, and only use Bazel dogfood after just cache-contract-strict
passes.
Validating Enrollment
Run the enrollment check:
./scripts/benchmark/canary-enrollment-check.sh your-org/your-repo your-org-nix
The checker uses both repo runner inventory and recent successful job-label evidence, because scale-to-zero or shared runners may not appear in the repo inventory endpoint while idle.
Measuring Performance
Run the benchmark harness:
./scripts/benchmark/runner-benchmark.sh your-org-nix nix-build