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.yamlinto 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.
curl -fsSL https://raw.githubusercontent.com/buildkite/cleanroom/main/scripts/install.sh | bashThis 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@latestCleanroom 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 verifyA 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"| 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 serveBy 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]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 verifyis integrity and UX, not the security boundary. - Provenance rides in SporeVM annotations and is merged into every
snapshot, so a
.sporehanded 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.
- Policy reference
- SporeVM layer plan — the design and its slices
- CI setup
- Basic example