# Rust

SlowShield serves crates.io at `https://slowshield.example.com/cargo/` as a sparse registry that replaces `crates-io` in Cargo's configuration. Needs Cargo 1.68 or later.

## Set it up

Cargo only takes source replacement from a configuration file, not from environment variables:

### Cargo

Release age: Cargo's own setting (min-publish-age) isn't stable yet: SlowShield is the only layer.

*~/.cargo/config.toml*

```
[source.crates-io]
replace-with = "slowshield"

[registries.slowshield]
index = "sparse+https://slowshield.example.com/cargo/"
```

*CI and Dockerfiles (appends to $CARGO_HOME/config.toml)*

```
mkdir -p "${CARGO_HOME:-$HOME/.cargo}" && printf '%s\n' '[source.crates-io]' 'replace-with = "slowshield"' '[registries.slowshield]' 'index = "sparse+https://slowshield.example.com/cargo/"' >> "${CARGO_HOME:-$HOME/.cargo}/config.toml"
```

- **Cargo.lock doesn't change.** It keeps crates.io as the source, with crates.io's checksums, so a project builds the same with or without SlowShield.

- **The official `rust` images** set `CARGO_HOME=/usr/local/cargo`, where `~/.cargo/config.toml` isn't read. The command writes `${CARGO_HOME:-$HOME/.cargo}/config.toml`, which works in both.

## What changes

- **The publish time** is each version's `pubtime` in the index, which crates.io sets and authors can't. The first one SlowShield sees is kept, so a rewritten index can't move it earlier.

- **Held versions show up as yanked.** Cargo resolves to the newest version that isn't, exactly as when a version is yanked upstream. When only held versions satisfy a requirement, Cargo says the version "is yanked".

- **A Cargo.lock that pins a held version** fails with the reason and the command to use instead:
   ```
   failed to get successful HTTP response from `…/cargo/crates/tokio/1.53.2/download`, got 403
   body:
   slowshield: tokio@1.53.2 is too new.
   It was published 2026-10-03T11:18:32Z (3.0 days ago); this proxy requires 7 days.
   It becomes available at 2026-10-10T11:18:32Z.
   Use an older version (cargo update -p tokio@1.53.2 --precise 1.53.1), or ask your SlowShield administrator for an exception.
   ```

- **Brand-new crates** none of whose versions is old enough are held too: Cargo doesn't fail open, because nearly all malicious crates are brand-new typosquats and impersonations.

- **Malware** from OSV (including RustSec's malicious advisories) and GitHub is refused with `451`, and a `.crate` is checked against the index's checksum and the fingerprint SlowShield recorded the first time.

## The second layer

Cargo's own setting, `min-publish-age`, isn't stable yet, so SlowShield is the only layer.

## In Docker and CI

```
FROM rust:1 AS build
ARG CARGO_INDEX=sparse+https://slowshield.example.com/cargo/
RUN if [ -n "$CARGO_INDEX" ]; then printf '%s\n' '[source.crates-io]' 'replace-with = "slowshield"' \
      '[registries.slowshield]' "index = \"$CARGO_INDEX\"" >> "$CARGO_HOME/config.toml"; fi
WORKDIR /src
COPY . .
RUN cargo build --release
```

Or run the Setup page's one-line command in a `RUN` step. [Building images](https://slowshield.org/docs/container-builds/) covers the base image too, and how to check that nothing in a build goes around SlowShield.

## Limits

- Commands that need the crates.io web API: `cargo search` fails, `cargo info` needs `--registry slowshield`, and `cargo publish`, `yank` and `owner` need `--registry crates-io`.

- Not covered: git dependencies, build scripts that download things, rustup toolchains, and alternative registries.

- Block index.crates.io and static.crates.io at the firewall for machines that should only use SlowShield.

---

This page as HTML: https://slowshield.org/docs/rust/. All of the guide in one file: https://slowshield.org/llms-full.txt
