Skip to content

Releases: buildkite/cleanroom

v0.11.0

06 Jun 10:44
@lox lox
e491fe1

Choose a tag to compare

Cleanroom v0.11.0 focuses on repo-aware sandbox creation, explicit system warmup, and another round of gateway and policy hardening.

Remote callers can now create sandboxes from a repository checkout without compiling and sending a policy themselves. When a CreateSandboxRequest includes repository_checkout without policy, Cleanroom resolves the checkout to a concrete commit, loads cleanroom.yaml or .buildkite/cleanroom.yaml from that commit, compiles it, and continues through the normal sandbox creation path. The client API also exposes repository checkout/change types, LoadPolicy, and repository fields on EnsureSandboxOptions.

A new warmup command lets operators pay first-use darwin-vz setup cost before release validation or local development:

cleanroom system warmup --backend darwin-vz

The warmup path resolves the managed kernel, prepares the default image-derived runtime rootfs when needed, and reports the resulting kernel/rootfs state. Unsupported backends fail clearly.

Security hardening from the DeepSec follow-up is included across the gateway, DNS, repository, cache, and policy paths. Cleanroom now rejects authority-shaped policy hosts and embedded Git gateway ports, gates DNS queries before forwarding, scopes OCI cache entries by repository, avoids unnecessary JWKS refreshes for unknown key IDs, sanitizes retained execution output, bounds Git mirror command output, and restores gateway/darwin-vz policy state after execution.

Other highlights:

  • Protobuf schemas are now linted and published to the Buf Schema Registry from CI.
  • Example smoke tests and macOS Go tests are reported through Buildkite Test Engine.
  • The getting started guide, control-plane docs, CI docs, and Buf schema registry links were expanded.
  • Agent image dependencies were refreshed.

Upgrade notes:

  • Repository checkout policy loading is additive; callers that already send an explicit compiled policy can keep doing so.
  • cleanroom system warmup is optional. Normal sandbox creation still performs lazy setup if warmup has not run.
  • Policy host entries that relied on authority-shaped hosts or embedded gateway ports should be updated to the stricter host form.

Full changelog:

v0.10.0...v0.11.0

v0.10.0

30 May 05:23
@lox lox
84ee69f

Choose a tag to compare

Cleanroom v0.10.0 focuses on shared-server safety and sandbox lifecycle management.

This release adds OIDC-backed authorization for remote Cleanroom servers, with policy checks that bind tokens to Cleanroom principals and enforce owner-scoped access to sandboxes, executions, snapshots, file operations, streams, and port forwarding. It also tightens remote listener defaults: non-loopback control-plane listeners now require auth.required=true, and bearer auth is rejected over non-loopback plain HTTP.

Sandbox lifecycle support is now available across the supported VM backends. Operators can manually suspend and resume sandboxes, enable opt-in idle suspension in runtime config, and rely on guest-touching operations to wake suspended sandboxes automatically.

Security hardening from the DeepSec review is included across gateway, cache, image handling, listener, and workspace boundaries.

Other highlights:

  • cleanroom auth check validates token and policy decisions before rollout.
  • Firecracker now supports same-host suspend/wake.
  • Suspend/wake observability and Buildkite OIDC smoke coverage were added.
  • CI now runs Linux tests through Buildkite Test Engine.

Upgrade notes:

  • Use HTTPS plus auth.required for shared Cleanroom servers.
  • Idle suspend remains disabled unless sandbox_lifecycle.idle_suspend_after_seconds is configured.
  • Existing snapshots without owner metadata are not accessible through authenticated HTTP(S) calls.

v0.9.1

26 May 00:47
@lox lox
e1eb5d2

Choose a tag to compare

This patch release fixes a macOS darwin-vz upgrade issue that could break sandbox creation after installing a newer Cleanroom release over an older local install.

On affected machines, cleanroom sandbox create could fail while reading the guest readiness probe:

create sandbox: invalid_argument: provision sandbox: decode guest readiness probe over darwin-vz proxy: missing stream frame type

The problem was caused by a stale legacy guest-agent binary named cleanroom-guest-agent-linux-<arch> being selected ahead of the current cleanroom-guest-agent installed by the release. v0.9.1 changes darwin-vz discovery to prefer the current generic guest agent, and the installer now removes the old arch-specific alias during macOS installs.

After upgrading, the standard sandbox create path should work again:

cleanroom sandbox create --image ghcr.io/buildkite/cleanroom-base/alpine:latest

No policy, API, or configuration changes are required.

v0.9.0

23 May 21:56
@lox lox
d5414b2

Choose a tag to compare

Cleanroom 0.9 focuses on making daemon-operated Git access easier to manage, moving managed darwin-vz kernels to the dedicated kernel release project, and hardening repository and image-cache behavior found during release-candidate smoke testing.

Highlights

Split managed kernel releases

Managed darwin-vz kernels now come from the dedicated buildkite/cleanroom-kernels release project instead of being built and attached by Cleanroom's own release pipeline.

When backends.darwin-vz.kernel_image is unset, Cleanroom resolves the default managed kernel manifest from buildkite/cleanroom-kernels at the pinned v0.1.0 release. Local kernel experiments can still override the managed path:

backends:
  darwin-vz:
    kernel_image: /path/to/kernel/Image

The Cleanroom release pipeline now publishes the application and macOS package assets only; the kernel bundle has separate ownership and release cadence.

GitHub App credentials for gateway Git access

Cleanroom can now mint GitHub App installation tokens host-side for matching github.com Git remotes. This gives installed daemons a daemon-friendly alternative to static tokens and interactive host credential helpers:

gateway:
  credentials:
    github_app:
      app_id: "3817917"
      installation_id: "134770928"
      private_key_file: /Users/lachlan/.config/cleanroom/github-app.pem
      repo_prefixes:
        - buildkite/

GitHub App credentials are tried before static host tokens and credential helpers. Matching repositories fail closed if token minting fails; repositories outside the configured prefixes continue through the normal credential chain.

The provider is shared by direct gateway proxying, mirror clone/fetch operations, and embedded content-cache upstream clients. cleanroom doctor reports configured credential hosts and warns when configured GitHub App credentials cannot be loaded.

Daemon log inspection

Installed daemon logs are now available through the Cleanroom CLI:

cleanroom daemon logs -n 200
cleanroom daemon logs -f

On macOS, refreshed LaunchAgent installs capture daemon stdout and stderr at ~/Library/Logs/Cleanroom/daemon.log. On Linux, Cleanroom reads the installed cleanroom.service logs from journalctl.

Repository bootstrap and refresh correctness

Repository checkouts now keep remote-tracking refs aligned with the requested commit during both fresh bootstrap and cached refresh paths. This fixes release smoke failures where a sandbox checked out the right HEAD but still reported stale metadata such as:

## main...origin/main [ahead 159]

Branch checkouts fetch and repair the requested branch tracking ref. Detached latest checkouts discover remote HEAD and repair that tracking ref. Cleanroom only rewrites an existing remote-tracking ref when the requested commit is verified to be ahead of it, so it does not clobber cases where the real remote branch is newer than the requested commit.

Image rootfs cache recovery

Image resolution can now recover from an empty image metadata database when the digest-named rootfs file already exists in the host cache. Cleanroom resolves real OCI metadata for the requested image before accepting the cached file, records the recovered platform/config metadata, and avoids unnecessary rootfs pulls or materialisation when the cached rootfs is valid.

Metadata resolution is now bounded by a per-attempt timeout and retried, while rootfs streams keep their existing stream idle timeout.

Policy and compatibility notes

  • gateway.credentials.github_app belongs in runtime config. Installed daemon service definitions do not persist GitHub App IDs, repo prefixes, or private-key paths as serve arguments.
  • Foreground cleanroom serve still supports the matching --github-app-* flags and CLEANROOM_GITHUB_APP_* environment variables.
  • Relative GitHub App private-key paths from runtime config resolve relative to the config file. Relative paths from foreground flags or environment variables resolve relative to the invocation working directory.
  • backends.darwin-vz.kernel_image remains the local override for managed kernel resolution.
  • Cleanroom release assets no longer include the managed kernel bundle; use buildkite/cleanroom-kernels for those assets.
  • Existing daemon installs should be refreshed with cleanroom daemon install --restart to pick up macOS stdout/stderr log capture.

Examples and validation

Release validation included:

go test ./...
cleanroom config validate
cleanroom doctor
darwin-vz installed-daemon repository bootstrap smoke
darwin-vz installed-binary TTI benchmark

The final installed release candidate was rebuilt from merged main at d5414b24, installed globally, and restarted under launchd. cleanroom doctor reported 41 pass, 0 warn, 0 fail.

The repository bootstrap smoke verified that a fresh darwin-vz sandbox checked out the merged release commit and reported matching tracking metadata:

HEAD=d5414b2
origin_main=d5414b2
## main...origin/main

Warm managed-kernel TTI was below the release target using:

scripts/benchmark-tti.sh --cleanroom-bin /usr/local/bin/cleanroom --start-server --backend darwin-vz --iterations 10 --warmup 2
mean:   354.3 ms
median: 353.3 ms
range:  346.1 ms ... 361.8 ms
stddev: 5.9 ms

Full changelog

v0.8.0...v0.9.0

v0.8.0

20 May 03:57
@lox lox
f5814ee

Choose a tag to compare

Cleanroom 0.8 focuses on making darwin-vz faster and easier to operate, improving local HTTPS exposure for real development servers, and tightening cache/resource behavior for larger dependency and service outputs.

Highlights

Faster darwin-vz startup and managed kernels

darwin-vz now boots the Cleanroom guest agent as init, uses a smaller rootfs-profile kernel path, retries guest vsock connections more quickly, and removes the fixed memory-grow wait during sandbox startup. Prepared runtime rootfs caches are also reused across different writable disk floors, so increasing a sandbox disk requirement no longer forces an otherwise-identical runtime rootfs rebuild.

The release includes an experimental Apple Silicon darwin-vz rootfs-profile kernel as direct GitHub Release assets:

  • cleanroom-darwin-vz-minimal-rootfs-arm64-linux-6.1.155-Image
  • cleanroom-darwin-vz-minimal-rootfs-arm64-linux-6.1.155.manifest.json
  • matching .config and .sha256 files

When backends.darwin-vz.kernel_image is unset, Cleanroom can resolve and cache the managed release kernel automatically. Local kernel experiments can still override it with a configured path:

backends:
  darwin-vz:
    kernel_image: /path/to/kernel/Image

Better resource visibility and boot diagnostics

Sandbox resource reporting is now more explicit. Cleanroom reports the effective vCPU, memory, and disk settings selected for a sandbox, exposes resource gauges for observability, and traces darwin-vz guest boot timings.

darwin-vz also supports adaptive memory ballooning, so the VM launch envelope can start smaller and grow toward the requested policy/runtime memory when needed.

For local server diagnostics, cleanroom serve can now expose a pprof listener:

cleanroom serve --pprof-listen 127.0.0.1:6060

Configured HTTPS exposure routes

Projects can now define HTTPS route names in cleanroom.yaml and use --expose-https without repeating every route on the command line:

expose:
  https:
    base: "{sandbox_id}.cleanroom.localhost"
    routes:
      - port: 3000
        hosts:
          - "{base}"
          - "*.{base}"
          - "*.*.{base}"
cleanroom exec --expose-https -- npm run dev

HTTPS forwarding also preserves forwarded host headers more accurately, which makes multi-host local apps and redirect-heavy flows behave more like they do outside the sandbox.

Cache output sizing and cache-key accuracy

Hosts can now set a backend-wide minimum for declared dependency and service output volumes:

backends:
  darwin-vz:
    minimum_cache_output_volume_bytes: 16GiB

The setting applies independently from rootfs sizing. It affects new cache-output volumes and restores from older snapshots without resizing the main runtime rootfs.

Dependency cache keys now include toolchain manifests, so caches that depend on files such as .mise.toml are less likely to be reused across incompatible toolchain changes. Sparse file volume copies are preserved, and stalled image rootfs streams are retried.

Repository and image tag resolution

Repository and image tag resolution is more robust for policy-driven sandboxes. Git credential prompts are disabled during credential fill, avoiding interactive hangs when a credential helper cannot provide a usable result in non-interactive flows.

Policy and compatibility notes

  • expose.https is an optional top-level policy section. Passing --expose-https without a value loads configured routes from policy; explicit --expose-https name:port arguments still work.
  • minimum_cache_output_volume_bytes belongs in runtime config under the backend, not in repository policy.
  • minimum_cache_output_volume_bytes and minimum_rootfs_bytes are independent settings.
  • backends.darwin-vz.kernel_image remains the escape hatch for local kernel development. If the configured path is missing, Cleanroom falls back to managed kernel resolution.
  • The managed darwin-vz rootfs-profile kernel is experimental and currently targets Apple Silicon.

Examples and validation

New and updated examples cover multi-host HTTPS routing and cache-output sizing behavior.

Release validation included:

go test ./...
policy validate: examples/basic
policy validate: examples/buildkite-agent
policy validate: examples/docker
policy validate: examples/docker-cache-output
policy validate: examples/multi-host-routing
policy validate: examples/rails
policy validate: examples/seeded-output-cache

Manual daemon smoke testing covered:

  • darwin-vz sandbox create, exec, inspect, and file copy
  • repository-aware workspace copy-in and copy-out
  • raw TCP exposure
  • configured HTTPS exposure with cleanroom.localhost
  • Docker sandbox execution with overlay2

Full changelog

v0.7.1...v0.8.0

v0.7.1

09 May 04:06
@lox lox
27ae113

Choose a tag to compare

This patch release fixes and hardens the dependency and service volume cache behavior introduced in v0.7.0.

The main correction is that cacheable commands now behave like normal workspace commands: they run against a writable workspace overlay, and declared outputs are captured from their natural guest paths instead of requiring tools to write outside the checkout.

Highlights

Dependency and service outputs can now live inside the checkout:

sandbox:
  dependencies:
    - name: node
      command: npm ci
      inputs:
        files:
          - package.json
          - package-lock.json
      outputs:
        dirs:
          - node_modules

Relative output paths resolve against repository.path, so node_modules maps to /workspace/node_modules by default, or /src/node_modules when the repository is checked out at /src. ${WORKSPACE} and absolute guest paths are still supported.

Existing output directories can seed cache misses. This supports repository layouts where an output path is present in git with a placeholder file:

sandbox:
  services:
    - name: assets
      command: test -f public/assets/.keep && npm run build:assets
      inputs:
        files:
          - package-lock.json
          - public/assets/.keep
      outputs:
        dirs:
          - public/assets

Docker-backed service caches now work for outputs such as /var/lib/docker. Cleanroom delays cache output mounts until dependency and service stages, starts Docker after those mounts are in place, and preserves Docker socket behavior when runtime paths are scratch-mounted:

sandbox:
  services:
    - name: docker-images
      command: docker pull ghcr.io/buildkite/cleanroom-base/alpine@sha256:...
      outputs:
        dirs:
          - /var/lib/docker

Cache correctness is stricter around edge cases:

  • cache blocks without repository bootstrap are rejected
  • copy-in refreshes avoid dependency cache mounts
  • directory output layers are preserved across dependency and service blocks
  • escaped writes fall back instead of publishing unsafe cache records
  • temporary output stores are cleaned up when a block cannot publish

Input digesting now supports globs and submodules, making cache keys more practical for repositories that track nested projects or broad dependency manifests.

Trace output now includes per-block cache events with block name, cache key, result, miss reason, and output paths.

Policy And Compatibility Notes

Workspace output declarations are now supported. The v0.7.0 restriction that rejected outputs inside the checkout no longer applies.

Output paths are guest paths. Relative paths are repository-relative. Outputs still cannot be /, the repository root, duplicate paths, overlapping paths, or glob patterns.

Cacheable dependency and service blocks require repository bootstrap so Cleanroom can build deterministic keys from repository state.

Dependency block environment remains scoped to the dependency command. For example, the Rails example sets BUNDLE_PATH and BUNDLE_APP_CONFIG for dependency install, then passes the same environment during execution.

Examples And Validation

New and updated examples cover:

  • examples/docker-cache-output
  • examples/seeded-output-cache
  • examples/rails

Release validation included:

mise exec -- go test ./...
policy validate: examples/basic
policy validate: examples/buildkite-agent
policy validate: examples/docker
policy validate: examples/docker-cache-output
policy validate: examples/rails
policy validate: examples/seeded-output-cache

Full Changelog

v0.7.0...v0.7.1

v0.7.0

06 May 11:16
@lox lox
5da65cb

Choose a tag to compare

Cleanroom 0.7 focuses on making sandboxes useful for day-to-day development loops: sync a real Git workspace in and out, expose services back to the host, scope network access by lifecycle stage, and reuse dependency or service outputs more aggressively across source-only changes.

Highlights

Workspace sync for existing sandboxes and top-level commands

Cleanroom now has a Git-backed workspace sync flow for tracked and unignored local changes. You can copy local edits into a kept sandbox, inspect sandbox-side changes, and copy them back to the matching local checkout.

# Copy local tracked and unignored workspace changes into an existing sandbox
cleanroom workspace copy-in --dry-run <id>
cleanroom workspace copy-in <id>

# Inspect sandbox changes before writing anything locally
cleanroom workspace diff <id>
cleanroom workspace copy-out --dry-run <id>

# Copy sandbox workspace changes back into the matching local checkout
cleanroom workspace copy-out <id>
cleanroom workspace copy-out --force <id>

Top-level commands can run the same operations automatically:

cleanroom exec --copy-in -- npm test
cleanroom exec --copy-out -- npm run fmt
cleanroom exec --sync -- npm run generate
cleanroom console --sync -- sh

--copy-in also handles local-only commits by sending a bounded Git bundle for history that is reachable from local HEAD but not yet reachable from the configured remote, then layers uncommitted changes on top. Copy-out records workspace bindings, previews changes before writing, handles copy-in manifest deletions, and refuses local conflicts unless --force is used.

Port exposure for local development

Sandboxes can expose raw TCP ports or HTTPS routes back to the local host from exec, console, sandbox create, or a standalone cleanroom expose process.

# Raw TCP: host 127.0.0.1:15432 -> sandbox port 5432
cleanroom exec --expose 15432:5432 -- postgres

# HTTPS route, usually https://buildkite.cleanroom.localhost:8143
cleanroom exec --expose-https buildkite:3000 -- npm run dev

# Keep forwarding from this client to an existing sandbox
cleanroom expose --in <id> --expose-https buildkite:3000
cleanroom port-forward --in <id> 15432:5432

Exposure requests are request-scoped client metadata. They are not stored in cleanroom.yaml or the sandbox record.

Stage-scoped network egress

Policies can now give each lifecycle stage its own allowlist instead of sharing one broad sandbox allowlist. Repository checkout uses repository.network.allow; dependency blocks, service blocks, and execution each use their own sandbox.network stage block.

version: 1
repository:
  network:
    allow:
      - github.com:443
sandbox:
  network:
    dependencies:
      allow:
        - proxy.golang.org:443
        - sum.golang.org:443
    services:
      allow:
        - registry-1.docker.io:443
        - auth.docker.io:443
    execution:
      allow:
        - api.buildkite.com:443

Stage-local allowlists do not inherit from one another. When any stage-local block is configured, omitted stages get no external egress. The older sandbox.network.allow list remains the all-stage fallback for policies that do not use stage-local blocks.

Dependency and service output volume caches

Dependency and service blocks now support declared inputs and outputs. Cleanroom can run block commands from isolated input projections, back declared output directories with cacheable volumes, restore matching outputs on cache hits, and publish reusable records after misses.

sandbox:
  dependencies:
    - name: toolchains
      command: mise install
      inputs:
        files: [.mise.toml]
      outputs:
        dirs:
          - ${HOME}/.cache/mise
          - ${HOME}/.local/share/mise
    - name: go-modules
      command: mise exec -- go mod download
      inputs:
        files: [go.mod, go.sum]
      outputs:
        dirs:
          - ${HOME}/go/pkg/mod

The initial Firecracker and darwin-vz implementations support cache output volumes, declared file outputs, sidecar mounts, and guest overlay write capture. If a block writes persistently outside its declared outputs, Cleanroom warns, skips unsafe file-keyed publication, resets declared outputs, and reruns through the exact full-rootfs path before continuing.

This makes source-only changes much cheaper for dependency-heavy projects: if the declared input files did not change, dependency and service outputs can be reused without rerunning the block commands.

Docker registry mirroring and Docker preflight checks

Guest Docker support now installs registry-host configuration for built-in public registries (ghcr.io, public.ecr.aws) and runtime-configured non-Docker-Hub registries, routing pulls through the Cleanroom gateway /registry/<host>/ cache without a direct upstream fallback.

Docker Hub continues to use the /v2/ mirror endpoint, and the checked-in Docker example documents the redirect hosts needed for real pulls:

sandbox:
  docker:
    required: true
  network:
    default: deny
    allow:
      - auth.docker.io:443
      - registry-1.docker.io:443
      - production.cloudflare.docker.com:443
      - docker-images-prod.6aa30f8b08e16409b46e0173d6de2f56.r2.cloudflarestorage.com:443

When sandbox.docker.required is true, Cleanroom now fails fast if the selected rootfs cannot satisfy the Docker service contract. Unsupported images produce a clear error instead of creating a sandbox with no docker CLI, no dockerd, and no /var/run/docker.sock.

ZFS cache peer import/export

The Firecracker ZFS cache path now has a narrow host-to-host replication foundation for system dependency and services caches. Configured peers can advertise cache records, validate parent lineage, stream ZFS incrementals from a sender, receive into unpublished import datasets, validate metadata, and publish the imported cache only after receiver-side checks pass.

This is intentionally limited to Firecracker + ZFS system caches where the receiver already has the required parent stage. Failed lineage, metadata, policy, backend, driver, architecture, or producer checks fall back to local rebuild.

Host storage visibility and pruning

Cleanroom now includes host storage inspection and pruning commands:

cleanroom system df
cleanroom system prune

Storage cleanup also covers terminated sandbox storage and stale ZFS import datasets, and daemon startup performs import-dataset cleanup for failed or abandoned transfers.

Better examples, diagnostics, and agent workflow

The checked-in examples were updated for the current policy shape:

  • examples/buildkite-agent/ now uses multiple dependency blocks for toolchains and Go modules.
  • examples/docker/ documents Docker Hub redirect allowlists for gateway-backed pulls.
  • examples/rails/ uses declared dependency inputs and a reusable bundle output.
  • skills/cleanroom/ adds a reusable Cleanroom agent skill and OpenAI agent config.

Diagnostics also improved across this release: cleanroom doctor reports more host/runtime detail, backend capabilities advertise cache-output and overlay-capture support, execution inspection includes more state, and observability has cache-specific metrics and trace attributes.

Policy and compatibility notes

  • --include-local-changes has been removed. Use --copy-in, --copy-out, or --sync.
  • sandbox.dependencies and sandbox.services are ordered block lists. Each cacheable block has a stable name, a command, declared inputs.files, and declared outputs.dirs or outputs.files.
  • Dependency and service output paths must not overlap, and workspace-output declarations are rejected in this first volume-cache slice.
  • Block environment values are literal strings, except supported leading ${HOME} / $HOME / ~ and ${WORKSPACE} / $WORKSPACE shorthand expansion.
  • Stage-local network blocks cannot be combined with legacy all-stage sandbox.network.allow.
  • Registry redirects are still checked against the redirected host. For example, GHCR blob downloads commonly require pkg-containers.githubusercontent.com:443 in policy.
  • Guest Docker service support requires a Docker-capable image such as ghcr.io/buildkite/cleanroom-base/debian-docker.

Examples and validation

The release range was reviewed from v0.6.0..v0.7.0. Local validation before tagging included:

mise exec -- go test ./...
for dir in examples/basic examples/buildkite-agent examples/docker examples/rails; do
  mise exec -- go run ./cmd/cleanroom policy validate --chdir "$dir"
done

See:

  • README.md
  • docs/spec.md
  • docs/gateway.md
  • docs/plans/workspace-sync.md
  • docs/plans/stage-scoped-egress.md
  • docs/plans/dependency-service-volume-caches.md
  • docs/plans/zfs-stage-cache-replication.md
  • examples/buildkite-agent/
  • examples/docker/
  • examples/rails/

Full changelog: v0.6.0...v0.7.0

v0.6.0

29 Apr 07:07
@lox lox
9b81b5e

Choose a tag to compare

Cleanroom 0.6 is focused on making repository sandboxes faster to create, easier to inspect, and more useful for real dependency-heavy projects while keeping egress policy explicit.

Highlights

Layered setup and dependency caching

Policies can now describe setup work that belongs at sandbox creation time instead of repeating it before every command. Use sandbox.dependencies.command for deterministic dependency bootstrap, sandbox.services.command for snapshotable service preparation, and sandbox.run.before for live startup before each execution.

version: 1
sandbox:
  image:
    ref: ghcr.io/buildkite/cleanroom-base/debian@sha256:...
  dependencies:
    command: bundle install
    key:
      files: [Gemfile.lock]
    reuse: portable
  services:
    docker:
      required: true
    command: |
      docker compose up -d postgres valkey
      bin/rails db:prepare
      docker compose stop postgres valkey
    key:
      files: [docker-compose.yml, db/schema.rb]
  run:
    before: docker compose up -d postgres valkey

reuse: portable lets Cleanroom reuse dependency state across source-only commits when the declared key files still match after refreshing the checkout.

Gateway-backed package and repository traffic

The host gateway now embeds buildkite/content-cache routes for Git, OCI registry traffic, Go modules and sumdb, RubyGems, and immutable downloads. Credentials stay on the host, while sandbox traffic is still checked against the policy allowlist.

For Go projects, allowlisting the relevant upstream hosts lets Cleanroom route module and SDK downloads through the gateway cache:

sandbox:
  network:
    default: deny
    allow:
      - github.com:443
      - proxy.golang.org:443
      - sum.golang.org:443
      - dl.google.com:443
      - storage.googleapis.com:443

For Ruby projects, Bundler traffic to rubygems.org can use the embedded RubyGems cache. The new Rails example shows a full dependency-stage bundle install against rails/rails:

cd examples/rails
cleanroom policy validate
cleanroom exec \
  --backend darwin-vz \
  --repo-url https://github.com/rails/rails.git \
  --repo-commit cfa4e1b475472c7980a42dd810f237951db5108a \
  -- sh -lc 'cd activesupport && bin/test test/benchmarkable_test.rb'

Docker-in-sandbox also benefits from gateway-backed Docker Hub mirroring, and guest Docker now defaults to overlay2 where supported.

Reproducible local changesets

Repository-aware commands can package local uncommitted changes into a reproducible changeset and apply it after the exact repository checkout:

cleanroom exec --include-local-changes -- go test ./...
cleanroom console --include-local-changes -- bash

Cleanroom now persists changeset metadata and preserves pending changeset retries, so a failed pre-run no longer silently consumes the requested local changes.

Resource requirements in policy

Policies can declare backend-neutral minimum resources for workloads that need more CPU, memory, or disk. These raise smaller backend defaults without lowering larger host settings.

sandbox:
  resources:
    vcpus: 4
    memory: 8GiB
    disk: 16GiB

File copy primitives

The new cleanroom copy / cleanroom cp command can move individual files into or out of an existing sandbox:

SANDBOX_ID="$(cleanroom create)"
cleanroom cp ./input.json "$SANDBOX_ID:/tmp/input.json"
cleanroom exec --in "$SANDBOX_ID" -- sh -lc 'process /tmp/input.json > /tmp/result.json'
cleanroom cp "$SANDBOX_ID:/tmp/result.json" ./result.json

Copy-out preserves useful file metadata such as mode and mtime.

Better observability and diagnostics

0.6 expands OpenTelemetry coverage with trace links, gateway spans, structured server logs, cache attributes, and consistent gateway action/reason telemetry for cached and malformed requests. Gateway routes now emit stable decisions for Git, OCI, Go proxy, sumdb, RubyGems, and fetch traffic.

Firecracker diagnostics also now detect host support more directly, and blocked connection reporting has NFLOG-based support on Linux.

Easier sandbox lifecycle

Sandbox creation now streams progress, daemon install/restart UX is smoother, execution inspection commands are more useful, and guests get localhost host entries and loopback setup. darwin-vz also supports allow-all sandboxes created via --dangerously-allow-all.

cleanroom console --dangerously-allow-all -- bash
cleanroom exec --dangerously-allow-all -- npm test

Policy and compatibility notes

Policy validation is intentionally stricter in 0.6:

  • cleanroom.yaml requires version and sandbox.image.ref.
  • Repository policy files remain deny-by-default; network.default: allow is only produced internally by --dangerously-allow-all when creating a new sandbox.
  • sandbox.network.allow supports exact host:port shorthand or mapping entries with explicit ports.
  • Wildcards, CIDR ranges, bare hosts, optional ports, network.deny, registries, metadata, secrets, and lockfile-enforcement fields are not accepted yet.
  • Unknown config and policy fields fail validation.

Cleanroom also no longer auto-wraps commands with mise. If you need mise, call it explicitly in the command, sandbox.dependencies.command, or sandbox.services.command:

cleanroom exec -- mise x -- go test ./...

Examples and validation

The checked-in examples now run in CI on both macOS and Linux. The Buildkite Agent example exercises Go module and SDK gateway caching, and the Rails example exercises RubyGems caching and dependency-stage bootstrap.

See:

  • examples/buildkite-agent/
  • examples/rails/
  • docs/spec.md
  • docs/gateway.md
  • docs/observability.md

Full changelog: v0.5.0...v0.6.0

v0.5.0

12 Apr 02:15
@lox lox
a502209

Choose a tag to compare

Cleanroom v0.5.0

This release expands network-policy enforcement and observability, improves the macOS darwin-vz backend, and makes repo-aware sandboxes more practical for development and CI workflows.

Highlights

  • Added trusted DNS policy/runtime support for Firecracker so hostname allowlists can be enforced from observed DNS answers without requiring setup-time resolution.
  • Improved network observability with blocked-connection logging, client-visible warnings, and warnings for DNS queries to disallowed hosts.
  • Added darwin-vz filehandle networking with Cleanroom-owned gateway behavior and allowlist egress filtering on macOS.
  • Added workspace seed snapshot reuse and clone-based Firecracker root volumes to reduce provisioning overhead for repeated sandbox runs.
  • Added repository checkout overrides for repo-aware commands.
  • Added --no-mise to cleanroom exec and cleanroom console to disable mise wrapping for a single execution.
  • Improved CLI UX by surfacing sandbox creation progress.
  • Refreshed docs, CI guidance, and the Buildkite agent example.

Notable fixes

  • darwin-vz now defaults to the filehandle network path and removes legacy network mode support.
  • Improved darwin-vz startup behavior with loopback bring-up during init and better helper app bundle selection on install.
  • Automatic mise bootstrap is now disabled by default. Repo-aware sandboxes can still enable it explicitly, and per-run opt-out is available with --no-mise.
  • Improved CI reliability by isolating Cleanroom cache per job, reconciling Terraform with live CI hosts, and stabilizing cleanroom e2e jobs.
  • Fixed the macOS release packaging path and updated the release archive manifest for the filehandle-only darwin helper layout.

Upgrade notes

  • darwin-vz now supports only filehandle networking. Legacy vmnet/shared-style settings were removed.
  • Repo-aware commands do not automatically run through mise exec -- ... unless sandbox.mise.install: true is set in cleanroom.yaml and sandbox.mise.enabled is not false.
  • cleanroom exec --no-mise and cleanroom console --no-mise disable mise wrapping for a single execution when policy enables it.
  • Host allowlisting is still based on observed DNS answers plus destination IP:port. Co-hosted services on the same IP:port are not distinguished.

Full changelog

Includes all changes from v0.4.1 on April 5, 2026 through v0.5.0 on April 12, 2026.

v0.4.1

05 Apr 01:18
@lox lox
c2c18fb

Choose a tag to compare

Changelog

  • f010088 chore(terraform): bump prod cleanroom to v0.4.0 (#141)
  • 45a75db fix: harden Firecracker ZFS snapshot lifecycle (#142)
  • 91a5332 fix: prefer notarized macOS pkg in install script (#139)
  • 316200f fix: support piped macOS installs (#140)