Skip to content
shippy

Share a cache

This page shows the shortest path from one laptop's cache to a cache your whole team shares. Read it when you want a second machine, or CI, to reuse what the first one already compiled.

A shared cache has two sides:

  • The box: one Linux machine that runs shippyd, the server. It keeps the cache and runs compilations in a sandbox.
  • Clients: every machine that builds. They run shippy as usual and talk to the box over HTTP.
Loading diagram...

Set up the box

Install bubblewrap

sudo apt-get install -y bubblewrap

bubblewrap is the sandbox. The box uses it to re-run compilations that people upload, with no network and only the declared inputs.

Make tokens

shippyd token add ci-main --role trusted --tokens tokens.toml
shippyd token add alice --tokens tokens.toml

Each command prints a new token once, alone on standard output. The tokens file stores only its SHA-256, so a copy of the file lets nobody in. Hand the token to its owner through something that is not a chat log.

The name you give (ci-main, alice) is the principal: one identity with one role. A principal may have several tokens.

Start the server

shippyd --store /dev/shm/shippy --listen 0.0.0.0:7447 --tokens tokens.toml

The store is where the cache lives. /dev/shm is a tmpfs, so this keeps the cache in memory. A reboot empties it, which is fine for a first try. A real box keeps its store on disk (Run a box).

Connect a developer machine

shippy setup --remote http://box:7447 --token-file token.txt
shippy doctor
shippy build
  • shippy setup runs once per user. It writes ~/.config/shippy/config.toml and the token beside it, both readable by you alone, and asks the box whether it takes the token. --token-file - reads the token from standard input.
  • shippy doctor checks the toolchain, the wrapper, the store, where the remote and token come from, the proxy, the box's latency and clock, whether it takes the token and whether it is ready. Each problem comes with its fix. It exits with 1 when something is wrong.
  • shippy build is cargo build with shippy's compiler wrapper. Crates the box already has are restored instead of compiled.

Clients need no bubblewrap, unless a project sets fixed_paths.

Where the settings come from

Loading diagram...

The environment is how CI sets the remote and token: both, in the pipeline's own settings. A project's shippy.toml may name a remote, and it is used only by someone who named the same one, so a cloned repository cannot send your token, your sources or your results to another server. The file's token is sent to the file's remote only. Give the box an https address, or reach it over a network only you and it are on: over plain http the token travels unencrypted, and shippy setup says so.

When the box is down

A build never fails because of the box. If the box is unreachable, slow or refuses, the wrapper compiles locally and keeps what it compiled. The next build, or shippy push, uploads it.

Trusted and untrusted tokens

Every token has a role. The role decides what happens to what its holder uploads.

RoleWhat it may do
untrusted (the default)Everything a developer or an agent needs. Its uploads are claims: visible to itself only, until the box has compiled the same thing in its sandbox.
trustedIts uploads are public as they are.
metricsGET /metrics and nothing else. For a Prometheus scraper.
worker, trusted-workerA worker machine that runs sandbox jobs for the box (Workers).

Why the split? You cannot check a compiled .rlib by looking at it. The safe way to accept a stranger's result is to build it again from the same inputs. Trusted uploads skip that step because you vouch for the machine.

What happens to an untrusted upload

A claim is an untrusted principal's upload: a compiled result plus the recipe that made it (the exact compiler invocation and the digest of every input). The box re-runs the recipe in its sandbox.

Loading diagram...

What becomes public is always the box's own output, never the uploaded bytes. Nothing an untrusted principal uploads can change what another principal reads. See Trust for the full rules.

Why my edit-loop rebuilds are not uploaded

A rebuild that started from rustc's incremental state stays on your machine and is not uploaded. The box always compiles from nothing, so its result would never be the same bytes. SHIPPY_CLAIM_INCREMENTAL=1 (or claim_incremental = true under [cache]) uploads those too.

Manage tokens

shippyd token add alice-laptop2 --expires 90d --tokens tokens.toml
shippyd token list --tokens tokens.toml
shippyd token revoke alice --tokens tokens.toml     # every token of the principal
shippyd token revoke 3f9a1c2e --tokens tokens.toml  # one token, by its id
  • A running shippyd reads the tokens file again whenever it changes. A new token works with the next request, and a revoked one is refused with the next request. No restart.
  • A tokens file that does not parse lets nobody in until it is fixed. shippyd token never writes such a file.
  • Keep the file out of /usr and the Rust toolchains' directories. Sandboxed compilations can read those, and shippyd does not start with its tokens there.
  • Use one principal per person or machine pool, not one per token. Results and claims are kept per principal.

Rotate a token by adding a second one for the principal, moving the holder to it, and revoking the first by its id. Offboard someone with shippyd token revoke <principal>.

Toolchains on the box

The box re-runs a claim with the exact compiler its recipe names. It uses the toolchains rustup has on the box. Install what your developers build with:

rustup toolchain install 1.100.0 --profile minimal

An untrusted claim for a toolchain the box does not have stays a claim, and the log says why. You can let the box install missing toolchains itself:

SettingDefaultWhat it does
--install-toolchains (toolchains.install)noneToolchains to install when anyone's recipe names one, separated by commas: a release (1.100.0), a dated channel (nightly-2026-11-10) or a whole channel (stable, beta, nightly). Without it only a trusted principal's recipe installs one. Through a whole channel, an untrusted principal's recipe has only a toolchain near the newest one of that channel here installed: a stable release no older than two before the newest, a nightly or a beta of the twelve weeks before the newest (before today, where none of that channel is here), no stable release at all where none is here, and none into the last quarter of the places: those are for a trusted principal's recipe and for a toolchain named here itself.
--max-toolchains (toolchains.max_installed)8Install nothing while this many toolchains are installed. Make room with rustup toolchain uninstall.

The standard library of another --target is added only for a trusted principal's recipe.

Keep the box from filling up

The box keeps its store under a limit and checks it every 30 seconds. Untrusted principals' private data (claims, and blobs no public result names) goes first. Nothing an untrusted principal uploads costs anyone a public result.

SettingDefaultWhat it does
--max-store-size (store.max_size)autoHow large the store may get: 200GB, 64GiB, off, or auto (three quarters of the store's own size plus the free space of its filesystem).
--max-age (store.max_age)no limitAlso remove results nobody used for this long, and claims and packs this old.
--evict-grace (store.evict_grace)15mNever remove a public result stored or used within this long.
--principal-share (store.principal_share)10The percentage of the limit one untrusted principal may hold privately. Past it, requests get 507 until eviction makes room.

See Eviction for the order things go in.

Every flag is also a config key and a variable

Every shippyd flag is also a key in one config file and a SHIPPYD_* variable. shippyd config print-default prints the file with every key, its default and its variable. shippyd --config shippyd.toml reads it (else SHIPPYD_CONFIG, else /etc/shippy/shippyd.toml). The environment overrides the file, and flags override both. shippyd config check says what a start would find. See Server config.

Go deeper