Skip to content

Registries ​

BatleHub proxies, caches, and privately hosts 23 registry types — from language package managers to OS package repositories, editor extension marketplaces, and generic file mirrors.

Every registry type can run in one of three modes, set per registry in the config:

  • proxy — a pure read-through cache in front of an upstream. The first request is fetched from upstream and stored; every later request is served from cache.
  • local — a fully private registry. Nothing is fetched from upstream; you publish and serve your own artifacts.
  • hybrid — local artifacts win, and anything not published locally falls through to the upstream proxy.

Five types are proxy-only (no private publish model): GitHub, Forgejo, GitLab (they host source/releases upstream) and JetBrains IDE archives + Generic file mirrors (path-only caches).

The registries ​

Source hosting ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
GitHubgithubReleases, assets, tarballs, raw filesproxy-only❌api.github.com
Forgejo / GiteaforgejoReleases, assets, archives, raw (/api/v1)proxy-only❌codeberg.org
GitLabgitlabReleases, link assets, archives (/api/v4)proxy-only❌gitlab.com

Language package managers ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
npmnpmPackument + tarballsproxy · local · hybrid✅registry.npmjs.org
CargocargoSparse index + .crateproxy · local · hybrid✅crates.io
Go ModulesgoproxyGOPROXY (.info/.mod/.zip)proxy · local · hybrid✅proxy.golang.org
MavenmavenMetadata XML + JAR/POMproxy · local · hybrid✅repo1.maven.org
PyPIpypiSimple API (PEP 503/691) + wheelsproxy · local · hybrid✅pypi.org
Condacondarepodata.json + .conda/.tar.bz2proxy · local · hybrid✅conda.anaconda.org
Composer (PHP)composerPackagist v2 (p2 metadata + dist)proxy · local · hybrid✅repo.packagist.org
RubyGemsrubygemsGems + versions + info APIproxy · local · hybrid✅rubygems.org
NuGet (.NET)nugetv3 index + flat + .nupkgproxy · local · hybrid✅api.nuget.org
TerraformterraformProviders + modules (v1 API)proxy · local · hybrid✅registry.terraform.io
Ansible GalaxygalaxyCollections API v3 (versions list, version document, tarball) + the v1 role readsproxy · local · hybrid✅galaxy.ansible.com/api/

Editor extensions ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
OpenVSXopenvsxExtension VSIXproxy · local · hybrid✅open-vsx.org
VS Code Marketplacevscode-marketplaceExtension VSIX (MS Gallery)proxy · local · hybrid✅marketplace.visualstudio.com
JetBrains Marketplacejetbrains-marketplacePlugin API + downloadsproxy · local · hybrid✅plugins.jetbrains.com

OS / system packages path-addressed ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
Debian / APTdebPackages/Release + .debproxy · local · hybrid✅none — set upstreams
RPM / YUM / DNFrpmrepodata/ + .rpmproxy · local · hybrid✅none — set upstreams
Pacman / Archpacman<repo>.db + .pkg.tar.zstproxy · local · hybrid✅none — set upstreams
Alpine / apkapkAPKINDEX.tar.gz + .apkproxy · local · hybrid✅none — set upstreams

Binaries & mirrors path-addressed ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
JetBrains IDEsjetbrainsIDE installer archivesproxy-only❌download.jetbrains.com
Generic mirrorgenericAny HTTP file treeproxy-only❌none — set upstreams + path_allow

Build caches RFC 0028 ​

RegistrytypeWhat it proxiesModesPublishDefault upstream
Nix binary cachenixnix-cache-info, one {hash}.narinfo per store path (relayed with only URL: rewritten, so every Sig: still verifies) and the NARsproxy · local · hybrid✅cache.nixos.org

Toolchains RFC 0010 ​

Typed, so a release can be blocked rather than merely cached — the identity the generic mirror cannot give the same bytes.

RegistrytypeWhat it proxiesModesPublishDefault upstream
Node distributionsnodedistindex.tab/index.json + release tarballs, SHASUMS256.txt byte-exact (nvm, fnm, n, mise)proxy-only❌nodejs.org/dist
SDKMANsdkmanCandidates API + download broker (the JDK, Gradle, Maven, Kotlin, …); the broker's 302 followed server-sideproxy-only❌api.sdkman.io/2 + broker.sdkman.io
Rust toolchainrustupChannel manifests (the filtered listing) + per-target component tarballs and their .sha256; the .asc relayed byte-exactproxy-only❌static.rust-lang.org

Feature matrix ​

Every registry and how its capabilities map across BatleHub's features. The path-addressed types (Deb, RPM, Pacman, JetBrains IDEs, Generic) have no per-package version model, so the structural axes (version listing, source archive, binary, age gate, warming, search) show —. They still get registry-level RBAC and multi-upstream fanout, and Deb/RPM/Pacman support signed private hosting. Forgejo and GitLab mirror GitHub's behaviour.

Legend: Ver. version listing · Src source archive · Bin binary/extension asset · Pub private publish · Fan multi-upstream fanout · Age release age gate · Warm cache warming (version enumeration) · Search Package Explorer upstream search. ✓ supported · — not applicable · ⚠ partial.

RegistryVer.SrcBinPubFanAgeRBACWarmSearch
GitHub✓✓✓—✓✓✓✓—
Forgejo / Gitea✓✓✓—✓✓✓✓—
GitLab✓✓✓—✓✓✓✓—
npm✓✓—✓✓✓✓✓✓
Cargo✓✓—✓✓✓✓✓✓
Go Modules✓✓—✓✓✓✓✓✓
Maven✓✓✓✓✓✓✓—✓
PyPI✓✓✓✓✓✓✓✓✓
Conda✓ ¹✓✓✓✓⚠ ²✓✓ ¹—
Composer✓✓—✓✓✓✓—✓
RubyGems✓✓—✓✓✓✓—✓
NuGet✓—✓✓✓✓✓✓✓
Terraform✓✓✓✓✓✓✓—✓
Ansible Galaxy✓ ⁶✓—✓✓✓ ⁷✓✓—
OpenVSX✓—✓✓✓✓✓✓✓
VS Code Marketplace✓—✓✓✓✓✓✓—
JetBrains Marketplace✓—✓✓✓✓✓—✓
Debian / APT ³———✓✓—✓——
RPM / YUM / DNF ³———✓✓—✓——
Pacman / Arch ³———✓✓—✓——
JetBrains IDEs ³————✓—✓——
Generic ³————✓—✓——
Node distributions✓✓✓—✓✓ ⁴✓✓ ⁵—
SDKMAN✓—✓—✓⚠ ⁴✓✓ ⁵—

¹ Conda has no dedicated per-package version listing API. BatleHub synthesises one by scanning repodata.json across noarch, linux-64, osx-64, osx-arm64, and win-64; results are the union of versions found on all available platforms.

² Conda timestamps come from the timestamp field in repodata.json (ms since epoch). Most packages carry it; packages without one skip the gate unless you set deny_missing_timestamp = true on the rule.

³ Path-addressed type: artifacts are fetched by file path with no per-package version model, so the structural axes show —. These types don't enumerate versions but can pre-warm specific files via cache.warm_paths, and are gated with a mandatory path_allow allowlist. Deb/RPM/Pacman additionally support signed private hosting (local/hybrid); JetBrains IDE archives and Generic are proxy-only.

⁴ Toolchain age gates (RFC 0010 §6.7): nodedist reads the release date from index.tab, so current releases are gated and a de-listed one reaches the gate undated; sdkman publishes no dates at all, so the gate is decided entirely by deny_missing_timestamp. On both kinds that field is mandatory on a release_age_gate rule.

⁵ Warming by platform: a Node release and an SDKMAN version are one archive per platform, so warm_packages warms the platforms in cache.warm_platforms, defaulting to the server's own. The console's per-version fetch button is refused for the same reason.

⁶ One page, always (RFC 0031 §4.4): ansible-galaxy resolves a pagination link against the configured API root, and upstream's links are absolute paths that replace the whole path — so no continuation BatleHub could emit would be followed back to it. Every listing it serves therefore carries a null next, and the adapter walks upstream's pages itself.

⁷ Galaxy age gates: every upstream collection version carries created_at, so the gate is fully decided in proxy mode. A locally published collection and an air-gapped listing may carry no date, so deny_missing_timestamp is mandatory on a release_age_gate rule here, as it is on the toolchain kinds.

Package Explorer upstream ("Not Yet Proxied") search: Go uses pkg.go.dev; PyPI is exact-name lookup; Terraform combines module search with namespace/exact provider lookup. The release proxies (GitHub/Forgejo/GitLab), VS Code Marketplace, Conda, and the path-addressed types have no upstream search API — see the Package Explorer guide.

READMEs ​

What a package says about itself, per version. Whether a registry has one at all is a property of its protocol, not a preference: the text either travels in a document the proxy already fetches to resolve a version, or it sits inside the artifact — and that decides whether the package page can show it for a version this instance holds no bytes for.

Held nowhere here is the column worth reading before you go looking for a gap: versions + README means the package page answers in full for a package nothing here has ever pulled; versions only means it lists the versions and says the README arrives when one is first downloaded; neither means the page answers from what this instance holds and nothing else.

Fetchable is whether the page offers a Fetch this version button on those upstream-only rows, and whether the catalogue offers one on an upstream search result. no is not a limitation of the button but of the coordinate: a Maven version is a set of files, a Terraform provider is addressed by OS and architecture as well as version, a PyPI version is an sdist plus one wheel per interpreter and platform, and a conda artifact carries a channel platform and a build string — so "fetch this version" has no single meaning for any of them. The page says which rather than showing a disabled button — see fetching from the console.

RegistryREADME sourcePer versionHeld nowhere hereFetchable
githubthe README is one of the repository files this proxy already serves by path, under raw/{ref}/, so a second URL for it would be a second answer to a solved question—neitherno
forgejothe README is one of the repository files this proxy already serves by path, under raw/{ref}/, so a second URL for it would be a second answer to a solved question—neitherno
gitlabthe README is one of the repository files this proxy already serves by path, under raw/{ref}/, so a second URL for it would be a second answer to a solved question—neitherno
cargoa file inside the artifactyesversions onlyyes
npmthe metadata document, else the artifactyesversions + READMEyes
openvsxa URL in the metadata, read separatelyyesversions + READMEyes
goproxya file inside the artifactyesversions onlyyes
pypithe metadata document, else the artifactyesversions + READMEno
condaa file inside the artifactyesversions onlyno
composera file inside the artifactyesversions onlyyes
vscode-marketplacea URL in the metadata, read separatelyyesversions + READMEyes
maventhe POM carries <description>, which is a sentence rather than a document; putting one where a reader expects the other makes every package look thinly documented—versions onlyno
terraforma file inside the artifactyesversions onlyno
rubygemsa file inside the artifactyesversions onlyyes
nugeta file inside the artifactyesversions onlyyes
debpath-addressed: there is no package identity to hang a README on—neitherno
rpmpath-addressed: there is no package identity to hang a README on—neitherno
pacmanpath-addressed: there is no package identity to hang a README on—neitherno
apkan .apk carries pkgdesc, one sentence in .PKGINFO; putting a sentence where a reader expects a document makes every package look thinly documented—neitherno
jetbrainspath-addressed: there is no package identity to hang a README on—neitherno
jetbrains-marketplacethe metadata document, already fetchedyesversions + READMEyes
genericpath-addressed: there is no package identity to hang a README on—neitherno
nodedista Node release is a set of tarballs and a checksum file; the dist tree carries no prose—versions onlyno
sdkmanSDKMAN describes a distribution, not a package: no document in the protocol carries prose about a candidate—versions onlyno
rustupa toolchain release is a manifest and a set of tarballs; the dist tree carries no prose—versions onlyno
galaxya file inside the artifactyesversions onlyyes
nixa store path is a NAR and its narinfo; the protocol carries no prose, and the NAR is a filesystem image rather than a package with a manifest—neitherno

Configured per registry with [registries.readme] and [registries.upstream_detail]. Both are on by default, and the second one makes an outbound request the first time somebody opens a package page for something this instance holds nothing of — see what leaves this instance.

Two settings shape what else the page can do: remote_images = "proxy" renders a README's images through this server rather than charting them, and console_fetch (on by default) is the Fetch this version button, on the package page and on the catalogue's upstream rows alike. Prose search across stored READMEs is instance-wide and off by default — [search] readmes.

See also ​

Released under the Apache 2.0 License. Made with ❤️ and too much ☕.