A content-addressed cache for build artifacts.
Ferrule stores build outputs by the hash of their inputs, so repeated builds resolve from disk instead of running again. Single static binary, no daemon, no configuration file required.
Overview
Deterministic keys
Cache keys are derived from the command line, environment allowlist, and a digest of every declared input file. Identical inputs always resolve to the same entry.
No background process
Everything happens in the calling process. There is no daemon to supervise, no socket to clean up, and nothing left running after the build exits.
Bounded on disk
The store is pruned by least-recent-use against a size ceiling. Eviction runs inline and is safe to interrupt at any point.
Portable stores
A store is an ordinary directory. Copy it, mount it read-only, or share it over NFS between machines of the same architecture.
Usage
Wrap any command that produces files. Declare the inputs it reads and the outputs it writes:
# first run executes the compiler ferrule run \ --in src/*.c --in include/ \ --out build/libwidget.a \ -- cc -c -O2 src/*.c -o build/libwidget.a # second run restores from the store in ~40ms ferrule run --in src/*.c --in include/ \ --out build/libwidget.a -- cc -c -O2 src/*.c -o build/libwidget.a → hit 3f9c1a2e (restored 1 file, 2.1 MiB)
Inspect what is stored and reclaim space:
ferrule stat # entries, total size, hit ratio ferrule prune --max 4G # evict until under the ceiling ferrule verify # re-hash every entry, drop corrupt ones
Options
- --store <path>
- Location of the cache directory. Defaults to
$XDG_CACHE_HOME/ferrule, falling back to~/.cache/ferrule. - --env <name>
- Include an environment variable in the cache key. Repeatable. Variables not listed are excluded, which keeps unrelated shell state out of the hash.
- --max <size>
- Size ceiling for the store, accepting
K,M, andGsuffixes. Default is 8G. - --no-write
- Read from the store but never add to it. Useful on CI runners with a read-only shared cache mount.
- --json
- Emit machine-readable results on stdout. The command's own output is passed through on stderr unchanged.
Changes
- 0.6.2 — fix eviction skipping entries whose mtime is in the future
-
0.6.1 —
verifyno longer holds the store lock while hashing -
0.6.0 — directory outputs,
--json, faster digest on large trees - 0.5.4 — correct handling of symlinked inputs pointing outside the tree