Content addressing & the CAS
Everything in zut is addressed by the hash of its bytes. This is the substrate that makes caching correct and sharing safe.
Digests
A digest is the BLAKE3 hash of some bytes plus their length. zut’s digest
type and hashing live in src/core/digest.zig. The hash function is recorded in
a REAPI-compatible way (the CLI banner shows digest: blake3, REAPI #9), so the
store knows which function produced a given address.
The CAS (content-addressed store)
The CAS (src/core/cas.zig) is a key→bytes store where the key is the
content’s digest. Properties that fall out of that:
- Deduplication — identical bytes are stored once, regardless of how many actions produce or consume them.
- Integrity — a blob read back is verified against the digest it was asked for; corruption or a lying remote is detected.
- Immutability — an address never changes meaning, which is why a cached result computed elsewhere is trustworthy here.
The CAS is tiered: a read misses locally, falls through to the remote cache if configured, verifies the blob, and populates the local tier. Writes go through to the remote (write-through). A remote failure degrades to local-only rather than breaking the build.
Trees (Merkle directories)
A directory is content-addressed as a Tree (src/core/merkle.zig): a node
listing names → child digests (files or sub-trees). The digest of a Tree is
therefore a hash of its entire recursive content. This is how:
- a crate’s unpacked source is grafted into a build as a single immutable input
(
crate_tree(...)), and - an action’s input set is named by one root digest.
The action cache
An action (src/core/action_cache.zig) is keyed by the digest of its
command + input Tree + environment. The action cache maps that key to the action
result (output digests, exit code, captured stdout/stderr). Because the key is
a content hash:
- the same action never runs twice, and
- the cache is valid across machines, which is exactly what the remote cache exploits.
This shape mirrors the Bazel Remote Execution API, so the same store backs both local incrementality and remote caching without a second code path.
See
docs/ARCHITECTURE.md§3 for the precise encodings and the REAPI mapping.