<!doctype html>
<html lang="en">
<head>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-TKQKJ8LG5Z"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-TKQKJ8LG5Z');
</script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#121315">
<meta name="description" content="Mitts is a Rust-first build system with a machine-wide, content-addressed artifact cache and explicit input tracking. It reads your existing Cargo.toml and Cargo.lock.">
<meta name="author" content="Alexander Panasenko">
<title>Mitts — build work, accounted for</title>
<link rel="icon" href="favicon.ico?v=3" sizes="any">
<link rel="icon" href="logo.svg?v=3" type="image/svg+xml">
<link rel="icon" href="icons/icon-32.png" sizes="32x32" type="image/png">
<link rel="icon" href="icons/icon-16.png" sizes="16x16" type="image/png">
<link rel="apple-touch-icon" href="icons/apple-touch-icon.png">
<link rel="manifest" href="site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500;12..96,700;12..96,800&family=IBM+Plex+Sans:wght@400;500&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="shell topbar">
<a class="brand" href="#top" aria-label="Mitts home">
<img class="mark" src="logo.svg?v=3" alt="" width="20" height="20">
<span>mitts<span class="dim">/build</span></span>
</a>
<nav class="nav" aria-label="Main navigation">
<a href="#problem">Problem</a>
<a href="#how">How it works</a>
<a href="#architecture">Architecture</a>
<a href="#compare">Compare</a>
<a href="#limits">Limits</a>
<a href="#start">Start</a>
</nav>
</header>
<main id="top">
<section class="shell hero" aria-labelledby="hero-title">
<div class="hero-text">
<p class="kicker"><span class="dot"></span>Rust first · reads your existing Cargo.toml</p>
<h1 id="hero-title">Build work,<br><span class="accent">accounted for.</span></h1>
<p class="lede">Mitts plans a Cargo workspace itself and calls <code>rustc</code> directly. Every compile action is keyed by its full set of inputs, stored once in a machine-wide immutable store, and restored into every workspace that needs it — across projects, worktrees and branches. If an input can't be accounted for, the action is rebuilt, not guessed.</p>
<div class="actions">
<a class="button" href="#how">How it works</a>
<a class="button ghost" href="#start">Quick start</a>
</div>
</div>
<figure class="ledger" aria-label="A compile action's inputs combine into an action key, which maps to an immutable content-addressed object restored into the workspace target.">
<div class="ledger-head"><span>action / rlib</span><span class="ok">inputs tracked</span></div>
<ol class="ledger-rows">
<li><span>src</span><code>source set · Cargo.toml</code></li>
<li><span>deps</span><code>dependency action keys</code></li>
<li><span>tool</span><code>rustc · target triple</code></li>
<li><span>cfg</span><code>features · profile · flags</code></li>
<li><span>env</span><code>tracked environment</code></li>
</ol>
<div class="ledger-sum"><span>action key</span><code>sha256(inputs)</code></div>
<div class="ledger-flow">
<div><small>shared store</small><code>cas/ab/3f…</code><em>immutable</em></div>
<span class="arrow" aria-hidden="true">→</span>
<div><small>workspace</small><code>target/mitts/</code><em>isolated</em></div>
</div>
</figure>
</section>
<section class="shell section" id="problem" aria-labelledby="problem-title">
<header class="section-head">
<span class="num">01</span>
<div>
<h2 id="problem-title">The same crates, compiled again and again.</h2>
<p>Cargo's cache lives in each workspace's <code>target/</code>. Anything that creates a new one starts from zero.</p>
</div>
</header>
<div class="grid4">
<article class="card"><span class="tag">worktrees</span><h3>A second checkout</h3><p>A new clone or <code>git worktree</code> of the same repository rebuilds every dependency.</p></article>
<article class="card"><span class="tag">branches</span><h3>Switching back</h3><p>A branch that changes <code>Cargo.lock</code> evicts work you'll need again after the switch back.</p></article>
<article class="card"><span class="tag">projects</span><h3>Shared dependencies</h3><p>Two projects using the same <code>serde</code>, <code>syn</code> or <code>tokio</code> build and store them twice.</p></article>
<article class="card"><span class="tag">agents</span><h3>Parallel work</h3><p>Coding agents in separate worktrees each pay the full build, at the same time.</p></article>
</div>
<div class="split workarounds">
<ul class="checklist">
<li class="no"><span><b>A shared <code>CARGO_TARGET_DIR</code></b> serializes builds behind one lock, and feature sets overwrite each other.</span></li>
<li class="no"><span><b>A compiler wrapper such as sccache</b> sees one <code>rustc</code> call at a time: keys depend on absolute paths unless normalized, linking crates aren't cached, and Cargo still runs every build script.</span></li>
</ul>
<p class="note">Mitts replaces Cargo's executor, not the compiler. It sees the whole unit graph — so it can key each unit by its dependencies, cache linking units and build-script runs, and let builds share one store safely.</p>
</div>
</section>
<section class="shell section" id="how" aria-labelledby="how-title">
<header class="section-head">
<span class="num">02</span>
<div>
<h2 id="how-title">Reuse follows the inputs.</h2>
<p>Five phases, from your manifests to restored outputs. A cache hit counts only when the effective inputs match exactly.</p>
</div>
</header>
<ol class="pipeline">
<li><b>resolve</b><span>Reuse <code>Cargo.lock</code>, or resolve against the crates.io sparse index.</span></li>
<li><b>fetch</b><span>Download <code>.crate</code> archives in parallel, verify, unpack once per machine.</span></li>
<li><b>plan</b><span>Units — lib, bin, test, build script, proc macro — with features and edges, by Cargo's resolver rules.</span></li>
<li><b>key</b><span>SHA-256 over toolchain, target, profile, flags, features, sources, dependency keys and environment.</span></li>
<li><b>execute</b><span>Parallel DAG of direct <code>rustc</code> calls, pipelined on <code>.rmeta</code>. Hit: verify and restore. Miss: lock the key, compile, publish atomically.</span></li>
</ol>
<div class="grid3">
<article class="card">
<span class="tag">keys</span>
<h3>Keys chain down the graph</h3>
<p>A unit's key includes its dependencies' keys, not their bytes. A change anywhere below a unit changes its key.</p>
</article>
<article class="card">
<span class="tag">build scripts</span>
<h3>Scripts are cached too</h3>
<p>Their directives enter the key of the unit they configure; their <code>rerun-if-*</code> declarations decide when a cached run is reused.</p>
</article>
<article class="card">
<span class="tag">fast path</span>
<h3>Nothing changed, nothing done</h3>
<p>An unchanged workspace skips planning and keying. Any mismatch falls back to the full keyed build.</p>
</article>
</div>
</section>
<section class="shell section" id="architecture" aria-labelledby="arch-title">
<header class="section-head">
<span class="num">03</span>
<div>
<h2 id="arch-title">A neutral core, language adapters on top.</h2>
<p>The store, action cache, locks and GC know nothing about Cargo. The Rust adapter turns a Cargo workspace into keyed actions; other languages plug into the same core.</p>
</div>
</header>
<div class="arch" role="img" aria-label="The mitts CLI drives language adapters — Rust, and an experimental C/C++ adapter — which emit actions and keys to a language-neutral core: content-addressed store, action cache, key locks, jobserver, fast path and GC.">
<div class="arch-layer"><small>cli</small><code>build · run · check · clippy · fmt · test · coverage · explain · fetch · gc · clean</code></div>
<div class="arch-row">
<div class="arch-layer accent-edge"><small>rust adapter</small><code>manifest · resolver · fetcher · features · planner · keys · DAG runner · build scripts · doctests</code></div>
<div class="arch-layer dim-edge"><small>c/c++ adapter · experimental</small><code>Mitts.toml · compile · link</code></div>
</div>
<div class="arch-arrow" aria-hidden="true">actions + keys ↓</div>
<div class="arch-layer core"><small>core</small><code>content-addressed store · action cache · key locks · jobserver · fast path · GC · reflinks</code></div>
</div>
<ul class="facts">
<li><b>Immutable objects.</b> Published read-only, verified by hash before use, restored by reflink or copy. Never hardlinked into a writable workspace.</li>
<li><b>Atomic publish.</b> Outputs are staged, then renamed into the store. An interrupted build never leaves a partial object.</li>
<li><b>Cooperating builds.</b> Per-key locks with double-checked lookup: two builds that need the same unit compile it once.</li>
<li><b>One jobserver.</b> Parallelism is bounded across every Mitts process on the machine.</li>
</ul>
</section>
<section class="shell section" id="inputs" aria-labelledby="inputs-title">
<header class="section-head">
<span class="num">04</span>
<div>
<h2 id="inputs-title">What invalidates the cache.</h2>
<p>Every input below must match for a hit. If Mitts can't see an input, it does not reuse the result.</p>
</div>
</header>
<div class="table-wrap">
<table class="table inputs-table">
<colgroup><col class="c1"><col></colgroup>
<thead><tr><th>Input</th><th>Tracked as</th></tr></thead>
<tbody>
<tr><td>Toolchain</td><td><code>rustc -vV</code> output</td></tr>
<tr><td>Target & profile</td><td>target triple, profile settings, <code>RUSTFLAGS</code> and Cargo config rustflags</td></tr>
<tr><td>Features</td><td>the unit's resolved feature set, resolver 1 or 2 as Cargo would</td></tr>
<tr><td>Sources</td><td>content hashes of package files and every file rustc reports reading</td></tr>
<tr><td>Dependencies</td><td>action keys of all dependency units</td></tr>
<tr><td>Environment</td><td><code>CARGO_*</code>, <code>RUST_*</code>, wrappers, and every variable read through <code>env!</code> / <code>option_env!</code>, with <code><unset></code> recorded</td></tr>
<tr><td>Cargo configuration</td><td>the <code>.cargo/config(.toml)</code> files Cargo would read</td></tr>
<tr><td>Build scripts</td><td>directives, <code>OUT_DIR</code>, <code>rerun-if-changed</code> contents and <code>rerun-if-env-changed</code> values</td></tr>
</tbody>
</table>
</div>
</section>
<section class="shell section" id="compare" aria-labelledby="compare-title">
<header class="section-head">
<span class="num">05</span>
<div>
<h2 id="compare-title">Where it sits.</h2>
<p>How each tool reuses work. A qualitative comparison, not a performance claim.</p>
</div>
</header>
<div class="table-wrap">
<table class="table compare">
<colgroup><col class="c1"><col class="c2"><col class="c3"><col class="c4"></colgroup>
<thead><tr><th></th><th>Cargo</th><th>Cargo + sccache</th><th class="hl">Mitts</th></tr></thead>
<tbody>
<tr><td>Reads <code>Cargo.toml</code> / <code>Cargo.lock</code> as is</td><td data-label="Cargo">yes</td><td data-label="Cargo + sccache">yes</td><td data-label="Mitts" class="hl">yes</td></tr>
<tr><td>Reuse across workspaces and worktrees</td><td data-label="Cargo">no, per <code>target/</code></td><td data-label="Cargo + sccache">yes, per <code>rustc</code> call</td><td data-label="Mitts" class="hl">yes, per unit</td></tr>
<tr><td>Key depends on the checkout path</td><td data-label="Cargo">—</td><td data-label="Cargo + sccache">yes, unless normalized</td><td data-label="Mitts" class="hl">no for registry crates; workspace crates when not eligible</td></tr>
<tr><td>Caches <code>bin</code>, test and <code>proc-macro</code> units</td><td data-label="Cargo">—</td><td data-label="Cargo + sccache">no</td><td data-label="Mitts" class="hl">yes</td></tr>
<tr><td>Caches build-script runs</td><td data-label="Cargo">no</td><td data-label="Cargo + sccache">no</td><td data-label="Mitts" class="hl">yes</td></tr>
<tr><td>Concurrent builds of one dependency</td><td data-label="Cargo">wait on the <code>target/</code> lock</td><td data-label="Cargo + sccache">both may compile on a miss</td><td data-label="Mitts" class="hl">one compiles, the other reuses</td></tr>
<tr><td>Remote / team cache</td><td data-label="Cargo">no</td><td data-label="Cargo + sccache">yes</td><td data-label="Mitts" class="hl">planned</td></tr>
<tr><td>Windows</td><td data-label="Cargo">yes</td><td data-label="Cargo + sccache">yes</td><td data-label="Mitts" class="hl">no</td></tr>
</tbody>
</table>
</div>
<p class="aside">Bazel and Buck2 solve a larger problem — hermetic, multi-language builds with remote execution — but need their own build files instead of <code>Cargo.toml</code>.</p>
</section>
<section class="shell section" id="limits" aria-labelledby="limits-title">
<header class="section-head">
<span class="num">06</span>
<div>
<h2 id="limits-title">A deliberate cache boundary.</h2>
<p>Mitts caches the actions it can describe completely. Everything else builds normally, and <code>mitts explain</code> says why.</p>
</div>
</header>
<div class="split">
<ul class="checklist">
<li class="yes">Library, binary, test and proc-macro units with matching keys</li>
<li class="yes"><span>Build scripts, re-run only when a declared <code>rerun-if</code> input changes</span></li>
<li class="yes">Concurrent builds compile each shared unit once</li>
<li class="yes">Your own crates across checkouts, when their key can be made path-independent</li>
<li class="no"><span><code>cdylib</code>, <code>dylib</code>, <code>staticlib</code> crates and untrackable <code>env!</code> reads</span></li>
<li class="no">Inputs a build script reads without declaring them</li>
<li class="no">Windows, cross-compilation and a remote cache — not yet</li>
</ul>
<p class="note">Unknown inputs are never treated as unchanged. If Mitts can't prove an action is compatible, it builds it — and unsupported Cargo features fail loudly instead of guessing.</p>
</div>
</section>
<section class="shell section" id="start" aria-labelledby="start-title">
<header class="section-head">
<span class="num">07</span>
<div>
<h2 id="start-title">A small CLI.</h2>
<p>No new manifest and no second lockfile. Build from source, then point it at a Cargo workspace. Cargo and rust-analyzer keep working next to it.</p>
</div>
</header>
<pre class="term"><code><span class="p">$</span> cargo install --path . <span class="c"># from a clone of the repository</span>
<span class="p">$</span> cd your-workspace
<span class="p">$</span> mitts build</code></pre>
<dl class="cli">
<div><dt>mitts build</dt><dd>Compile the package and its dependencies into <code>target/mitts/</code></dd></div>
<div><dt>mitts run</dt><dd>Build and run a binary of the local package</dd></div>
<div><dt>mitts check · clippy · fmt</dt><dd>Type-check, lint and format without producing binaries</dd></div>
<div><dt>mitts test · coverage</dt><dd>Unit, integration and doc tests with Cargo-style target selection; LLVM source-based coverage</dd></div>
<div><dt>mitts explain</dt><dd>The build plan, every unit, and why it is cached or not</dd></div>
<div><dt>mitts fetch</dt><dd>Prefetch dependencies into the global source store</dd></div>
<div><dt>mitts gc</dt><dd>Prune old or unreferenced objects within storage quotas</dd></div>
<div><dt>mitts clean</dt><dd>Remove this workspace's Mitts artifacts</dd></div>
</dl>
</section>
<section class="shell section" id="status" aria-labelledby="status-title">
<header class="section-head">
<span class="num">08</span>
<div>
<h2 id="status-title">Where it stands.</h2>
<p>Pre-release. Milestones M0–M6 are complete; hardening for real projects and a reproducible benchmark study are in progress. No performance numbers are published until that study is — with fixed hardware, cold, warm and changed-input runs, and the stronger of Cargo and sccache as the baseline.</p>
</div>
</header>
<ol class="milestones">
<li><b>M1</b>Uncached build correctness</li>
<li><b>M2</b>Content-addressed store & action cache</li>
<li><b>M3</b>Parallel fetcher & lockfile resolution</li>
<li><b>M4</b>Native parallel DAG runner</li>
<li><b>M5</b>Pilot, deduplication & conservative GC</li>
<li><b>M6</b>Second adapter: C/C++</li>
<li class="wip"><b>M7</b>Hardening for real projects</li>
<li class="wip"><b>M8</b>Reproducible benchmarks</li>
<li class="plan"><b>M9</b>Remote & team cache</li>
</ol>
</section>
<section class="shell author" id="author" aria-labelledby="author-title">
<div>
<span class="tag">author</span>
<h2 id="author-title">Alexander Panasenko</h2>
</div>
<p><a href="https://prod.codes/about/">prod.codes/about</a><a href="mailto:alex@prod.codes">alex@prod.codes</a></p>
</section>
</main>
<footer>
<div class="shell"><span>© 2026 Alexander Panasenko</span><span>MIT OR Apache-2.0</span><a href="https://mittsbuild.com">mittsbuild.com</a></div>
</footer>
</body>
</html>