Package types

The Virtual Registry classifies every request twice before it does anything else: by package type, which names the ecosystem the request belongs to, and by resource type, which says whether the request is for a manifest, a package, or something else.

The pair decides how long the response is cached, whether the request is sent through the Artifact Firewall, which URLs are rewritten in the response body, and which cache tags the object is stored under.

Classification needs no configuration. A registry does not declare which ecosystem it serves, so one registry can front an upstream that serves several.

How the package type is decided

Three signals are consulted in order, and the first that matches wins.

  1. The Accept header, for the vendor media types that name their own format: application/vnd.docker.* and application/vnd.oci.* for OCI, application/vnd.npm.* for npm, application/vnd.pypi.simple.v1+* for PyPI.
  2. The User-Agent, which is what identifies most clients. The table below lists what is recognised.
  3. The URL, for requests that arrive with neither, such as a curl of a package URL or a client whose user agent is unknown.

A request that matches none of the three is typed other and cached on the general rules, with no ecosystem-specific handling.

Requests reaching a repository manager rather than a registry carry a path prefix that the package path does not. Sonatype Nexus (/repository/<repo>) and JFrog Artifactory (/artifactory/<repo> and /<context>/api/<type>/<repo>) prefixes are recognised and stripped before the package coordinates are read, so the same rules and cache keys apply either way.

Recognised clients

Package type Clients recognised by user agent
oci docker, containerd, cri-o, containers
npm npm, yarn, pnpm, bun
pypi pip, twine, poetry, python-requests
maven Apache-Maven, Gradle
nuget NuGet, dotnet, VisualStudio
hex Mix, hex_core
cargo cargo
conda conda, mamba, micromamba
git, git-lfs git, git-lfs
helm Helm
php Composer
deb Debian APT, apt-get, aptitude
rpm libdnf, yum, rpm
rubygems RubyGems, bundler
conan conan
ansible ansible, ansible-galaxy, ansible-runner, mazer, AWX
apk apk
bower bower
chef Chef Client
cocoa CocoaPods
cran R
dart Dart pub
ivy Apache-Ivy
opkg opkg
puppet Puppet
sbt sbt
swift swift-package-manager
terraform Terraform
vagrant Vagrant

The go and github types have no user agent of their own and are recognised from the URL, as are the ecosystems served under a repository manager path such as /api/npm/.

Ecosystem-specific handling

Some package types are parsed further, which is what lets the Virtual Registry tell a manifest from a package, extract the coordinates a firewall rule matches on, and rewrite the URLs a manifest hands back to the client:

oci, git, git-lfs, go, npm, pypi, maven, nuget, hex, helm, conda, cargo, conan, ansible, php, deb and github.

The rest are classified, counted and cached, but not parsed. Their requests are typed as other resources and cached on the general rules.

Go

Go modules are served under /@v/ and /@latest, and the Virtual Registry also serves the Go checksum database at /sumdb/<name>/, forwarding to the database itself. A client that can reach nothing but the Virtual Registry can therefore still verify checksums, with GOPROXY as the only setting it needs. Only the two databases the go command ships with are proxied, sum.golang.org and sum.golang.google.cn. Any other name gets a 404.

OCI

Manifests are cached per tag and per digest, blobs by digest. A digest-addressed object is immutable and shared, and access to it is authorized per repository, so a client with access to one image is not served a layer belonging to an image it cannot reach.

PyPI

The simple index is cached and filtered in both its HTML and its PEP 691 JSON form. The PEP 658 metadata sidecars (<file>.metadata) are cached too, which matters because a resolver fetches one per candidate wheel, more often than it downloads anything.

Resource types

Every request also lands in one of three resource types, each with its own default lifetime and its own TTL setting to override it:

  • manifest: a mutable reference, such as an npm packument, a PyPI simple index, a maven-metadata.xml, or a tag-addressed OCI manifest. Revalidated on every request by default, with requests for the same manifest coalesced into one fetch, and kept for a week so a stale copy can be served while the origin is unreachable. manifest_ttl replaces the revalidation with a fixed window.
  • package: the artifact itself, which is immutable. Cached for a hundred years, capped by package_ttl.
  • other: everything else, including every recognised-but-unparsed ecosystem and unclassified traffic. No lifetime of its own unless other_ttl sets one, so the origin’s cache headers decide, falling back to default_ttl.

Some requests deviate from their resource type’s default because the protocol makes them immutable whatever their type: a digest-pinned OCI manifest, a Go .info or .mod, a PyPI metadata sidecar. Those are cached for a hundred years and are unaffected by manifest_ttl.


®Varnish Software, Wallingatan 12, 111 60 Stockholm, Organization nr. 556805-6203