mitts 0.3.21

Rust-first build and package tooling with a shared artifact cache
Documentation
<!doctype html>
<!--
 * Mitts Build — project website
 * Copyright (c) 2026 Alexander Panasenko
 *
 * Contact: alex@prod.codes
 * Author: https://prod.codes/about/
 * Project: https://github.com/alex09x/mitts
 * SPDX-License-Identifier: MIT OR Apache-2.0
 -->
<html lang="en">
<head>
  <!-- Google tag (gtag.js) -->
  <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 &amp; 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>&lt;unset&gt;</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 &amp; action cache</li>
        <li><b>M3</b>Parallel fetcher &amp; lockfile resolution</li>
        <li><b>M4</b>Native parallel DAG runner</li>
        <li><b>M5</b>Pilot, deduplication &amp; 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 &amp; 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>