Caching
heph caches at the granularity of a target. Before running anything it hashes a target's complete inputs into a single digest; if that digest is already in the cache, heph returns the stored output instead of re-running the action. That is a cache hit.
$ heph run //app:server
0.565s · 1 / 1 done · 1 cached · 0 failed
What goes into the hash
Everything a target declares folds into its input hash: its sources, its dependencies' outputs, the tools it uses, its environment, and the target definition itself. Change any of them and the digest changes, so the target re-runs. Leave them all identical and you get the cached bytes — on your laptop or in CI. This is the foundation of reproducibility.
Because outputs are content-addressed by their inputs, a cache hit is provably the exact artifact you would have built. The cache is trustworthy, not a guess.
Fixed-point caching
A target whose inputs equal its outputs reaches a fixed point: re-running it
produces the same digest, so it's a cache hit and never executes again. This is
what makes idempotent codegen (an in_place formatter,
say) free to re-run — once the tree is formatted, the formatter is a no-op hit.
Controlling caching per target
Whether a target caches, and how aggressively, is a property of the
driver that runs it. The exec driver
exposes a cache field — a bool, or a map of {enabled, remote, history} — to
opt a target out or tune retention. See the driver's page for the exact shape.
In-memory cache
Within a single run, heph also keeps results in memory. Size it with the
memCache block in
.hephconfig2; capacityBytes: 0 disables it.
Durable cache storage
Between runs, artifacts are stored on disk. Small artifacts (≤ 8 MiB by default)
are kept in the cache database; larger ones are stored as plain files under
<homeDir>/cache/blobs/. GC reclaims both. The threshold is tunable with
cache.spillThresholdBytes
in .hephconfig2.
Remote shared cache
A remote cache lets multiple machines share build artifacts. When one machine builds a target, its artifacts are pushed to the remote; any other machine with the same inputs pulls them instead of rebuilding.
Configure one or more remote caches in .hephconfig2:
caches:
shared:
uri: s3://my-bucket/heph-cache # s3://, gs://, az://, https://, file://
Writes happen on a background task after a local cache write — every writable remote receives the artifacts in parallel. The build's critical path does not wait on the network; heph stays open until all uploads drain.
Reads happen on a local cache miss. heph tries remotes in ascending-latency order and pulls the full revision from the first cache that has it. Latency is measured once per process (or loaded from a persisted file) and refreshed when the cache definitions change.
A failing remote never fails the build — errors are logged once and, after three consecutive failures, that cache is skipped for the rest of the run.
For the full set of options and supported backends, see
caches in the configuration reference.
For a step-by-step CI walkthrough, see the Remote cache guide.
Filesystem scan cache
heph also remembers the results of workspace file scans — glob expansion, package discovery — across runs, validated against file and directory metadata. Any change to the tree re-scans the affected paths, so edits are always picked up. This is transparent and needs no configuration.
When debugging, rule the scan cache out by setting
HEPH_DEBUG_CACHED_WALKER=0 — every scan then reads the filesystem directly,
with no caching at all.
Reclaiming space
The on-disk cache grows as inputs change. Garbage-collect entries that are no longer reachable:
heph tool gc