Rust build-cache hygiene study
Measures Rust build-cache growth and compares cleanup tools, filesystem behavior, and safe integration options.
Research state: Current
Summary
Rust build caches need measured, policy-driven cleanup because filesystem behavior and tool safety differ across environments.
The measurements and design options share a July 2026 verification cutoff; implementation is tracked separately.
Research question
Rust-heavy jackin❯ work can silently turn host-local cache directories into the largest disk consumer on the machine. The July 2026 operator disk audit found ~/.cache/jackin at roughly 410 GB, with one jackin❯ project cache target around 402 GB. Separate Rust workspaces also had large local target/ trees: one non-jackin❯ Rust workspace around 30 GB, this jackin❯ checkout around 18 GB, and another Rust checkout around 13 GB.
A follow-up deep audit the same week found the problem was worse than initially scoped and was growing structurally:
- A single shared cargo
target/dir (bind-mounted into three running jackin❯ containers) ballooned to 460 GiB within hours of cleanup. The breakdown: 42 GiB ofdebug/depsand 29 GiB ofdebug/incrementalcontaining over 3,000 per-session incremental directories — each concurrent cargo invocation creates its own session tree, and nothing garbage-collects them. - An external agent (OpenAI Codex CLI) created separate
CARGO_TARGET_DIRoverrides (codex-otel,codex-otel-fast,codex-otel-msrv,msrv) inside the shared target, adding 111 GiB of near-identical compilation artifacts — the same crates compiled multiple times into separate directories. - The host's own
target/dir held 23 GiB of artifacts from stuckcargo testprocesses that deadlocked on PTY I/O for hours, blocking cleanup. - Disk usage cycled between 240 GiB and 515 GiB on a 926 GiB volume as caches were manually cleaned and then immediately regenerated by concurrent builds.
The root bug class is not "one bad directory". jackin❯ has multiple places that create or preserve build/cache state for speed, but there is no single Rust build-cache ownership model with quotas, TTLs, eviction order, operator-visible accounting, or an automatic cleanup path. A cache can be created because it improves launch or build latency, then survive indefinitely because no policy owns its lifetime. Worse, concurrent agent builds into shared or overlapping target directories multiply artifacts without deduplication, and incremental-compilation session directories accumulate without garbage collection.
Why it matters
Agents run many builds across many branches, isolated worktrees, generated checkouts, and PR verification bundles. Rust makes the pressure worse because Cargo stores build output under target/ by default, and those targets are per-workspace unless explicitly redirected. Compiler-result caches such as sccache solve a different layer: they avoid recompiling repeated rustc inputs, but they do not delete every workspace's target/ directory and they add their own disk cache that needs a size cap.
If jackin❯ keeps optimizing for faster warm starts without matching cleanup mechanics, the product becomes hostile to long-running local use. The operator sees free disk disappear, macOS becomes unstable near full-disk conditions, and the natural workaround is destructive manual deletion instead of a trusted jackin prune or jackin doctor flow.
The fix should be structural: every cache jackin❯ creates or recommends must have an owner, a purpose label, a default budget, a cleanup mechanism, and a visibility surface.
Tool comparison
Why a comparison is needed
The credible options differ materially in cache storage and eviction. kache (github.com/kunobi-ninja/kache) directly addresses the disk-pressure failure mode measured in the July 2026 audit, while sccache has broader ecosystem maturity. The comparison below keeps the decision evidence-based.
Feature matrix
| Feature | sccache | kache | ccache | CARGO_INCREMENTAL=0 only |
|---|---|---|---|---|
| What it caches | Compiler results (rustc + C/C++) | Compiler results (rustc + C/C++) | C/C++ only | Nothing — just disables incremental |
| Already in jackin❯ mise.toml | Yes (0.16.0) | No | No | n/a |
| Already in jackin❯ CI | Yes (all workflows) | No | No | Yes (CARGO_INCREMENTAL=0 alongside sccache) |
| Disk dedup across target dirs | No — each target dir stores full copies | Yes — reflink/hardlink from content-addressed store | No (C/C++ only) | No |
| APFS reflinks (zero-copy restore) | No — copies artifacts into target/ | Yes — copy-on-write clone, zero disk cost per restore | No | n/a |
| Content-addressed store | No (opaque cache) | Yes — blake3 hash, identical blobs stored once | Partial (hash-based) | n/a |
| Auto-disable incremental | No — requires manual CARGO_INCREMENTAL=0 | Yes — artifact caching replaces incremental | n/a | n/a (it IS the disable) |
| Built-in target/ cleanup | No | Yes — kache clean (TUI), kache gc (LRU eviction) | No | No |
| Cache key portability | Path-sensitive (needs SCCACHE_BASEDIRS) | Path-normalized by default — cross-machine keys out of box | Path-sensitive | n/a |
| C/C++ caching | Yes (separate sccache gcc wrapper) | Yes (kache cc / kache c++, same store) | Yes (C/C++ only) | No |
| Remote backends | Redis, S3, GCS, Azure, GHA, Memcached, WebDAV, OSS, COS | S3 (AWS, R2, Ceph, MinIO) | Remote (limited) | n/a |
| Multi-level cache | Yes (disk,redis,s3 chains with backfill) | No (local + optional S3 sync) | No | n/a |
| Distributed compilation | Yes (icecream-style) | No | No | n/a |
| Maturity / adoption | Very high — Mozilla-backed, 7.5k stars, shipped since 2016 | New — v0.10.0, 380 stars, 2026 | Very high (C/C++ world) | Cargo built-in |
| GitHub Actions integration | sccache-action | kache-action | n/a | n/a |
| Install methods | brew, cargo, Nix, scoop, winget | mise, brew, cargo-binstall, APT, winget, AUR | brew, apt, all distros | Cargo built-in |
| Daemon model | Client-server (auto-start, 10 min idle timeout) | Optional daemon (local cache works without it) | None | n/a |
| Monitor / dashboard | sccache --show-stats (text) | kache monitor (live TUI), kache report (Perfetto/Chrome trace) | ccache --statistics | n/a |
sccache — strengths and limits
sccache (Mozilla, Apache-2.0) is a mature ccache-like compiler wrapper for Rust, C/C++, CUDA, and HIP. It is already installed via jackin❯'s mise.toml (cargo:sccache = "0.16.0") and wired as RUSTC_WRAPPER with CARGO_INCREMENTAL=0 across all CI workflows.
Strengths for jackin❯:
- Already integrated — zero new dependencies to adopt.
- Most remote backends of any tool (Redis, S3, GCS, Azure, GHA, Memcached, WebDAV, OSS, COS).
- Multi-level cache chains (
disk,redis,s3) with automatic backfill — a small local L0 plus a remote L1 can eliminate cold builds across CI runners and developer machines. - Distributed compilation (icecream-style with authentication and sandboxing).
SCCACHE_BASEDIRSfor path normalization across checkouts.
Limits for jackin❯'s disk problem:
- No disk deduplication. Each
target/directory stores full copies of compiled artifacts. Five separate target dirs (e.g.codex-otel,codex-otel-fast,debug,msrv) each contain their own copies of the same.rlib/.rmetafiles. sccache makes rebuilds fast aftercargo clean, but the target dirs themselves still balloon. - Does not manage target dirs. No built-in mechanism to find, report, or clean stale
target/trees. The operator must know to look for them and delete manually. - Requires explicit
CARGO_INCREMENTAL=0. Without it, incremental compilation sessions accumulate intarget/debug/incremental/— the July 2026 audit found 3,091 session directories totaling 64 GiB in a single shared target. sccache documents this as a known caveat: "Incrementally compiled crates cannot be cached." - Opaque cache. The cache store is not content-addressed in a way the operator can inspect.
sccache --show-statsgives hit/miss counts but not "which crates are cached" or "how much disk is duplicated across targets."
kache — strengths and limits
kache (Kunobi, Apache-2.0) is a zero-copy, content-addressed build cache for Rust and C/C++. It is a drop-in RUSTC_WRAPPER and a cc/c++ compiler wrapper. Cache keys are blake3 hashes of normalized compiler inputs; cache hits restore zero-copy — a reflink (copy-on-write clone) where the filesystem supports it (APFS, btrfs, XFS-with-reflink), and a hardlink or copy otherwise.
Strengths for jackin❯:
- APFS reflinks eliminate the multi-target-dir disk explosion. On macOS (APFS), kache restores cached artifacts into
target/via copy-on-write reflinks. Multiple target dirs sharing the same compiled crate share the same physical disk blocks. Five target dirs × 40 GiB each could be ~40 GiB of unique blocks instead of 200 GiB. This directly solves thecodex-otel*directory multiplication problem. - Content-addressed store deduplicates identical blobs. Every compiled artifact is stored by blake3 hash. If two different crate compilations produce identical output (common for crates in multiple feature sets), they share one blob file. The SQLite index tracks references; GC removes blobs only when no entry references them.
- Automatically disables incremental compilation. kache disables incremental on every platform while wrapping rustc, because artifact caching replaces that path and avoids APFS-related incremental-compilation corruption in git worktrees. No need for a separate
CARGO_INCREMENTAL=0env var. - Built-in target/ management.
kache cleanis a TUI that findstarget/directories, shows what percentage of each is cached, and can safely delete them.kache gcprovides LRU or age-based eviction.kache stats/kache reportgive detailed hit/dup/miss breakdowns including Perfetto/Chrome traces. - Path-normalized cache keys by default. Cross-machine portability without
SCCACHE_BASEDIRSconfiguration. A developer's laptop and a CI runner with the same toolchain produce identical keys. - C/C++ caching in the same store.
kache cc/kache c++wraps gcc/clang/clang-cl object compiles. The jackin❯ workspace's heavy C dependencies (aws-lc-sys, rocksdb, secp256k1) would be cached alongside Rust artifacts in one store. - Daemon is optional for local caching. Local hits and misses work without the daemon. The daemon only matters for async S3 uploads, remote checks, and prefetching.
- Available via mise.
mise use -g github:kunobi-ninja/kache@latest— jackin❯ already uses mise for all tool management.
Limits for jackin❯:
- Newer project. v0.10.0, 380 GitHub stars, first released in 2026. Less battle-tested than sccache (7.5k stars, shipping since 2016). Risk: undiscovered bugs, API instability, smaller community.
- Fewer remote backends. Only S3-compatible (AWS, R2, Ceph, MinIO). No Redis, GCS, Azure, GHA, or Memcached backends yet. Multi-level cache chains not available.
- No distributed compilation. sccache's icecream-style distributed compilation is not available.
- Container filesystem caveat. Inside OrbStack/Docker containers on macOS, bind-mounted paths from the host go through VirtioFS. Reflinks work on APFS (native macOS) but may fall back to hardlinks or copies through VirtioFS. The content-addressed store and automatic incremental disabling still apply, but the reflink disk deduplication benefit is reduced for container-internal target dirs. Host-native builds get full reflink benefits.
- Not yet in jackin❯ CI. Adopting kache for CI would require replacing
sccache-actionwithkache-actionacross workflows.
ccache — not applicable
ccache is the original C/C++ compiler cache. It does not support Rust. sccache was created as a Rust-aware successor. For jackin❯'s mixed Rust + C/C++ workload, sccache or kache both subsume ccache's C/C++ coverage while adding Rust.
CARGO_INCREMENTAL=0 alone — necessary but insufficient
Setting CARGO_INCREMENTAL=0 eliminates the incremental session directory explosion (64 GiB in the July audit). It is already set in jackin❯ CI alongside sccache. But it does not:
- Deduplicate artifacts across target dirs.
- Provide cache hits (rebuilds are full, not incremental).
- Manage or clean target dirs.
- Cache C/C++ compilation.
It is a prerequisite for any sccache/kache setup (both require or benefit from incremental disabled), not a standalone solution.
Recommendation
Primary: kache for host + containers; sccache retained for CI until kache-action is proven.
The disk deduplication problem is structural — multiple concurrent agent builds with separate CARGO_TARGET_DIR values creating near-identical artifact copies. Only kache's reflink/content-addressed approach solves this at the storage layer. sccache, no matter how well configured, still results in full-copy target dirs.
For CI, sccache is retained because it is already wired, has GHA cache integration, and CI runners are ephemeral (disk dedup is less critical than cache-hit speed).
If kache proves unstable or insufficient in practice, sccache with SCCACHE_DIR scoped under the jackin❯ cache root and SCCACHE_CACHE_SIZE capped remains the fallback. The env-var surface (RUSTC_WRAPPER, CARGO_INCREMENTAL) is the same for both tools, so switching is a one-variable change.
Filesystem behavior matrix
The disk savings from any build cache depend heavily on the filesystem. This matrix shows what happens in each environment jackin❯ operates in:
| Filesystem | reflink (CoW) | hardlink | kache restore strategy | Dedup benefit |
|---|---|---|---|---|
| APFS (macOS host) | Yes | Yes | reflink — zero-copy clone, independent inode | Maximum — target dirs share blocks via CoW |
| VirtioFS (OrbStack bind mount) | Unlikely¹ | Yes² | hardlink or copy | Partial — content-addressed store deduplicates; target dirs may still copy |
| ext4 (Linux container root) | No³ | Yes | hardlink (immutable artifacts), copy (bin/dylib) | Partial — store deduplicates; targets use hardlinks where possible |
| btrfs (Linux) | Yes | Yes | reflink | Maximum |
| tmpfs | No | Yes | hardlink or copy | Partial |
| NTFS (Windows) | No | No⁴ | copy | None — full copies |
¹ VirtioFS passes through to the host APFS filesystem but does not expose clonefile(2) / FICLONE ioctl to the guest. kache detects reflink failure and falls back to hardlink or copy. The content-addressed store still deduplicates; only the target-dir copy is affected.
² Hardlinks work on VirtioFS for immutable artifacts (.rlib/.rmeta) because they share the same underlying inode on the host APFS filesystem. Mutable artifacts (bin/dylib/proc-macro) are copied to prevent post-link modification from corrupting the store.
³ ext4 does not support reflink/COW. reflink=on requires btrfs or XFS with reflink=1 mount option.
⁴ Windows does not expose a portable inode link count. kache reports zero hardlink savings on Windows.
Key insight: Even when reflinks are unavailable (containers), kache's content-addressed store means the cache itself is deduplicated — identical blobs stored once regardless of how many builds reference them. The target-dir duplication is the remaining cost, and it is bounded by kache gc eviction plus kache clean for stale dirs.
Design
Research notes
- Cargo stores build output in
targetandbuilddirectories, defaulting to a workspace-localtargetdirectory;CARGO_TARGET_DIR, Cargo configbuild.target-dir, or--target-dircan redirect it. See the Cargo Book build-cache reference. - Cargo workspaces already share one output directory within a workspace, but Cargo does not currently provide a stable built-in user-wide target cache for every workspace. Cargo tracks the requested shared cross-workspace cache, but it remains future work rather than a shipped feature jackin❯ can depend on.
- A single global
CARGO_TARGET_DIRcan reduce duplicate target trees, but it has sharp edges for jackin❯: concurrent builds can serialize on Cargo locks,cargo cleancan remove artifacts for unrelated workspaces, and path-dependency identity/collision issues have been reported when multiple workspaces share one target directory. See Cargo issue rust-lang/cargo#12516. - sccache (mozilla/sccache, 7.5k stars, Apache-2.0) is a ccache-like compiler wrapper for Rust and C/C++. It stores compiler results on local disk or remote backends (Redis, S3, GCS, Azure, GHA, Memcached, WebDAV, OSS, COS). Local disk storage has explicit controls:
SCCACHE_DIRchanges the cache location,SCCACHE_CACHE_SIZEcaps size (default 10 GB). Multi-level cache chains (disk,redis,s3) with automatic backfill are supported. Path normalization across checkouts is viaSCCACHE_BASEDIRS. sccache documents that incrementally compiled crates cannot be cached —CARGO_INCREMENTAL=0is required. See the sccache README and Configuration reference. - kache (kunobi-ninja/kache, v0.10.0, Apache-2.0) is a zero-copy, content-addressed build cache for Rust and C/C++. Cache keys are blake3 hashes of normalized compiler inputs (rustc version, crate name, source content, dependencies, flags, target triple, compile-time env). Local hits restore zero-copy: reflink (copy-on-write) on APFS/btrfs/XFS-with-reflink, hardlink for immutable artifacts (
.rlib/.rmeta) on other filesystems, copy for mutable outputs (bin/dylib/proc-macro). The store is content-addressed — identical blobs stored once, shared across all cache entries. Incremental compilation is disabled automatically while kache wraps rustc.kache initconfiguresRUSTC_WRAPPER+ daemon;kache cleanfinds target/ dirs with cache breakdown;kache gcprovides LRU/age-based eviction;kache doctordiagnoses setup;kache monitoris a live TUI dashboard. S3 sync (AWS, R2, Ceph, MinIO) for remote cache. Available via mise (mise use -g github:kunobi-ninja/kache@latest), Homebrew, cargo-binstall, APT, winget, AUR. See the kache docs and deduplication reference. - Incremental compilation (
-C incremental) is Cargo's per-session optimization for fast rebuilds after small source changes. It creates per-session directories undertarget/debug/incremental/that are never garbage-collected. Concurrent cargo invocations each create their own session directories, multiplying the cache. Disabling it (CARGO_INCREMENTAL=0) eliminates this class of disk growth at the cost of slower iterative development — but sccache/kache artifact caching compensates by making full rebuilds fast. See the Cargo profiles reference. - APFS clonefile (
clonefile(2),FICLONEioctl) creates a copy-on-write clone of a file — zero disk cost until one side modifies the data. kache uses this on APFS to restore cached artifacts intotarget/without duplicating blocks. Multiple target dirs sharing the same compiled crate consume disk only once. See kache's deduplication reference. - The Rust project has explicitly discussed cache cleaning as an ongoing Cargo concern, including global cache cleanup and future target-directory tracking work. See the Rust Blog cache-cleaning post.
Proposed direction
Introduce a Rust build-cache hygiene program for jackin❯ with five layers:
- Inventory and ownership. Define every jackin❯-managed cache class: project build targets under
~/.cache/jackin/projects, global Cargo registry/git caches, compiler-result cache (kache or sccache), generated role/agent prefetch caches, docs/build output, PR verification bundles, and per-workspacetarget/paths when jackin❯ intentionally sets them. Each class gets an owner, purpose, default retention policy, and deletion safety rule. - Out-of-box compiler cache. Install and wire a compiler-result cache automatically in every jackin❯ container — no operator setup required. The construct image ships with the cache tool installed; the runtime env sets
RUSTC_WRAPPERandCARGO_INCREMENTAL=0; a shared cache store is bind-mounted into all containers for a project (like the existing Cargo registry mount). See Out-of-box integration below. - Bounded defaults. Do not create unbounded Rust build targets under
~/.cache/jackin. For Rust-heavy jackin❯-managed builds, prefer a bounded policy: compiler cache enabled by default, cache size capped (KACHE_CACHE_DIRorSCCACHE_CACHE_SIZE), cache store scoped under the jackin❯ cache root, andCARGO_TARGET_DIRscoped per project or per verification bundle only when a cleanup owner exists. - Prune and doctor integration. Extend
jackin pruneandjackin doctorso operators can see and clean Rust build/cache classes separately from role images, instance state, and registry caches. The first useful operator surface is likelyjackin prune cache --dry-runshowing bytes by class, followed by explicit--rust-builds,--compiler-cache, or equivalent class selectors. If kache is the active cache,kache cleanandkache gccan be delegated to for rich target-dir management. - Automatic guardrails. Add a soft budget checker that warns before a jackin❯-owned cache root crosses a configured threshold, and an optional TTL/least-recently-used cleanup path for safe classes. Automatic deletion must never remove operator source trees or non-jackin❯ caches silently; it may only touch paths jackin❯ owns and labels.
Out-of-box integration
The goal: when an operator spawns a jackin❯ container for a Rust project, the container automatically has a working compiler cache with incremental compilation disabled — no manual mise install, no env-var editing, no kache init/sccache --start-server. The cache is shared across all containers for the same project, and it is visible in jackin doctor / jackin prune.
Construct image changes
docker/construct/Dockerfile
Add the compiler cache tool to the construct image. Two options:
Option A — kache (recommended):
# Install kache for zero-copy build caching (reflink on APFS, hardlink elsewhere).
# kache disables incremental compilation automatically when wrapping rustc.
RUN curl -fsSL https://r2.kunobi.com/kache/apt/gpg.key | gpg --dearmor -o /etc/apt/keyrings/kache.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/kache.gpg] https://r2.kunobi.com/kache/apt stable main" \
> /etc/apt/sources.lists.d/kache.list && \
apt-get update && apt-get install -y --no-install-recommends kache && \
rm -rf /var/lib/apt/lists/*This follows the construct image's AGENTS.md rule: prefer the official package-manager source (APT for kache on Debian). If the APT package is unavailable for the target arch, fall back to mise use -g github:kunobi-ninja/kache@latest (mise is already installed in the image).
Option B — sccache (fallback, already in mise.toml):
sccache is already pinned in mise.toml as cargo:sccache = "0.16.0". No Dockerfile change needed — mise installs it. But the env vars must be set (see below).
Container environment
Set in the Dockerfile or the runtime env injection (crates/jackin-capsule/src/session.rs / crates/jackin-runtime/src/runtime/docker_profile.rs):
# Disable incremental compilation — prevents unbounded session-dir growth.
# Both sccache and kache require/benefit from this.
CARGO_INCREMENTAL=0
# Wire the compiler cache wrapper. Use whichever tool is installed.
RUSTC_WRAPPER=kache # or: RUSTC_WRAPPER=sccache
# Scope the cache store under the jackin❯-owned cache root.
# This dir is bind-mounted from the host (see below).
KACHE_CACHE_DIR=/home/agent/.cache/kache # kache
# SCCACHE_DIR=/home/agent/.cache/sccache # sccache alternative
# SCCACHE_CACHE_SIZE=20G # sccache size capShared cache mount
Add a bind mount in the container spawn logic, parallel to the existing Cargo registry mount:
~/.cache/jackin/global/kache → /home/agent/.cache/kacheOr for sccache:
~/.cache/jackin/global/sccache → /home/agent/.cache/sccacheThis makes all containers for all projects share one compiler cache. A crate compiled in one container is a cache hit in another. The mount follows the same pattern as the existing:
~/.cache/jackin/global/cargo/registry → /home/agent/.cargo/registry
~/.cache/jackin/global/cargo/git → /home/agent/.cargo/gitmise.toml [env] section
Add an [env] section to mise.toml so local development builds (outside containers) also use the cache:
[env]
CARGO_INCREMENTAL = "0"
RUSTC_WRAPPER = "kache"
# kache disables incremental automatically; CARGO_INCREMENTAL=0 is belt-and-suspenders
# for environments where kache is not yet installed.CI workflow alignment
jackin❯ CI already uses sccache (RUSTC_WRAPPER: sccache, CARGO_INCREMENTAL: "0") across all workflows. If kache is adopted for local/container use, CI can either:
- Keep sccache — CI runners are ephemeral, disk dedup is less critical than cache-hit speed. sccache's GHA cache integration is proven. This is the pragmatic default.
- Switch to kache — use
kache-actioninstead ofsccache-action. Evaluate after local/container adoption proves kache's stability.
Both tools use RUSTC_WRAPPER, so the CI env-var surface is identical. Switching is a one-line workflow change per job.
Operator visibility
Extend jackin doctor to report:
Rust build cache:
Tool: kache 0.10.0
Cache store: ~/.cache/jackin/global/kache (12.4 GiB / 20 GiB budget)
Target dirs: 3 active (~45 GiB total, ~18 GiB after reflink dedup)
Incremental: disabled (CARGO_INCREMENTAL=0)Extend jackin prune cache to delegate to the cache tool:
# kache:
jackin prune cache --rust-builds # runs `kache clean` (interactive TUI)
jackin prune cache --compiler # runs `kache gc --max-age 7d`
# sccache:
jackin prune cache --compiler # stops server, clears SCCACHE_DIR, restartsHost setup (operator's macOS machine)
For operators who also build Rust on the host (not just inside containers), install the cache tool globally:
kache (recommended for APFS reflink benefits):
mise use -g github:kunobi-ninja/kache@latest
kache init # configures RUSTC_WRAPPER in ~/.cargo/config.toml + starts daemon
kache doctor # verify setupsccache:
brew install sccache
# Add to ~/.cargo/config.toml:
# [build]
# rustc-wrapper = "sccache"
# Set in shell profile:
# export CARGO_INCREMENTAL=0
# export SCCACHE_CACHE_SIZE=20GOn the host (native APFS), kache's reflink deduplication gives maximum benefit: every target/ dir across every Rust project shares blocks with the content-addressed store, so 10 project target dirs don't consume 10× the disk.
Specific local-case recommendation
For the disk layout that triggered this item, the target architecture is a three-cache model:
- Shared downloads (already shipped): Cargo registry and git checkouts in a shared cache mounted into all containers (
~/.cache/jackin/global/cargo/). - Shared compiler results (new): kache or sccache store in a shared cache mounted into all containers (
~/.cache/jackin/global/kache/). This addresses the duplicate "same crate compiled in many containers" problem. With kache, the content-addressed store means identical blobs are stored once regardless of how many target dirs reference them. - Bounded build-output targets (existing, with new budget): Cargo
targetoutput scoped by project/workspace, under~/.cache/jackin/projects/. These remain disposable, visible indoctor, and removable byprune. With kache on APFS, the target dirs are reflink-backed — their logical size may be large but their physical disk cost is bounded by the unique-artifact count, not the directory count.
# Jackin❯-managed env vars for Rust build cache:
CARGO_INCREMENTAL=0 # eliminate incremental session explosion
RUSTC_WRAPPER=kache # zero-copy compiler cache
KACHE_CACHE_DIR="$JACKIN_CACHE/global/kache" # shared store, bind-mounted
# Project-specific (scoped, disposable):
CARGO_TARGET_DIR="$JACKIN_CACHE/projects/<project-key>/target"Rejected defaults
- One global
CARGO_TARGET_DIRfor all operator Rust workspaces. It is attractive because it removes manytarget/folders, but it mixes unrelated projects, makescargo cleanblast-radius confusing, and can serialize or collide under parallel builds. It may still be useful as an advanced opt-in for a single operator who accepts those trade-offs. - sccache as the sole solution. sccache makes rebuilds fast after
cargo cleanbut does not deduplicate disk across multiple target dirs. The July 2026 audit showed that the problem is not just "rebuilds are slow" — it is "the same artifacts exist in 5+ target dirs and consume 200+ GiB." Only kache's reflink/content-addressed approach addresses this at the storage layer. - Leaving the choice to the operator. If the compiler cache is not configured out-of-box, most operators will never set it up, and the disk pressure problem recurs. jackin❯ should ship with a working default.
Policy questions
- Should jackin❯ ship kache as the default compiler cache, or sccache (already in CI), or both with a preference order?
- Should the default Rust build target cache be per project, per branch, per PR verification bundle, or shared by a stable workspace/project key?
- Which cache classes are safe for automatic TTL eviction, and which require explicit operator confirmation because deletion causes expensive rebuilds?
- Should cache budget live globally, per workspace, or both?
- Should remote/shared cache backends (kache S3, sccache Redis/S3) be role-managed configuration, operator global configuration, or out of scope for V1?
- Should jackin❯ expose an advanced single-
CARGO_TARGET_DIRmode for operators who prefer disk deduplication over parallel-build isolation? - Should the construct image install kache via APT (official package) or via mise (already installed, consistent with other tools)? The construct image AGENTS.md prefers official package-manager sources when available.
- How should jackin❯ handle the case where an external agent (e.g. OpenAI Codex CLI) sets its own
CARGO_TARGET_DIRinside the shared target? Should jackin❯ intercept and redirect, or let it create separate dirs (relying on kache reflinks to deduplicate)?
Tasks
- Audit all jackin❯ code paths that create host-side cache or build-output directories, including project cache roots, PR verification bundles, prewarm/build flows, generated role/agent cache mounts, and docs/build outputs.
- Add a contributor reference table for cache classes: path owner, creation path, safe deletion condition, prune command, default budget, and visibility surface.
- Decide the Rust build-cache architecture: kache vs sccache (or both), where the cache store lives, what default cache size/budget should be, and whether
CARGO_TARGET_DIRshould be redirected for jackin❯-managed Rust builds. - Add the compiler cache tool to the construct image (
docker/construct/Dockerfile) via APT or mise. - Add
CARGO_INCREMENTAL=0,RUSTC_WRAPPER=kache(or sccache), andKACHE_CACHE_DIR(orSCCACHE_DIR) to the container runtime environment (crates/jackin-capsule/src/session.rs,crates/jackin-runtime/src/runtime/docker_profile.rs). - Add a shared compiler-cache bind mount (
~/.cache/jackin/global/kacheor~/.cache/jackin/global/sccache) to the container spawn logic, parallel to the existing Cargo registry mount. - Add an
[env]section tomise.tomlwithCARGO_INCREMENTAL = "0"andRUSTC_WRAPPERfor local development builds. - Extend
jackin pruneto report and clean Rust build artifacts and compiler caches. Delegate tokache clean/kache gcwhen kache is the active tool. - Add
jackin doctoroutput that flags jackin❯-owned cache roots above budget, reports cache tool status, and names the exact cleanup command. - Add tests around cache classification so new cache roots cannot be introduced without owner/policy metadata.
- Document operator-facing setup flows (host install) and cleanup flows once behavior ships; keep internal path and policy details in contributor reference docs.
- Evaluate kache stability over a 2-4 week trial period in local/container builds before deciding whether to also switch CI from sccache to kache.
Related work
crates/jackin-runtime/src/runtime/cleanup.rs— existing prune/delete machinery.crates/jackin-runtime/src/runtime/prune_output.rs— current prune output surface.crates/jackin-runtime/src/runtime/prewarm_trigger.rs— prewarm flows that trade disk for speed.crates/jackin-runtime/src/runtime/docker_profile.rs— container spawn argv + env injection + mount assembly.crates/jackin-capsule/src/session.rs— in-container runtime env setup.crates/jackin-core/src/agent/runtime.rs— generated role/agent build cache mounts.crates/jackin-core/src/agent/adapters/claude.rs— agent prefetch cache mount precedent.docker/construct/Dockerfile— construct image definition (where cache tool would be installed).mise.toml— tool pinning + env config (where[env]section + cache tool version would be added)..cargo/config.toml— Cargo config (wherebuild.rustc-wrappercould be set as a fallback).docs/content/(public)/commands/prune.mdx— future operator cleanup documentation.docs/content/(public)/commands/doctor.mdx— future diagnostics/warning documentation.- Workspace registry cache — adjacent cache program for DinD image/layer reuse.
- sccache — GitHub — compiler cache (Rust + C/C++), Mozilla.
- sccache configuration reference — env vars, multi-level cache, all backends.
- kache — GitHub — zero-copy content-addressed build cache (Rust + C/C++), Kunobi.
- kache docs — architecture, cache key, deduplication, commands.
- kache deduplication reference — content-addressed blobs, reflink/hardlink/copy fallback matrix.
- kache cache key reference — what's hashed, cross-machine portability, path normalization.