The OxygenFlow build image
A root file system for Rust and Node jobs:
- Rust 1.97.1 with rustfmt and clippy.
- Node 24.21.0, checked against its published SHA-256.
- git, make and pkg-config.
- Coverage: llvm-tools and cargo-llvm-cov 0.9.1, so a job can write an LCOV report for the hub's Coverage tab.
- Test reports: cargo-nextest 0.9.148, so a job can write JUnit XML for the hub (with
cargo llvm-cov nextest, both from one run). - Release cross-building: the Windows, Linux arm64 and riscv64, and macOS targets, zig 0.16.0 (checked against its SHA-256), cargo-zigbuild and MinGW, with zip, so
scripts/dist.shruns in a job. - Every crate that oxygen, oxygenhub and oxygenflow use, vendored in
/opt/vendor.
OxygenFlow jobs have no network (network "isolated"). So cargo is pointed at the vendored crates by /.cargo/config.toml, which cargo reads for any directory under /, including /src. CARGO_HOME stays at its default, $HOME/.cargo, which is under /tmp and writable. The toolchain's binaries are linked into /usr/local/bin, because the guest's PATH is fixed and nothing at run time needs rustup.
Building and importing
images/rust/build.sh /path/to/oxygenflow-build.tar
scp /path/to/oxygenflow-build.tar claude@earth:/home/claude/flow/dl/
ssh claude@earth 'cd ~/flow && FLOW_HOME=~/flow bin/flow rootfs import dl/oxygenflow-build.tar'
build.sh vendors the crates of the three workspaces next to this repository with cargo vendor --locked, then builds the image. Docker is used only on the machine that builds it, as a way to assemble a file system: the image is exported as a plain tarball. On the runner, flow rootfs import makes an ext4 image pinned by BLAKE3, which Firecracker boots read-only.
Rebuild and re-import whenever a Cargo.lock of those projects changes, then pin the new hash in the pipelines that use the image. A crate missing from the image fails the job with cargo's "no matching package" error, not with a network error.
Using it
sandbox "rust" {
runtime "microvm"
rootfs "cas://blob/<hash printed by flow rootfs import>"
memory-limit "8GiB"
cpu-limit 4
network "loopback" // tests that serve on 127.0.0.1 need it; nothing leaves the guest
}
job "test" {
sandbox "rust"
mount {
source "ephemeral://disk"
target "/src/target"
size "24GiB"
}
env {
CARGO_INCREMENTAL "0"
}
exec "cargo" "test" "--workspace"
}
Measured on Earth (4 cores, the job given all of them and 8 GiB): oxygen's cargo fmt --check, clippy -D warnings and cargo test --workspace from a cold build take 135 s.
The build tree goes on an ephemeral://disk mount. The workspace itself has only 512 MiB to spare, and a tmpfs would take the build tree out of the job's memory.