
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 | Yes† | ✓ |
| Hex | Elixir | Yes* | ✓ |
| pub.dev | Dart | Yes | ✓ |
| PyPI | Python | Yes | ✓ |
| Maven | Java | Yes‡ | ✓ |
| 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 | ✓ | |
| FreeBSD | FreeBSD (ports and pkgbase) | ✓ | |
| Chef Supermarket | Chef | ✓ | |
| Generic | Any | ✓ | |
| Git | 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.
† Go cooldown uses the time in each version's .info, which is the commit time of the tagged revision, not when the tag was pushed.
‡ Maven cooldown uses the Last-Modified header of each version's .pom. On Maven Central that is the deploy time; a Nexus or Artifactory upstream may report when it cached the file instead. SNAPSHOT versions are not held back.
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.
To trust a private CA, for example when upstreams sit behind a TLS-intercepting
corporate proxy or are internal registries signed by an in-house CA, mount the
certificate with extraVolumes and extraVolumeMounts and add its directory
to SSL_CERT_DIR. The proxy process makes all upstream TLS connections itself,
Git fetches included, so no other configuration is needed. Public CAs stay
trusted alongside the private one:
extraVolumes:
- name: corp-ca
configMap:
name: corp-ca # e.g. key corp-ca.crt, PEM-encoded
extraVolumeMounts:
- name: corp-ca
mountPath: /etc/proxy/ca
readOnly: true
extraEnv:
- name: SSL_CERT_DIR
value: /etc/ssl/certs:/etc/proxy/ca
# 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. For image builds, see Using proxy in Docker builds.
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. Packuments point each version's attestations (provenance) at
the proxy, which fetches them from the package's registry and caches them;
attestations of versions held back by cooldown or blocked by the denylist are
not served.
To serve private packages through the same registry URL, see Private package routes.
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: