A hermetic, incremental, content-addressed build system in Zig —
with Rust and Zig as first-class languages.
⚠️ Early and experimental. The design is settled; APIs, the Starlark dialect, and the CLI still change. Not yet production-ready.
zut is a from-scratch build system in the spirit of Bazel
and Buck2 — its own Starlark front-end, content-addressed
store, sandboxed executor, and incremental engine — built in Zig
0.16. A rust_binary can depend on a zig_library and vice versa, linked over
the C ABI, built hermetically and cached per action.
- Correct by construction — a build's output is a pure function of its declared inputs. Undeclared inputs are made physically unavailable by the sandbox, so "works on my machine" becomes a build error, not a mystery.
- Fine-grained incrementality — the cache unit is the action, not the target. Change one file, rebuild exactly what depended on it.
- Content-addressed caching — identical work never runs twice, locally or across a team via a shared remote cache (S3 / GCS / a shared dir / your own).
- Rust & Zig as peers — neither is a second-class bolt-on.
- Remote-ready — the execution path is shaped like the Bazel Remote Execution API, so remote caching (today) and remote execution (later) drop in.
- Extensible — rules like
rust_binaryare Starlark definitions over primitives, not hardcoded into the engine.
Requires Zig 0.16.0.
$ git clone https://github.com/Sh4d1/zut && cd zut
$ zig build -Doptimize=ReleaseFast # → zig-out/bin/zut
$ zig build test --summary all # run the test suiteA minimal workspace — a Rust binary linking a Zig static library:
# BUILD
load("@builtin//:rust.bzl", "rust_binary")
load("@builtin//:zig.bzl", "zig_library")
zig_library(name = "greet", src = "greet.zig", libname = "greet")
rust_binary(
name = "hello",
crate_root = "src/main.rs",
srcs = ["src/main.rs"],
edition = "2021",
out = "hello",
native_deps = [":greet"],
)$ zut build //:hello # builds into ./zut-out/, caches every action
$ zut test //:hello_test # runs the test binary as a cached action
$ zut fetch Cargo.lock # vendor crates.io deps into the CAS (the only networked step)See the zut Book for the full guide: getting started, configuration, remote caching, sandboxing, and writing BUILD files.
Back the local caches with a shared store — --remote-cache=<spec> or
remote-cache = <spec> in .zutrc:
| Spec | Backend |
|---|---|
fs:///mnt/cache |
a shared directory (NFS, local) |
s3://bucket?region=eu-west-3 |
native S3 / S3-compatible (MinIO, R2) — hand-rolled SigV4 |
gs://bucket |
native Google Cloud Storage (OAuth2 bearer) |
cmd:/path/to/helper |
bring your own store via a get|put|has helper |
The S3 and GCS clients are standalone in-tree Zig SDKs — no AWS/GCP SDK or CLI dependency. Details: remote caching.
- 📖 The zut Book — the practical guide.
- 📐
docs/ARCHITECTURE.md— the design contract (the why). - 🗺️
docs/ROADMAP.md— the phased plan. - 🧩
docs/design/— focused design notes (layout, remote cache, crates.io).
Contributions are welcome — see CONTRIBUTING.md. In short:
build with zig build, keep zig build test --summary all green, use the
internal logger (never std.debug.print), and read docs/ARCHITECTURE.md first.
Licensed under either of
- MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.