Skip to content

About

Safety through patience.

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

Repository files navigation

SlowShield: safety through patience

SlowShield is a supply-chain defence proxy for PyPI, npm, Go modules, Maven (Maven, Gradle, sbt), Cargo (crates.io) and container images (Docker Hub, GHCR, Quay, registry.k8s.io and others). It sits between your developers, CI and production builds and the public registries, and:

  • holds new releases back for a configurable number of days (default 7), so the community and the threat feeds have time to notice a compromised version before you install it;
  • blocks known-malicious packages from the OpenSSF/OSV and GitHub Advisory malware feeds (HTTP 451, even when failing open);
  • detects tampering: every artifact is verified against the registry's published digests and its first-seen fingerprint; if the bytes ever change, the download is aborted mid-stream;
  • shows you what you depend on: a fast, read-only web UI with installs, trends, new dependencies, held-back versions and a security timeline — plus OpenTelemetry metrics, logs and traces with ready-made Grafana dashboards and alerts.
pip / uv / poetry / npm / pnpm / yarn / bun / go / maven / gradle / cargo / containerd / docker / podman
                 │  HTTPS (TLS 1.3, HTTP/2, HTTP/3)
                 ▼
          Caddy (TLS, H3)  ──────────────── certificates: ACME, your files, or internal CA
                 │
          SlowShield (Python 3.15, Granian)
          ├─ blocklist      OSV + GitHub malware advisories (sync every hour)
          ├─ release age    per file (PyPI, Maven) / per version (npm, Go, Cargo) / per digest (images)
          ├─ integrity      registry digests + trust-on-first-use fingerprint, verified cache
          └─ telemetry      OTLP → Alloy → Prometheus / Loki / Tempo → Grafana
                 │  HTTP/2
                 ▼
     pypi.org · files.pythonhosted.org · registry.npmjs.org · proxy.golang.org · sum.golang.org
     repo1.maven.org · dl.google.com (Google Maven) · plugins.gradle.org · index.crates.io · static.crates.io
     registry-1.docker.io · ghcr.io · quay.io · registry.k8s.io · gcr.io · mcr.microsoft.com · public.ecr.aws

Quick start

Try it on your laptop: one container, plain HTTP on localhost, nothing kept after you stop it.

docker run --rm -p 127.0.0.1:8080:8080 ghcr.io/squirro/slowshield:latest

The dashboard is at http://localhost:8080. In a second terminal, install something through it (in a throwaway virtualenv, because Homebrew and current Linux Pythons refuse pip installs outside one):

python3 -m venv /tmp/slowshield-try
/tmp/slowshield-try/bin/pip install --index-url http://localhost:8080/pypi/simple/ requests

Releases younger than a week are held back; known malware is refused with HTTP 451. To send pip, uv, npm and go through it in every new terminal (bash on Linux shown; on macOS bash reads ~/.bash_profile and zsh ~/.zshrc; in fish use set -Ux NAME value):

cat >> ~/.bashrc <<'EOF'
export PIP_INDEX_URL=http://localhost:8080/pypi/simple/
export UV_DEFAULT_INDEX=http://localhost:8080/pypi/simple/
export npm_config_registry=http://localhost:8080/npm/
export GOPROXY=http://localhost:8080/go
EOF
source ~/.bashrc

Remove the lines to switch it off.

Run it for your team

git clone https://github.com/squirro/slowshield
cd slowshield/deploy/docker
cp .env.example .env              # your hostname, TLS mode, optional GITHUB_TOKEN
docker compose up -d
# with the full Grafana stack:
docker compose -f compose.yaml -f compose.observability.yaml up -d

Caddy in front handles TLS (ACME, your own certificates, or an internal CA for testing). Podman (rootless Quadlet) and Kubernetes (Helm) setups live in deploy/podman and deploy/helm; every variant has an observability flavour. Images: ghcr.io/squirro/slowshield and ghcr.io/squirro/slowshield-caddy (amd64 and arm64, SBOM and provenance attached); pin a release or a digest in production.

Point your clients at it

pip config set global.index-url https://slowshield.example.com/pypi/simple/
export UV_DEFAULT_INDEX=https://slowshield.example.com/pypi/simple/
npm config set registry https://slowshield.example.com/npm/
go env -w GOPROXY=https://slowshield.example.com/go      # without ",direct", which would go around SlowShield
# Maven: a mirror in ~/.m2/settings.xml; Gradle: an init script in ~/.gradle/init.d/ (both on the Setup page)
# Cargo: source replacement in ~/.cargo/config.toml, index "sparse+https://slowshield.example.com/cargo/"
# containerd, Docker: /etc/containerd/certs.d/_default/hosts.toml (Docker: /etc/docker/certs.d/_default/) with
#   server = "https://slowshield.example.com" and capabilities = ["pull", "resolve"]

Coding agents: the SlowShield Agent Skill teaches them the setup and what SlowShield's answers mean (Claude Code: /plugin marketplace add squirro/slowshield), and https://slowshield.org/llms.txt indexes the guide as Markdown.

The UI's Setup page renders ready-to-copy snippets for pip, uv, Poetry, PDM, Pipenv, npm, pnpm, Yarn, Bun, Go, Maven, Gradle, sbt, Coursier, Cargo, containerd, Docker, Podman and BuildKit with your URLs. Every ecosystem lives under a path on the one host (docs/design/routing.md); per-ecosystem hostnames are deprecated and removed in 0.1.

What clients see:

Situation Index / packument Direct download (lockfile)
version older than the delay listed 200 (verified, cached)
version younger than the delay hidden (Cargo: marked yanked); latest points at the newest allowed 403 + Retry-After (Maven: 425)
no version old enough yet (brand-new package) all non-blocked versions (fail-open, recorded) 200
package or version on the malware blocklist removed (451 if nothing is left) 451 with the advisory
bytes differ from the registry digest or first-seen fingerprint — stream aborted, event recorded, later 451

Container images: a tag resolves to the newest digest it has pointed to for the delay, so nginx:latest keeps working about a week behind. A pinned digest that is too new, or a blocked image, gets 403 with the reason (docs/design/oci.md).

Threat feeds

Feed Token Notes
OSV / OpenSSF malicious packages none full snapshot once, then incremental via modified_id.csv
GitHub Advisory Database (malware) GITHUB_TOKEN (fine-grained PAT, no permissions) without a token the feed is off, the UI shows how to enable it, and slowshield_feed_enabled{reason="missing_token"} lets you alert on it

No feed covers container images; block a repository, tag or digest with [[blocks]] in config.toml, which also works for every other ecosystem.

Configuration

Everything works with defaults plus a few environment variables; config.toml (see config.example.toml) adds exceptions and tuning. Policy changes are applied live. Reference: docs/configuration.md.

Security

  • Containers: Amazon Linux 2023 only (builders included), distroless runtime (no shell, no package manager, RPM database kept for scanners), non-root UID 65532, read-only root filesystem, all capabilities dropped, no-new-privileges, multi-arch (amd64/arm64).
  • TLS by Caddy: TLS 1.3 only by default, hybrid post-quantum key exchange (X25519MLKEM768), HTTP/3, HSTS.
  • UI: strict Content-Security-Policy (no inline script/style), every value escaped, read-only.
  • Supply chain of SlowShield itself: locked & hashed dependencies (uv.lock), digest-pinned base images, SHA-pinned GitHub Actions audited by zizmor --persona=pedantic, Dependabot with a 7-day cooldown, SBOM + provenance attestations on every image.

See docs/security.md and SECURITY.md for the threat model and how to report a vulnerability.

Performance

Async Python 3.15 on Granian (Rust HTTP server) with pyreqwest (Rust reqwest/hyper, HTTP/2 upstream) and msgspec. npm packuments are filtered without decoding version manifests; cached artifacts are sent zero-copy. Every release is load-tested against the previous one on the same runner and blocked on regressions — see perf/README.md.

Development

uv sync                       # creates .venv with Python 3.15
uv run pytest                 # unit + integration tests (fake registries, no network)
uv run ruff check && uv run ruff format --check && uv run ty check
cp .env.example .env && uv run slowshield serve --config config.example.toml

More in CONTRIBUTING.md and docs/development.md.

License

Apache License 2.0 — see LICENSE and NOTICE. If you distribute a modified version, please give it a different name and logo (brand/README.md).

About

Safety through patience.

Resources

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages