CI & untrusted runners

CI runners that can't escape.

Untrusted PRs, dependency builds, and third-party actions run arbitrary code on your runners. crucible gives each job its own microVM with its own kernel, so a poisoned build stays in the box. Warm the toolchain once, fork a fresh runner per job.

  • KVM per job
  • ~125 ms warm fork
  • SSRF-safe egress
  • ephemeral by default
ci@host · crucible

what each job gets

A private machine, a network you drew, a clean slate

01

A private kernel

A job cannot pivot from a container escape into your runner fleet, because there is no shared kernel. Each build is a real VM.

Firecracker + jailer
02

A network you drew

Default-deny egress with a hostname allowlist. Even wide open, RFC1918 and cloud metadata stay unreachable.

--net-allow · SSRF-safe
03

A clean slate every time

Fork from a warm toolchain snapshot, discard after the job. No state bleeds from one build into the next.

snapshot + fork + rm

wire it into a pipeline

Warm once, fork per job, throw it away

warm once

Snapshot the toolchain

Install deps and prime caches in one VM, snapshot it, reuse it forever.

$ SBX=$(crucible run node:22)
$ crucible exec $SBX -- npm ci
$ SNAP=$(crucible snapshot create $SBX)

per job

Fork a runner

Each PR gets a fresh fork of the warm snapshot, in seconds not minutes.

$ JOB=$(crucible fork $SNAP)
$ crucible cp ./pr $JOB:/src
$ crucible exec $JOB -- npm test

clean up

Throw it away

Remove the VM when the job ends. Nothing survives to the next build.

$ crucible rm $JOB
# no residue, no cross-build
# contamination

self-hosted, on your own runners

Isolation your CI can explain in one sentence.

Every job is a real virtual machine with its own kernel, forked warm and discarded clean.

$ curl -fsSL https://github.com/gnana997/crucible/releases/latest/download/install.sh | sudo bash -s -- --with-deps --enable