Skip to content

buildkite/cleanroom

Repository files navigation

👩‍🔬 Cleanroom

Build status Release License

Cleanroom turns a repository's declared policy into a repeatable warm spore — a microVM snapshot with dependencies installed and caches hot, provenance attached, and deny-by-default network policy enforceable wherever it runs.

Cleanroom is a thin layer over SporeVM. SporeVM owns the runtime: VM lifecycle, snapshots, copy-on-write fork and fan-out, and network enforcement. Cleanroom owns the three things SporeVM has no opinion about:

  • compile — translate cleanroom.yaml into enforceable SporeVM configuration, failing closed on anything SporeVM cannot enforce.
  • provenance — attach checkable facts (repo, commit, policy hash, image digest) to every baked artifact, and verify them later.
  • mediation — a host-side gateway that brokers credentials into the guest so secrets never enter the sandbox or its captured artifact.

Install

curl -fsSL https://raw.githubusercontent.com/buildkite/cleanroom/main/scripts/install.sh | bash

This installs the single cleanroom binary into /usr/local/bin. Use --version, --install-dir, or CLEANROOM_INSTALL_DIR to customise.

Cleanroom drives the spore CLI, so install SporeVM too:

mise use -g github:sporevm/sporevm@latest

Workflow

Cleanroom is the build tool; spore is the run tool.

# Bake a warm spore from a repo's cleanroom.yaml (deps installed via warmup)
cleanroom bake . --out repo.spore

# Run it — starts the gateway and command cache env when provenance requires them
cleanroom run repo.spore --dir . -- /bin/sh -lc 'make test'

# Fan out copy-on-write children that share the parent's memory and disk
spore fork repo.spore --count 100 --out agents/

# Verify an artifact's provenance and see what it needs to run
spore --json inspect repo.spore | cleanroom verify

A minimal cleanroom.yaml:

version: 1
sandbox:
  image:
    ref: ghcr.io/buildkite/cleanroom-base/alpine@sha256:...
  resources:
    memory: 1gb
  network:
    default: deny
    allow:
      - host: dl-cdn.alpinelinux.org
        ports: [443]
  warmup:
    - "apk add --no-progress make"

Commands

Command Purpose
cleanroom bake [dir] --out <spore> Compile policy, boot a builder, run warmup, capture a warm spore
cleanroom run <spore> --dir <repo> -- <argv...> Verify a warm spore, start the gateway if needed, then run it with SporeVM
cleanroom content-cache serve Serve a persistent host content-cache for gateway-backed runs
cleanroom compile [dir] Emit spore create arguments from policy (fail-closed)
cleanroom stamp [dir] Emit provenance annotations as spore create arguments
cleanroom verify [spore-dir] Verify provenance; audit the bake key against a repo with --dir
cleanroom gateway serve Serve the lineage credential-mediation gateway on a Unix socket
cleanroom policy validate Validate cleanroom.yaml

compile and stamp are the composable plumbing behind bake; use them to drive spore directly or in CI policy checks.

cleanroom run is the command-launch convenience layer. After it audits the spore's bake key against the current repository policy, if that policy requests the content-cache mediation service and HTTPS allow rules, it binds the gateway and wraps the command with Git and Go environment pointing at http://cleanroom-gateway.spore.internal:8170/services/content-cache/.... cleanroom bake does not mutate guest config for package managers; it only captures the warm spore and stamps policy/provenance.

For the common content-cache-only case, cleanroom run checks 127.0.0.1:8128/health and starts a child content-cache service for the run if one is not already available. The backing storage is still shared on disk across runs. To prewarm, debug, or manage the cache yourself, run:

cleanroom content-cache serve

By default it listens on 127.0.0.1:8128 and stores data under the user's cache directory at cleanroom/content-cache. Git caching allows github.com by default; use --git-allowed-hosts for other Git hosts. The run-scoped child cache is started with host allowlists derived from the audited policy, and the temporary gateway only forwards the configured cache route prefixes. For non-cache mediation services, or custom cache upstreams, grant services through the gateway config:

services:
  content-cache:
    upstream: http://127.0.0.1:8128
grants:
  - match: { remote: "https://github.com/buildkite/*" }
    services: [content-cache]

How it fits together

cleanroom bake ─▶ spore create ─▶ copy-in ─▶ warmup ─▶ spore save --out ... --stop ─▶ repo.spore
                                                │                         │
                        credentials via gateway ┘        provenance annotations
  • Enforcement lives in the spore manifest. Network rules are applied by SporeVM on every resume and fork, regardless of who invokes it. cleanroom verify is integrity and UX, not the security boundary.
  • Provenance rides in SporeVM annotations and is merged into every snapshot, so a .spore handed to you is traceable to the repo and policy that produced it.
  • The gateway is scoped to a spore lineage — a baked spore and all its forks share one gateway; authorization is by the bound socket, attribution by guest-presented identity. Secrets stay host-side.

Documentation

About

Policy-controlled microVM sandboxes for safe, reproducible repo workloads.

Topics

Resources

License

Stars

55 stars

Watchers

2 watching

Forks

Packages

 
 
 

Contributors