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
rustimages setCARGO_HOME=/usr/local/cargo, where~/.cargo/config.tomlisn't read. The command writes${CARGO_HOME:-$HOME/.cargo}/config.toml, which works in both.
What changes
- The publish time is each version's
pubtimein 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.crateis 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 --releaseOr run the Setup page's one-line command in a RUN step. Building
images 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 searchfails,cargo infoneeds--registry slowshield, andcargo publish,yankandownerneed--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.
For agents: this page as Markdown · llms.txt · the SlowShield skill