Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-10 08:09:17 -05:00
Commit graph pkg-proxy/docs/configuration.md
Author SHA1 Message Date
montehurd
436155e50c Clear before deleting on eviction, and requeue failed reclaims
Eviction deleted an object before checking its record still pointed at
it. If a refetch had moved the record, that object was queued and a
request that read the record earlier could still be opening it. Eviction
now clears first, skips the delete if the record moved, and queues the
object when the delete fails after a clear.

Reclaim queues a failed delete again, which moves it behind the rest, so
objects the backend keeps refusing cannot fill every batch.

Also drop the clearArtifactCache passthrough and ClearCachedArtifact's
unused context, note that only tests call ArtifactPath, and document that
max_size does not count objects waiting to be reclaimed.
2026-09-29 10:24:22 -07:00
abhinavgautam01
c753a74666
docs: explain APT denylist limitations and version pinning 2026-09-22 12:47:37 +05:30
Andrew Nesbitt
e28745742c
Merge pull request #353 from andrewmrich/arich/debian-repositories
enable multiple debian repositories
2026-09-21 12:06:55 +01:00
Andrew Nesbitt
1da2dc0bf9
Merge pull request #359 from abhinavgautam01/fix/helm-oci-index-320
fix(helm): support OCI references in HTTP repository indexes
2026-09-21 11:59:04 +01:00
abhinavgautam01
548a50d56e
feat: add exact-version package denylist 2026-09-20 14:42:30 +05:30
abhinavgautam01
429e7458e4
fix(helm): support OCI references in HTTP repository indexes 2026-09-19 14:39:29 +05:30
Andrew Rich
8805cd9283
Merge branch 'git-pkgs:main' into arich/debian-repositories 2026-09-16 10:04:56 -07:00
pinguinfuss
be2a1f05d2
fix(server): tune the shared upstream transport defaults (#351)
* fix(server): tune the shared upstream transport defaults

server.serve builds the shared client with safehttp.New, which clones
Go's default transport: MaxIdleConnsPerHost stays 0 (an effective limit
of two idle connections per host) and ResponseHeaderTimeout stays 0.
Handing that client to fetch.NewFetcher via fetch.WithHTTPClient
replaces the fetcher's own defaults of 10 idle connections per host and
a 60-second response-header timeout.

Set both on the shared transport, matching the fetcher defaults: a
second burst of concurrent cache misses to one registry now reuses its
connections instead of re-dialling most of them, and an upstream that
accepts a request but stalls before sending headers is cut off after 60
seconds rather than only by the client's overall timeout.

Tests measure connection reuse across two concurrent bursts against a
TLS upstream that counts accepted connections (Go default: at most two
reused; tuned: all eight) and assert that a stall before headers fails
with the response-header timeout.

Fixes #327

* fix(server): reconcile http_timeout docs and tighten the transport tests

The http_timeout documentation and the config comment said "0" disables
the upstream timeout entirely. With a fixed 60-second
ResponseHeaderTimeout on the shared transport that is no longer the
whole story, so both now say that waiting for response headers stays
bounded independently of the setting.

Test cleanup from review: the 50ms settle between bursts was dead time
(the transport returns a connection to the idle pool before the body's
final Read returns, so burst returning already means the pool is
settled); the maxNewInBurst sentinel became explicit min/max bounds per
case; the stall test dropped the client.Timeout override and the
elapsed-time assertion, which was redundant with the error-text check
in any realistic run and whose failure message misattributed the cause,
and its comment now says plainly that the field assertions pin
production while the behavioural half runs at a lowered timeout.

* test(server): hold each burst at the upstream instead of sleeping

The reuse test kept a burst in flight with a 100ms handler sleep, so a
process stall longer than that between spawning the goroutines and
their dials let a request finish early and hand its connection to a
sibling. Review reproduced this with forced stalls: the default-transport
case then dialled 5 instead of 6 connections.

The handler now answers only once burstSize requests are waiting at the
same time. With HTTP/1.1 pinned that forces every burst onto burstSize
distinct connections regardless of scheduling, and the measured counts
stay exactly 6 new for Go's default and 0 for the tuned transport, also
under the same forced stalls.
2026-09-16 08:11:06 +01:00
Andrew Rich
8a51d04011 feat(debian): serve additional APT archives at /debian/{name}/
A Debian release is served by more than one archive. Security updates live on
a different host than the main archive, so a single upstream.debian URL cannot
serve a complete suite set and -security suites are unreachable.

Add upstream.debian_repositories, a name-to-URL map served at /debian/{name}/,
modelled on upstream.apk. The field is additive: upstream.debian keeps serving
/debian/pool/... and /debian/dists/... with unchanged cache identities, so
existing deployments and their warm caches are unaffected, and the scalar
field and PROXY_UPSTREAM_DEBIAN are untouched.

Named repositories scope both caches by name, since the same filename can hold
different bytes in different archives. Metadata keys are hashed over name,
upstream URL, and path, as APKHandler does. Names are validated through
validateNamedUpstreams, and "pool" and "dists" are refused because they would
shadow the main archive's own paths.

An unconfigured first path segment stays a main-archive path rather than
returning 404 as the APK handler does: the main archive is unnamed and serves
paths of its own at the root.
2026-09-15 15:35:04 -07:00
Andrew Nesbitt
b67cfb1014
Add Homebrew JSON API and bottle proxy support (#254)
* Add Homebrew JSON API and bottle proxy support

* Fix Homebrew HEAD offline fallback and non-sha256 OCI manifest handling

Route Homebrew API HEAD requests through ProxyCached so a warm cache
answers without an upstream call and stale entries are served when the
upstream is unreachable. HEAD still reaches upstream as HEAD when
metadata caching is disabled.

Limit OCI manifest digest verification to sha256 references and
Docker-Content-Digest headers so other digest algorithms are proxied
instead of rejected, and log the failing expected value.

* Reconcile with #280 and #301 after rebase

Compute real manifest digests in #280's fixture upstreams so the new
verification accepts them, and add headerETag / headerLastModified to

* Send fixed Accept for Homebrew API and match If-None-Match properly

The Homebrew API cache key does not include Accept, so replaying the
client header could serve one representation under another; the API
does not negotiate anyway. Compare If-None-Match with weak comparison,
list splitting and "*" per RFC 7232 instead of string equality, and
apply the same helper to the metadata and swift responders.

* Reconcile with #298 and #304 after rebase

Move the configureScanning doc comment back to its function after the
auto-merge stacked it on mountProtocolHandlers, and drop the second
ETag/Last-Modified set in writeMetadataCachedResponse now that the
pre-304 set covers both response paths.
2026-09-03 16:59:12 +01:00
Giles Westwood
30e54a1e0c
Add generic HTTP download proxy for GitHub release assets (mise/aqua) (#302)
* Add generic HTTP download proxy for GitHub release assets

Adds a /generic/{name}/ route backed by a new upstream.generic named-upstream
map, so tools that download from fixed URL shapes (mise's aqua backend
fetching GitHub release assets, and its tag lookups on api.github.com) can be
pointed at the proxy with client-side URL rewriting. Only configured
upstreams are reachable, so this is not an open HTTP proxy.

Paths shaped like {owner}/{repo}/releases/download/{tag}/{asset} are
version-pinned and go through the artifact cache: fetched once, hashed,
served without revalidation, and still served when the upstream is down.
Every other path goes through the metadata cache with the client's Accept
header and query string replayed, so API responses are fresh within
metadata_ttl, revalidated after that, and served stale when the upstream
fails or rate-limits the request.

Tests cover path classification, unknown upstreams and traversal, cache
hits with the upstream down, HEAD, 404 pass-through, Accept and query
forwarding, stale-on-429, cache isolation between upstreams, and that an
upstream token scoped to the release host is not sent to the object store
it redirects to.

Closes #183.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RDjDeq27CKzEP2o3GWBY7F

* Use fixed Accept header for generic metadata

---------

Co-authored-by: Giles Westwood <giles@gileswestwood.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 16:44:54 +01:00
aarnaud
5499837084
Add pre-cache artifact scanning hook (trivy/ClamAV/Wiz/custom) (#298)
Runs fetched artifacts through pluggable external scanners after they're
staged in storage but before they're committed to the cache DB, so a
block verdict deletes the object instead of ever exposing it to a
client. Scanners pull the staged bytes themselves via a short-lived
HMAC-signed internal route rather than the proxy pushing bytes to them,
keeping the mechanism storage-backend-agnostic and avoiding uploading
potentially huge artifacts through the proxy's own egress.

Hardening baked in from the start: the internal scan-fetch route is
gated both at router-mount time and in the handler so it's inert
whenever scanning is disabled or unsigned; the signing key is mandatory
whenever scanning is enabled, enforced directly in scanner.NewGroup
rather than relying on callers to invoke config validation; the scan
call and the delete-on-block cleanup both run on a context detached
from the client's, so a client disconnecting mid-scan can't be mistaken
for a scanner failure, doesn't cause a legitimate artifact to be
deleted, and doesn't leave a genuinely blocked artifact's bytes
orphaned in storage; and scanner infrastructure errors (connection
failures, internal hostnames) are never forwarded verbatim to anonymous
clients, only a generic message. The scan-error metric also correctly
distinguishes a scanner's own timeout from being cancelled because a
sibling scanner already decided the verdict.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 10:06:44 +01:00
Andrew Nesbitt
7743417c72
Add Swift package registry support (#259)
* Add Swift package registry support

* Fix Swift archive integrity handling

* Fix Swift registry pagination and archive HEAD requests

* Canonicalize Swift package identifiers

* Handle Swift registry cache identities

* Use stored PURLs for cache lookups

* Address review: upstream-hash refetch and cache PURL cleanup

- Re-fetch instead of 502 when a cached artifact's hash disagrees with
  the upstream-declared checksum; log and discard the stale entry.
- Rename expectedHash to upstreamHash and document how the check
  differs from checkCache's stream integrity verification.
- Drop the repository_url qualifier from swift cache PURLs so cache
  entries survive an upstream.swift change, matching other ecosystems.
- Pass name to handleSourceArchiveHead instead of re-deriving it.

* Drop BulkCheckVulnerabilities coverage after #279 removed it

The rebase over #279 (dead-code cleanup) drops BulkCheckVulnerabilities;
remove the tests and helper that exercised the swift-identity-filtering
path through it, and the imports they pulled in.
2026-09-02 13:06:34 +01:00
Andrew Nesbitt
b5ee6dd96c
Add ECR auto-refreshing upstream authentication (#278)
* Add ECR auto-refreshing upstream authentication

- Add "ecr" auth type to upstream.auth config with optional region
- Cache ecr:GetAuthorizationToken results per region and refresh
  shortly before expiry via the AWS SDK default credential chain
- Route type: ecr through the token cache in Server.authForURL
- Document the new type in config.example.yaml and docs/configuration.md

Fixes #276

* Collapse ecrTokens.header to a single return path

Drops the internal/server package below the goconst min-occurrences
threshold for the Authorization literal.

* Coalesce concurrent ECR token fetches with singleflight

Concurrent cache misses for the same region now share a single
GetAuthorizationToken call instead of each issuing their own, avoiding
a request burst against the ECR API at cold start and at each 12-hour
refresh. golang.org/x/sync is already a direct dependency.

* Improve ECR token refresh and region inference

* Back off failed ECR token refreshes
2026-09-02 12:43:43 +01:00
Abhinav Gautam
1e3369c959
fix(oci): cache tag lists and normalize manifest variants (#280)
* fix(oci): cache tag lists and normalize manifest variants

* fix(oci): refine manifest cache variants

* fix(oci): preserve manifest cache compatibility

* fix(oci): dual-write manifest cache variants

* fix(oci): preserve cached pagination links

* fix(oci): rewrite named registry pagination links
2026-09-02 12:40:35 +01:00
pinguinfuss
4b9b401d1f
Add Alpine APK repository proxy support (#293)
* Add Alpine APK repository proxy support

- Serve named APK repositories at /apk/{repository}/ with the official
  Alpine mirror as the default repository
- Cache v2 APKINDEX.tar.gz and v3 Packages.adb indexes and detached
  signatures via the metadata cache, serving stored bytes unchanged so
  apk signature verification keeps working
- Cache .apk packages in the shared artifact cache keyed by the full
  repository path, since APK filenames do not include the architecture
- Add configurable upstream repositories via upstream.apk with
  validation, plus dashboard registry instructions
- Add tests for index/signature byte fidelity, per-arch caching, cache
  hits, offline reads, upstream authentication, and 404 handling
- Document apk usage in README, config example, and configuration docs

* Serve APK package HEAD requests without a body

Use the method-aware serveArtifact helper (as container.go does) so HEAD
responses carry Content-Length but omit the body; add a regression test.

* Drop doubled blank line in docs/configuration.md

---------

Co-authored-by: Andrew Nesbitt <andrewnez@gmail.com>
2026-09-02 12:36:00 +01:00
Abhinav Gautam
7f8ccab96f
Accept inline SBOM documents in mirror API (#294)
* feat(mirror): accept inline SBOM API requests

* fix(mirror): address inline SBOM review feedback
2026-09-02 12:26:29 +01:00
Victor Chacon Codesseira
7e1cb68c7c
Add upstream.npm_full_metadata to serve publish times without cooldown (#297)
- Request application/json from the npm upstream when the option is set,
  independent of cooldown, so served packuments carry the "time" map
- Wire the option through the shared Proxy struct and the
  PROXY_UPSTREAM_NPM_FULL_METADATA environment override
- Document it in config.example.yaml and docs/configuration.md
- Test that the option forces full metadata with cooldown disabled
2026-09-02 12:13:12 +01:00
Andrew Nesbitt
f43aa9d13e
Make built-in upstream URLs configurable (#255)
Every built-in ecosystem upstream can now be set via the upstream
config block or PROXY_UPSTREAM_* env vars, with allow_private_hosts
and allow_loopback controlling access to non-public addresses.
2026-08-29 11:19:54 +01:00
Abhinav Gautam
088027cac3
feat: add Helm repository proxy support (#268)
* feat: add Helm repository proxy support

* fix(helm): address review feedback

* fix(helm): serve cached charts without index
2026-08-16 18:12:55 +01:00
Andrew Nesbitt
e4fbf3f277
Add JSONL access logging (#269)
* Add JSONL access logging

* Initialize access log before server dependencies
2026-08-16 18:07:39 +01:00
Andrew Nesbitt
538a15d9f8
fix(container): serve cached images when upstream is unavailable (#199)
* container: cache manifests for offline pulls

* Preserve direct-serve redirects for blob HEAD requests
2026-08-13 07:35:07 +01:00
Andrew Nesbitt
bbea63f046
fix(upstream): apply authentication through shared transport (#198)
* upstream: apply authentication through shared transport

* Address upstream authentication review findings
2026-08-13 07:08:23 +01:00
Andrew Nesbitt
cdbd1a9369 Canonicalize cooldown lookup PURLs to match config
NormalizedPackages runs config keys through purl.Parse().String(), which
applies per-type rules like lowercasing pypi names. The handler side was
building lookup keys with MakePURLString, which does not, so a config
entry for pkg:pypi/Django would be normalized to pkg:pypi/django and
never match the runtime key pkg:pypi/Django.

Route both sides through the same canonical form: a new
canonicalPackagePURL helper calls Normalize() on the constructed PURL
before stringifying, and all cooldown IsAllowed call sites use it.
2026-07-17 18:11:47 -07:00
Andrew Nesbitt
be15a0f826 Fix cooldown overrides for scoped package PURLs 2026-07-13 17:11:04 -07:00
Andrew Nesbitt
0ae63066e2
Make upstream HTTP timeout configurable
Add http_timeout config option (and PROXY_HTTP_TIMEOUT env var) to set
the per-request timeout on the shared HTTP client used by protocol
handlers for upstream metadata fetches and pass-through file requests.
Defaults to the previous hardcoded 30s; "0" disables the timeout.

Fixes #187
2026-07-07 17:48:36 +01:00
Andrew Nesbitt
da4fa2fc98
Add PROXY_UI_URL for separate UI/package URL advertisement (#161)
Adds UIBaseURL (env PROXY_UI_URL), advertised separately from BaseURL
for deployments where the UI is reached on a public domain while build
machines hit a Docker network alias for the package endpoints. Defaults
to BaseURL when unset.

Consumers:
- Logged alongside base_url at startup.
- <link rel="canonical"> and og:url / og:title / og:site_name in every
  UI page, omitted when UIBaseURL is empty.
- Banner on the install guide when UIBaseURL differs from BaseURL,
  clarifying that the URLs in the snippets are the package endpoint and
  that the UI itself lives elsewhere.

Docs call out that the proxy serves UI and package endpoints on the same
listener, so reverse proxies fronting the UI publicly must restrict the
public route to PathPrefix(/ui) to avoid exposing /npm, /pypi, etc.
nginx and Traefik examples both show the path-split pattern.
2026-06-07 16:12:43 +01:00
Andrew Nesbitt
0e7af4aed6
Make metadata size limit configurable (closes #149) (#150) 2026-06-02 07:59:00 +01:00
Mati Kepa
552c8ac4e5
Update Gradle cache with the configurable plugin support (#114)
* feat(gradle): add request metrics for build cache handler

* Implement fallback handling for Gradle plugin requests and update tests

* refactor(gradle): simplify response handling and remove unused metrics assertions

* Add tests for Gradle plugin metadata fallback and refactor metadata handling

---------

Co-authored-by: Mateusz (Mati) Kepa <m.kepa@sportradar.com>
2026-05-22 17:05:20 +01:00
Mati Kepa
31a9ca75b2
add Gradle Build Cache support with handler and tests (#87)
* add Gradle Build Cache support with handler and tests

* linting issue

* MR Suggestions: Add Gradle HTTP Build Cache configuration to README

* implement  minor stuff: Refactor Gradle handler to remove unnecessary URL parameter and update related tests

Co-authored-by: Copilot <copilot@github.com>

* Add Gradle build cache configuration and eviction support

- Introduced configuration options for Gradle build cache in config files and documentation.
- Implemented read-only mode and upload size limits for the Gradle build cache.
- Added cache eviction logic based on age and size, with corresponding tests.
- Enhanced storage interfaces to support listing objects by prefix.

* implement minor stuff: Refactor Gradle handler to remove unnecessary URL parameter and update related tests

* last finding fix

* fix tests and implement PR suggestions

Co-authored-by: Copilot <copilot@github.com>

* unify path

---------

Co-authored-by: Mateusz (Mati) Kepa <m.kepa@sportradar.com>
Co-authored-by: Copilot <copilot@github.com>
2026-05-04 11:15:16 +01:00
Andrew Nesbitt
7346008aa5
Add metadata TTL and stale-while-revalidate support
Cached metadata is now served directly within a configurable TTL window
(default 5m) without contacting upstream, reducing latency and upstream
load. When upstream is unreachable and the cache is past its TTL, stale
content is served with a Warning: 110 header per RFC 7234.

New config: `metadata_ttl` (YAML) / `PROXY_METADATA_TTL` (env).
Set to "0" to always revalidate with upstream.
2026-04-13 09:01:05 +01:00
Andrew Nesbitt
c01f0a5c05
Fix metadata caching, 404 propagation, mirror progress, and registry stubs
- ProxyCached now stores upstream Last-Modified in the cache and uses it
  (along with ETag) for conditional request handling, returning 304 when
  client validators match. Adds Content-Length to cached responses.

- Handlers calling FetchOrCacheMetadata (pypi, composer, pub, nuget) now
  check for ErrUpstreamNotFound and return 404 instead of 502, matching
  the existing npm and cargo behavior.

- Mirror jobs report live progress via a periodic callback while running,
  so API polls return real counts instead of zeroed progress.

- Registry mirroring removed from CLI flags, API acceptance, README, and
  docs since every enumerator was a stub returning "not yet implemented".

- Added tests for the conditional metadata path (ETag/If-None-Match,
  Last-Modified/If-Modified-Since, 304 responses, header omission).
2026-04-13 09:01:05 +01:00
Andrew Nesbitt
47681066b5
Fix review issues in mirror feature
- Fix race where runJob could overwrite canceled state set by Cancel()
- Fix Debian ecosystem name inconsistency ("deb" -> "debian")
- Stream metadata responses when caching is disabled to avoid buffering
- Add metadata_cache table to initial schema strings for consistency
- Gate mirror API behind mirror_api config flag (disabled by default)
- Fix goconst lint in metadata_cache_test.go
2026-04-13 09:01:04 +01:00
Andrew Nesbitt
d62c42b8d7
Add mirror command and API for selective package mirroring
Add a `proxy mirror` CLI command and `/api/mirror` API endpoints that
pre-populate the cache from various input sources: individual PURLs,
SBOM files (CycloneDX and SPDX), or full registry enumeration.

The mirror reuses the existing handler.Proxy.GetOrFetchArtifact()
pipeline so cached artifacts are identical to those fetched on demand.
A bounded worker pool controls download parallelism.

Metadata caching is opt-in via `cache_metadata: true` in config (or
PROXY_CACHE_METADATA=true). The mirror command always enables it. When
enabled, upstream metadata responses are stored for offline fallback
with ETag-based conditional revalidation.

New internal/mirror package with Source interface, PURLSource,
SBOMSource, RegistrySource, and async JobStore. New metadata_cache
database table for offline metadata serving.
2026-04-13 09:01:04 +01:00
Andrew Nesbitt
43a164ed72
Add cooldown support for Hex
Decode the Hex registry protobuf format, filter releases by fetching
timestamps from the Hex HTTP API (hex.pm/api/packages/{name}), and
re-encode without the original signature.

The protobuf handling uses protowire for low-level encoding/decoding
of the Signed wrapper, Package, and Release messages. Timestamps come
from the inserted_at field in the JSON API response.

Since the proxy re-encodes the payload without the original signature,
users need to disable registry signature verification.
2026-04-06 13:18:57 +01:00
Andrew Nesbitt
cb9bbbc385
Add cooldown support for RubyGems
Filter versions from the compact index (/info/{name}) by fetching
timestamps from the versions API (/api/v1/versions/{name}.json).
Both requests run concurrently to minimize latency. If the versions
API is unavailable, the compact index is proxied unfiltered.

Handles platform-specific versions (e.g. 1.0.0-java) by matching
the compact index format.
2026-04-06 13:16:26 +01:00
Andrew Nesbitt
75ff85f2f0
Add cooldown support for Conda (#68)
* Add cooldown support for Conda

Filter entries from Conda repodata.json based on the timestamp field
(milliseconds since epoch). Filters both packages and packages.conda
sections. When cooldown is disabled, repodata requests are proxied
directly without parsing.

* Update README table to mark Conda cooldown support
2026-04-06 13:16:00 +01:00
Andrew Nesbitt
70fe686953
Add cooldown support for NuGet (#67)
* Add cooldown support for NuGet

Filter versions from NuGet registration pages based on the
catalogEntry.published timestamp. Handles both RFC3339 and NuGet's
fractional-second timestamp formats. When cooldown is disabled,
registration requests are proxied directly without parsing.

* Update README table to mark NuGet cooldown support
2026-04-06 13:12:18 +01:00
Andrew Nesbitt
4f8f63f354
Add version cooldown to filter recently published packages
Hides package versions published too recently from metadata responses,
giving the community time to spot malicious releases. Configurable
per-ecosystem and per-package with duration overrides. Supported for
npm, PyPI, pub.dev, and Composer.
2026-03-04 19:00:31 +00:00
Andrew Nesbitt
fcc5289f97
Add auth pass-through for upstream registries
Configure authentication per URL prefix in config:

  upstream:
    auth:
      "https://registry.npmjs.org":
        type: bearer
        token: "${NPM_TOKEN}"

Supports bearer tokens, basic auth, and custom headers.
Credentials can reference environment variables with ${VAR_NAME} syntax.
The longest matching URL prefix wins when multiple patterns match.
2026-01-29 16:33:09 +00:00