
A lightweight caching proxy for package registries.
A caching proxy for package registries. Speeds up package downloads by caching artifacts locally, reducing bandwidth usage and improving reliability.
Most supply chain attacks rely on speed: a malicious version gets published and consumed by automated pipelines within minutes, before anyone notices. The cooldown feature adds a quarantine period to newly published versions. When enabled, the proxy strips versions from metadata responses until they've aged past a configurable threshold.
cooldown:
default: "3d" # hide versions published less than 3 days ago
ecosystems:
npm: "7d" # npm gets a longer window
cargo: "0" # disable for cargo
packages:
"pkg:npm/lodash": "0" # exempt trusted packages
A 3-day cooldown means that when lodash publishes version 4.18.0, your builds keep using 4.17.21 until 3 days have passed. If the new release turns out to be compromised, you were never exposed.
Resolution order: package override, then ecosystem override, then global default. This lets you set a conservative default and carve out exceptions for packages where you need faster updates. See docs/configuration.md for the full config reference.
Cooldown only looks at a version's publish timestamp — it never inspects the actual bytes. Artifact scanning closes that gap: when enabled, every artifact is staged into storage and scanned by one or more external services (trivy, ClamAV, Wiz, or anything else that speaks a small HTTP/JSON contract) before it's committed to the cache and served to clients.
scanning:
enabled: true
signing_key: ${PROXY_SCANNING_SIGNING_KEY}
scanners:
- name: clamav
url: http://clamav-adapter:8080/scan
mode: block # a block verdict deletes the artifact and returns 403
- name: trivy
url: http://trivy-adapter:8081/scan
mode: monitor # findings are logged, never gate caching
ecosystems: [npm, pypi]
The proxy never uploads artifact bytes to a scanner. Each scanner is notified with package metadata plus a short-lived signed URL; the scanner pulls the bytes itself from the proxy's own storage. Scanners run concurrently, and the first block-mode scanner to report a verdict of not-allowed wins immediately, canceling the rest. See docs/configuration.md for the full config reference and the scanner HTTP contract.
| Registry | Language/Platform | Cooldown | Completed |
|---|---|---|---|
| npm | JavaScript | Yes | ✓ |
| Cargo | Rust | Yes | ✓ |
| RubyGems | Ruby | Yes | ✓ |
| Go proxy | Go | ✓ | |
| Hex | Elixir | Yes* | ✓ |
| pub.dev | Dart | Yes | ✓ |
| PyPI | Python | Yes | ✓ |
| Maven | Java | ✓ | |
| Gradle Build Cache | Java/Kotlin | ✓ | |
| NuGet | .NET | Yes | ✓ |
| Composer | PHP | Yes | ✓ |
| Conan | C/C++ | ✓ | |
| Conda | Python/R | Yes | ✓ |
| CRAN | R | ✓ | |
| Julia | Julia | ✓ | |
| Swift | Swift | ✓ | |
| Container | Docker/OCI | ✓ | |
| Homebrew | macOS/Linux | ✓ | |
| Debian | Debian/Ubuntu | ✓ | |
| RPM | RHEL/Fedora | ✓ | |
| Alpine | Alpine Linux | ✓ | |
| Arch | Arch Linux | ✗ | |
| Chef | Chef | ✗ | |
| Generic | Any | ✓ | |
| Helm | Kubernetes | Yes | ✓ |
| Vagrant | Vagrant | ✗ |
Cooldown requires publish timestamps in metadata. Registries without a "Yes" in the cooldown column either don't expose timestamps or haven't been wired up yet.
* Hex cooldown requires disabling registry signature verification (HEX_NO_VERIFY_REPO_ORIGIN=1) since the proxy re-encodes the protobuf payload.
brew install git-pkgs/git-pkgs/proxy
Or download a binary from the releases page.
Install the chart from GHCR, setting the public URL that package-manager clients will use to reach the proxy:
helm install proxy oci://ghcr.io/git-pkgs/charts/proxy \
--set config.data.base_url=https://proxy.example.com
The default chart deploys one replica backed by a 10 GiB persistent volume,
using SQLite and filesystem artifact storage under /data. See
deploy/charts/proxy/values.yaml for ingress,
external database and object-storage configuration options.
# Build from source
go build -o proxy ./cmd/proxy
# Run with defaults (listens on :8080)
./proxy
# Run with custom settings
./proxy -listen :3000 -base-url https://proxy.example.com
The proxy is now running. Configure your package managers to use it.
This repo uses swaggo to generate an OpenAPI spec from annotated handlers.
Generate the spec:
go install github.com/swaggo/swag/cmd/swag@latest
go generate ./internal/server
Generated files are written to docs/swagger/.
When the proxy is running, fetch the live spec from:
http://localhost:8080/openapi.jsonOr replace http://localhost:8080 with your configured base URL. This link is also shown on the dashboard.
Create or edit ~/.npmrc:
registry=http://localhost:8080/npm/
Or set per-project in .npmrc:
registry=http://localhost:8080/npm/
Or use environment variable:
npm_config_registry=http://localhost:8080/npm/ npm install
npm audit, pnpm audit, yarn npm audit and npm audit signatures work
through the proxy: the audit and signing-key endpoints are passed through to the
configured upstream registry, with upstream authentication applied. Advisories
therefore come from upstream's database, not from the proxy's own vulnerability
data, and versions withheld by cooldown are not excluded
from the report.
Create or edit ~/.cargo/config.toml:
[source.crates-io]
replace-with = "proxy"
[source.proxy]
registry = "sparse+http://localhost:8080/cargo/"
Or set per-project in .cargo/config.toml in your project root.
Set the gem source in your Gemfile:
source "http://localhost:8080/gem"
Or configure globally:
gem sources --add http://localhost:8080/gem/
bundle config mirror.https://rubygems.org http://localhost:8080/gem
Set the GOPROXY environment variable:
export GOPROXY=http://localhost:8080/go,direct
Or in your shell profile for persistence.
Point Homebrew's JSON API and artifact domain at the proxy:
export HOMEBREW_API_DOMAIN=http://localhost:8080/homebrew
export HOMEBREW_ARTIFACT_DOMAIN=http://localhost:8080
The artifact domain proxies manifests and bottle blobs under /v2/homebrew/core/. GHCR routing is limited to that repository. Source archives, cask application downloads, custom tap artifacts, and legacy flat-file bottle mirrors use Homebrew's normal fallback URLs. Keep fallback enabled by leaving HOMEBREW_ARTIFACT_DOMAIN_NO_FALLBACK unset.
Enable cache_metadata or set PROXY_CACHE_METADATA=true to retain Homebrew JSON API responses for offline fallback. Bottle blobs and their OCI manifests are cached without this setting.
The upstreams default to https://formulae.brew.sh/api for the JSON API and https://ghcr.io for artifacts. To chain this proxy to another proxy, configure its Homebrew endpoints as the upstreams:
upstream:
homebrew_api: "https://upstream-proxy.example.com/homebrew"
homebrew_artifact: "https://upstream-proxy.example.com"
The equivalent environment variables are PROXY_UPSTREAM_HOMEBREW_API and PROXY_UPSTREAM_HOMEBREW_ARTIFACT.
Configure in ~/.hex/hex.config:
{default_url, <<"http://localhost:8080/hex">>}.
Or set the environment variable: