User Guide
This guide covers how to set up your local development environment to use BatleHub as a registry proxy, and how to publish private packages when your administrator has enabled local or hybrid mode.
Getting a token
Most BatleHub endpoints require a Bearer token. Ask your administrator for a token or, if OIDC login is enabled, generate one yourself:
Via the Web UI: log in at https://batlehub.example.com, open Settings → Tokens, and click "New token".
Via the API:
# Exchange your OIDC session token for a long-lived API token
curl -X POST \
-H "Authorization: Bearer <oidc-session-token>" \
-H "Content-Type: application/json" \
-d '{"name": "my-laptop", "expires_in_days": 90, "role": "user"}' \
https://batlehub.example.com/api/v1/auth/tokensThe raw token value is shown once — save it to a password manager or environment variable.
export BATLEHUB_TOKEN=bh_xxxxxxxxxxxxxxxxxxxxAuthenticating from GitHub / Forgejo Actions
If your administrator has configured an actions-oidc auth provider, GitHub and Forgejo workflow jobs can authenticate without any long-lived secret. The workflow requests a short-lived OIDC token from the runner and passes it directly as a Bearer token.
Enable OIDC token minting in your workflow:
jobs:
publish:
permissions:
id-token: write # required — lets the runner mint an OIDC token
contents: readThen exchange the token at the start of any step that calls BatleHub:
# In a GitHub Actions "run:" step:
BATLEHUB_TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=batlehub" | jq -r '.value')
# Use it exactly like any other Bearer token
curl -H "Authorization: Bearer $BATLEHUB_TOKEN" \
https://batlehub.example.com/api/v1/...The token is valid for the duration of the job. It carries claims like repository, ref, environment, and actor, which the actions-oidc provider uses to assign you to one or more groups — for example "github-actions/myorg-my-repo/main" — so you automatically receive the right RBAC permissions without any manual user management.
Ask your administrator which groups are mapped and what permissions they carry.
Setup Guide UI
The built-in Setup Guide at https://batlehub.example.com/setup generates ready-to-paste config snippets for every registered tool. The snippets are pre-filled with your server's address and available registries — use them as a starting point for the manual steps below.
npm
Point npm at BatleHub
# .npmrc (project root or ~/.npmrc)
registry=https://batlehub.example.com/proxy/npm/
//batlehub.example.com/proxy/npm/:_authToken=${BATLEHUB_TOKEN}For scoped packages only:
@myorg:registry=https://batlehub.example.com/proxy/npm/
//batlehub.example.com/proxy/npm/:_authToken=${BATLEHUB_TOKEN}Install packages
npm install lodash
npm install @myorg/my-private-packagePublish a private package (local/hybrid mode)
The registry must be in local or hybrid mode — ask your administrator.
npm publish --registry https://batlehub.example.com/proxy/internal-npm/Or, with .npmrc configured for internal-npm:
npm publishSecurity audit
npm audit works automatically once the registry is configured — both quick and bulk audit modes are proxied through BatleHub to the upstream advisory database.
npm audit
npm audit --fixVerify
npm view lodash --registry https://batlehub.example.com/proxy/npm/Cargo
Point Cargo at BatleHub (proxy mode)
Replace the default crates.io source so all cargo add / cargo build requests go through BatleHub:
# .cargo/config.toml
[source.crates-io]
replace-with = "batlehub"
[source.batlehub]
registry = "sparse+https://batlehub.example.com/proxy/cargo/registry/"Private registry (local/hybrid mode)
Configure an additional named registry for private crates:
# .cargo/config.toml
[registries.internal]
index = "sparse+https://batlehub.example.com/proxy/internal/registry/"
token = "<your-token>"Or export the token as an environment variable (useful in CI):
export CARGO_REGISTRIES_INTERNAL_TOKEN=$BATLEHUB_TOKENPublish a crate
cargo publish --registry internalDepend on a privately published crate
# Cargo.toml
[dependencies]
my-lib = { version = "0.1", registry = "internal" }Yank / restore a version
cargo yank --registry internal my-lib@0.1.0
cargo yank --undo --registry internal my-lib@0.1.0Verify
cargo add serde # via proxy (replaces crates-io)
cargo add my-lib --registry internal # private registryGo Modules
Point the go toolchain at BatleHub
export GONOSUMCHECK="*"
export GONOSUMDB="*"
export GOPROXY="https://batlehub.example.com/proxy/go,direct"To make this permanent:
go env -w GONOSUMCHECK="*"
go env -w GONOSUMDB="*"
go env -w GOPROXY="https://batlehub.example.com/proxy/go,direct"GONOSUMCHECK / GONOSUMDB disable the public checksum database — required for private modules. The ,direct fallback lets the go tool reach the internet if BatleHub returns a 404.
Fetch a module
go get golang.org/x/text@v0.3.7Publish a private module (local/hybrid mode)
1. Build the module zip (standard Go module zip format):
# From the root of your module (where go.mod lives)
go mod zip example.com/mymod@v1.0.0 . --mod-zip /tmp/mymod-v1.0.0.zip2. Upload:
curl -X PUT \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
-H "Content-Type: application/zip" \
--data-binary @/tmp/mymod-v1.0.0.zip \
"https://batlehub.example.com/proxy/internal-go/example.com/mymod/@v/v1.0.0.zip"The module path may contain slashes (e.g. example.com/org/mymod). BatleHub extracts go.mod from the zip and generates version metadata automatically.
3. Point the toolchain at the private proxy:
export GOPROXY="https://batlehub.example.com/proxy/internal-go,direct"
go get example.com/mymod@v1.0.0Vulnerability scanning with govulncheck
BatleHub proxies the Go Vulnerability Database so govulncheck works without direct access to vuln.go.dev. Set GOVULNDB to the same base URL as GOPROXY:
export GOVULNDB="https://batlehub.example.com/proxy/go"
govulncheck ./...With authentication (put the token in ~/.netrc):
echo "machine batlehub.example.com login user password $BATLEHUB_TOKEN" >> ~/.netrc
chmod 600 ~/.netrcThe govulndb URL can be changed per-registry with vuln_db_url in the server config (default: https://vuln.go.dev). Setting it to "" disables the endpoints.
Zip format requirements
All entries inside the zip must be prefixed with {module}@{version}/. The go mod zip command produces this layout automatically. If you build the zip manually, ensure every file path starts with example.com/mymod@v1.0.0/.
VS Code Extensions
Point VS Code at BatleHub (OpenVSX)
Add to .vscode/settings.json or user settings:
{
"vscode-extension-marketplace.serviceUrl": "https://batlehub.example.com/proxy/openvsx"
}Download and install an extension
curl -H "Authorization: Bearer $BATLEHUB_TOKEN" \
"https://batlehub.example.com/proxy/vscode/ms-python.python/2024.2.1/vsix" \
-o ms-python.python-2024.2.1.vsix
code --install-extension ms-python.python-2024.2.1.vsixPublish a private extension (local mode)
Both openvsx and vscode-marketplace registry types support the same upload endpoint. Extension IDs follow the {publisher}.{name} convention.
curl -X PUT \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @my-org.my-extension-1.0.0.vsix \
"https://batlehub.example.com/proxy/internal-ext/my-org.my-extension/1.0.0/vsix"Download a private extension
curl -H "Authorization: Bearer $BATLEHUB_TOKEN" \
"https://batlehub.example.com/proxy/internal-ext/my-org.my-extension/1.0.0/vsix" \
-o my-org.my-extension-1.0.0.vsix
code --install-extension my-org.my-extension-1.0.0.vsixComposer (PHP)
Point Composer at BatleHub
Add a repository entry to composer.json in your project. BatleHub implements the Packagist v2 protocol (packages.json + p2/ metadata endpoints), so Composer treats it as a native Composer repository.
{
"repositories": [
{
"type": "composer",
"url": "https://batlehub.example.com/proxy/packagist/",
"options": {
"http": {
"header": ["Authorization: Bearer ${BATLEHUB_TOKEN}"]
}
}
}
]
}For credentials, store them in auth.json (in ~/.config/composer/ or the project root — never commit this file):
{
"http-basic": {
"batlehub.example.com": {
"username": "user",
"password": "<your-token>"
}
}
}When auth.json is in place, the options.http.header entry in composer.json is not needed.
Install packages
composer install
composer require symfony/consolePublish a private package (local/hybrid mode)
The registry must be in local or hybrid mode — ask your administrator.
Create a ZIP archive that contains a composer.json at its root (or inside a single top-level directory, like a GitHub archive):
# Create the ZIP — composer.json must have "name" and "version" fields
zip -r my-vendor-my-pkg-1.0.0.zip my-vendor-my-pkg-1.0.0/
# Upload
curl -X POST \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
-H "Content-Type: application/zip" \
--data-binary @my-vendor-my-pkg-1.0.0.zip \
"https://batlehub.example.com/proxy/internal-composer/api/upload"The name field in composer.json must follow the vendor/package format (e.g. "name": "my-vendor/my-pkg"). The version field is used as the package version; it can be overridden by the ?version= query parameter on the upload URL.
Yank a version
curl -X DELETE \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
"https://batlehub.example.com/proxy/internal-composer/api/packages/my-vendor/my-pkg/versions/1.0.0"Yanked versions are hidden from version listings and return 404 on download attempts.
Security audit
composer audit works automatically once the repository is configured — BatleHub proxies the Packagist security advisory API transparently.
composer auditVerify
# List available versions of a package
curl -s "https://batlehub.example.com/proxy/packagist/p2/symfony/console.json" \
-H "Authorization: Bearer $BATLEHUB_TOKEN" | jq '.packages | keys'PyPI (Python packages)
Point pip at BatleHub
Add to ~/.pip/pip.conf (Linux/macOS) or %APPDATA%\pip\pip.ini (Windows):
[global]
index-url = https://batlehub.example.com/proxy/my-pypi/simple/For uv, add to pyproject.toml:
[[tool.uv.index]]
name = "batlehub"
url = "https://batlehub.example.com/proxy/my-pypi/simple/"
default = trueBoth tools read credentials from ~/.netrc automatically:
machine batlehub.example.com
login <your-user-id>
password <your-token>Alternatively, embed credentials in the URL:
index-url = https://__token__:<your-token>@batlehub.example.com/proxy/my-pypi/simple/Install packages
pip install requests
uv pip install requests
poetry add requests # after configuring the source in pyproject.tomlPublish a private package (local/hybrid mode)
The registry must be in local or hybrid mode — ask your administrator.
Build and upload with twine:
# Build wheel and source distribution
python -m build
# Upload via twine
twine upload \
--repository-url https://batlehub.example.com/proxy/my-private-pypi/legacy/ \
--username __token__ \
--password $BATLEHUB_TOKEN \
dist/*Or configure ~/.pypirc for convenience:
[distutils]
index-servers = batlehub
[batlehub]
repository = https://batlehub.example.com/proxy/my-private-pypi/legacy/
username = __token__
password = <your-token>Then: twine upload --repository batlehub dist/*
Browse published packages
After publishing, the package appears in the Simple index immediately:
curl -s "https://batlehub.example.com/proxy/my-private-pypi/simple/my-package/" \
-H "Authorization: Bearer $BATLEHUB_TOKEN"Conda
Point conda at BatleHub
Add to ~/.condarc (or a .condarc in the project root):
channels:
- https://batlehub.example.com/proxy/my-conda
- nodefaultsConda reads credentials from ~/.netrc automatically:
machine batlehub.example.com
login <your-user-id>
password <your-token>Install packages
conda install numpy
conda env create -f environment.ymlAn environment.yml with the BatleHub channel:
name: myenv
channels:
- https://batlehub.example.com/proxy/my-conda
- nodefaults
dependencies:
- python=3.11
- numpyPublish a private conda package (local/hybrid mode)
The registry must be in local or hybrid mode — ask your administrator.
Build the package with conda build, then upload:
# Build
conda build my-recipe/
# Upload (.tar.bz2 or .conda format both accepted)
curl -X POST \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @my-pkg-1.0.0-py311h0_0.tar.bz2 \
"https://batlehub.example.com/proxy/my-private-conda/linux-64/"The package is extracted automatically — name, version, build, and dependencies are read from info/index.json inside the archive. The channel's repodata.json is updated immediately.
Verify
# Check repodata.json for your package
curl -s "https://batlehub.example.com/proxy/my-conda/linux-64/repodata.json" \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(list(d['packages'].keys())[:10])"NuGet (.NET)
Point dotnet at BatleHub (proxy mode)
Add the BatleHub source once with the CLI:
dotnet nuget add source \
https://batlehub.example.com/proxy/nuget/nuget/v3/index.json \
--name batlehub \
--username __token__ \
--password $BATLEHUB_TOKENOr declare it in a project-level nuget.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="batlehub"
value="https://batlehub.example.com/proxy/nuget/nuget/v3/index.json" />
</packageSources>
<packageSourceCredentials>
<batlehub>
<add key="Username" value="__token__" />
<add key="ClearTextPassword" value="<your-token>" />
</batlehub>
</packageSourceCredentials>
</configuration>Install packages
dotnet add package Newtonsoft.Json
dotnet restorePrivate registry (local/hybrid mode)
The registry must be in local or hybrid mode — ask your administrator.
Pack and publish:
dotnet pack MyLib.csproj -c Release
dotnet nuget push bin/Release/MyLib.1.0.0.nupkg \
--api-key $BATLEHUB_TOKEN \
--source https://batlehub.example.com/proxy/internal-nuget/nuget/v3/index.jsondotnet nuget push sends a multipart/form-data request; BatleHub returns 201 Created on success and 409 Conflict if the version already exists.
Yank a version
curl -X DELETE \
-H "Authorization: Bearer $BATLEHUB_TOKEN" \
"https://batlehub.example.com/proxy/internal-nuget/nuget/v2/package/mylib/1.0.0"Check for vulnerable packages
dotnet list package --vulnerable works automatically — BatleHub exposes a VulnerabilitiesUrl resource in the v3 service index and proxies the vulnerability catalogue from the upstream NuGet gallery.
dotnet list package --vulnerable
dotnet list package --vulnerable --include-transitiveVerify
# Service index (all NuGet clients fetch this first)
curl -s https://batlehub.example.com/proxy/nuget/nuget/v3/index.json | jq '.version'
# → "3.0.0"
# Version list after publish
curl -s https://batlehub.example.com/proxy/internal-nuget/nuget/v3/flat/mylib/index.json
# → {"versions":["1.0.0"]}Forgejo / Gitea releases
A forgejo registry proxies release assets, source archives, and raw files from a Forgejo or Gitea instance (set upstreams to the instance root, e.g. https://codeberg.org). It reuses the GitHub URL scheme.
REG="$BATLEHUB/proxy/my-forgejo"
# List releases / get a release by tag
curl $REG/<owner>/<repo>/releases
curl $REG/<owner>/<repo>/releases/tags/v1.0.0
# Download a release asset by filename
curl -L -O $REG/<owner>/<repo>/releases/download/v1.0.0/app.tar.gz
# Source tarball / zip for a tag, branch, or commit
curl -L -O $REG/<owner>/<repo>/tarball/v1.0.0
curl -L -O $REG/<owner>/<repo>/zipball/v1.0.0
# Raw file
curl -L $REG/<owner>/<repo>/raw/main/README.mdFor private instances, configure a bearer token as the registry's upstream auth.
Package registries
A forgejo registry also proxies the Forgejo/Gitea package registry at /api/packages/{owner}/…. This is a transparent cache — ideal for the generic package registry (immutable file downloads):
curl -L -O $BATLEHUB/proxy/my-forgejo/api/packages/<owner>/generic/<name>/<version>/<file>For ecosystem registries (npm, Maven, PyPI, Composer, NuGet, …), use the matching typed adapter pointed at the package endpoint instead — it rewrites metadata URLs so downloads are cached through BatleHub. For example, a npm registry:
[[registries]]
type = "npm"
name = "forgejo-npm"
upstreams = ["https://codeberg.org/api/packages/myorg/npm"]
[registries.upstream_auth]
type = "bearer"
token = "${FORGEJO_TOKEN}"GitLab releases
A gitlab registry proxies releases, release link assets, and source archives from a GitLab instance (upstreams = instance root, e.g. https://gitlab.com). Project paths may include nested groups; the release sub-path is separated by /-/, mirroring GitLab's own URLs.
REG="$BATLEHUB/proxy/my-gitlab"
# List releases / get a release by tag (nested groups allowed)
curl $REG/<group>/<project>/-/releases
curl $REG/<group>/<subgroup>/<project>/-/releases/v1.0.0
# Download a release link asset (matched by link name)
curl -L -O $REG/<group>/<project>/-/releases/v1.0.0/downloads/app.bin
# Source archive for a tag (format inferred from the extension)
curl -L -O $REG/<group>/<project>/-/archive/v1.0.0/source.tar.gz
# Raw file from the repository
curl -L $REG/<group>/<project>/-/raw/main/README.mdGitLab personal access tokens use the PRIVATE-TOKEN header — configure it as a custom upstream auth header on the registry.
Package registries
A gitlab registry also proxies the GitLab Packages API under /api/v4/…. This is a transparent cache — ideal for the generic package registry:
curl -L -O $BATLEHUB/proxy/my-gitlab/api/v4/projects/<id>/packages/generic/<name>/<version>/<file>For ecosystem registries (npm, Maven, PyPI, NuGet, Composer, …), use the matching typed adapter pointed at the GitLab package endpoint, which rewrites metadata URLs so downloads route through BatleHub.
Debian / APT
A deb registry proxies a Debian/Ubuntu APT repository and, in local/hybrid mode, hosts your own: publish .deb packages and BatleHub regenerates the Packages/Release indexes, signing them with an Ed25519 OpenPGP key when [registries.repo_signing] is configured.
Consume the repository
REG="$BATLEHUB/proxy/my-apt/deb"
# Import the signing key (signed repos only)
curl -fsSL $REG/key.gpg | sudo tee /usr/share/keyrings/my-apt.asc >/dev/null
# Add the source (adjust suite/component to your repo)
echo "deb [signed-by=/usr/share/keyrings/my-apt.asc] $REG stable main" \
| sudo tee /etc/apt/sources.list.d/my-apt.list
sudo apt update && sudo apt install helloFor an unsigned local repository (no [registries.repo_signing] key), replace [signed-by=…] with [trusted=yes].
Proxy mode: a proxy registry has no BatleHub key —
…/deb/key.gpgis served only forlocal/hybridregistries with arepo_signingkey. In proxy mode BatleHub relays the upstream repo'sInRelease/Release.gpgand its signature, so apt verifies against the upstream's archive key:bash# Official Debian/Ubuntu mirrors already ship the key # (packages: debian-archive-keyring / ubuntu-keyring): echo "deb [signed-by=/usr/share/keyrings/debian-archive-keyring.gpg] \ $BATLEHUB/proxy/my-apt/deb stable main" | sudo tee /etc/apt/sources.list.d/my-apt.listA
NO_PUBKEY/ "the following signatures couldn't be verified" error means that key isn't in the keyring named bysigned-by— installdebian-archive-keyring(Debian) orubuntu-keyring(Ubuntu), import the upstream's key into a keyring and pointsigned-byat it, or use[trusted=yes]if you trust the channel.
Private registry access
APT reads credentials from /etc/apt/auth.conf.d/ (Debian 9+ / Ubuntu 19.04+). The sources.list entry stays unchanged — credentials live in a separate file that is not visible to apt-cache policy.
sudo tee /etc/apt/auth.conf.d/batlehub.conf > /dev/null <<'EOF'
machine batlehub.example.com
login <your-username>
password <your-token>
EOF
sudo chmod 0600 /etc/apt/auth.conf.d/batlehub.conf
sudo apt updateOn older systems without auth.conf.d support, use /etc/apt/auth.conf with the same machine / login / password stanza.
Alternatively, embed the credentials directly in the URL (less secure — the token is visible in apt-cache policy output):
echo "deb [signed-by=…] https://<user>:<token>@batlehub.example.com/proxy/my-apt/deb stable main" \
| sudo tee /etc/apt/sources.list.d/my-apt.listPublish a .deb (local/hybrid mode)
curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
--data-binary @hello_1.0_amd64.deb \
$BATLEHUB/proxy/my-apt/deb/pool/stable/main/uploadThe distribution (stable) and component (main) come from the upload path; BatleHub derives the pool location, regenerates the suite indexes, and re-signs InRelease/Release.gpg.
RPM / YUM (DNF)
An rpm registry proxies a YUM/DNF repository and, in local/hybrid mode, hosts your own: publish .rpm packages and BatleHub regenerates repodata/, signing repomd.xml.asc with an Ed25519 OpenPGP key when configured.
Consume the repository
# /etc/yum.repos.d/my-rpm.repo
[my-rpm]
name=my-rpm
baseurl=$BATLEHUB/proxy/my-rpm/rpm
enabled=1
repo_gpgcheck=1
gpgcheck=0
gpgkey=$BATLEHUB/proxy/my-rpm/rpm/repodata/repomd.xml.keysudo dnf makecache && sudo dnf install helloFor an unsigned local repo (no [registries.repo_signing] key), set repo_gpgcheck=0 and omit gpgkey.
Proxy mode: a proxy registry has no BatleHub key —
repodata/repomd.xml.keyis served only forlocal/hybridregistries with arepo_signingkey. In proxy mode BatleHub relays the upstreamrepodata(including anyrepomd.xml.asc), so either pointgpgkeyat the upstream project's key withrepo_gpgcheck=1, or setrepo_gpgcheck=0if you trust the channel.
Private registry access
DNF/YUM reads username and password directly from the .repo file:
# /etc/yum.repos.d/my-rpm.repo
[my-rpm]
name=my-rpm
baseurl=https://batlehub.example.com/proxy/my-rpm/rpm
enabled=1
repo_gpgcheck=0
gpgcheck=0
username=<your-username>
password=<your-token>Alternatively, use ~/.netrc (DNF and libcurl honour it for HTTP Basic Auth):
machine batlehub.example.com
login <your-username>
password <your-token>Publish an .rpm (local/hybrid mode)
curl -X PUT \
-H "Authorization: Bearer $TOKEN" \
--data-binary @hello-1.0-1.x86_64.rpm \
$BATLEHUB/proxy/my-rpm/rpm/uploadJetBrains IDE archives
A jetbrains registry is a proxy-only cache for JetBrains IDE installer archives. It is addressed purely by path: the segment after …/jetbrains/ maps directly onto the upstream URL. The first download is streamed from download.jetbrains.com and cached; subsequent downloads of the same file are served locally — ideal for CI/Docker builds and offline networks.
REG="$BATLEHUB/proxy/jetbrains-ide/jetbrains"
# download.jetbrains.com/idea/ideaIC-2024.1.4.tar.gz
# → $REG/idea/ideaIC-2024.1.4.tar.gz
curl -fL -o ideaIC.tar.gz $REG/idea/ideaIC-2024.1.4.tar.gzThere is no publish, signing, or local hosting — it is a cache only.
Use the canonical path — redirects are followed for you. Always request the
download.jetbrains.compath (e.g.idea/ideaIC-2024.1.4.tar.gz). That host 302-redirects to a CDN (download-cdn.jetbrains.com); BatleHub follows the redirect automatically and caches the final bytes under the path you requested. You never put the CDN host in the URL. Use the real archive names —ideaIU-<ver>(Ultimate) /ideaIC-<ver>(Community); a wrong name returns the upstream's 404. To proxy the CDN host directly instead, setupstreams = ["https://download-cdn.jetbrains.com"].
Large archives: IDE archives are ~1–1.7 GB. The proxy buffers the whole artifact in memory before caching and rejects anything over
limits.max_artifact_size_bytes(default 500 MiB), so raise that limit (e.g.2147483648for 2 GiB) or downloads will fail. Overrideupstreamsto cache a different host such ashttps://plugins.jetbrains.com.
Download via the CLI
batlehub download fetches a file through the proxy (and caches it as a side effect). Give it a registry-relative path with -r, a full /proxy/… path, or a full URL:
# registry-relative (needs -r); writes ./ideaIC-2024.1.4.tar.gz
batlehub -r jetbrains-ide download jetbrains/idea/ideaIC-2024.1.4.tar.gz
# or a full proxy path / URL, with an explicit output file
batlehub download /proxy/jetbrains-ide/jetbrains/idea/ideaIC-2024.1.4.tar.gz -o idea.tgzPre-warm the cache
Path-addressed registries pre-warm specific paths (there is no version model). List them under [registries.cache] warm_paths to fetch on startup, or warm on demand:
[registries.cache]
warm_paths = ["idea/ideaIC-2024.1.4.tar.gz"]# Warm one or more paths now (admin):
batlehub admin cache warm jetbrains-ide --paths "idea/ideaIC-2024.1.4.tar.gz"Team Namespace dashboard
If your administrator has assigned namespace claims to your group, the Team Namespace page at /my-namespace gives you a single place to view your ownership, browse published packages, manage visibility, and upload new packages without needing CLI access.
Your groups
The top card lists every auth-provider group you belong to. These are the values your administrator uses when creating namespace claims. Spaces are stripped from group names because package prefixes cannot contain spaces — "oidc:my team" is shown and matched as "oidc:myteam".
Your namespaces
The My namespaces table shows every namespace prefix claimed for your groups, across all registries. Each row shows:
| Column | Description |
|---|---|
| Registry | The registry this claim applies to |
| Prefix | Package name prefix your group owns |
| Group | The group identifier (spaces stripped) |
Click any row to load the packages published under that namespace.
Browsing and managing packages
After clicking a namespace row, the Packages card shows all published versions under that prefix. Columns include package name, version, visibility, publisher, and publication date.
Changing visibility inline:
Click the visibility badge on any row (or the "Edit visibility" button) to open an inline dropdown. Choose the new level and click Save:
| Level | Who can download |
|---|---|
public | Everyone, including unauthenticated |
internal | Any authenticated user |
team | Members of your group only |
Results are paginated (50 per page). Use the Previous / Next buttons to navigate.
Uploading packages
The Upload package card lets you publish directly from the browser for registry types that accept binary file uploads. Only registries in local or hybrid mode appear in the selector.
File upload (browser)
| Registry type | Accepted file | Extra fields |
|---|---|---|
| RubyGems | .gem | None — name and version are read from the gem |
| Composer | .zip | None — name and version are read from composer.json inside the archive |
| OpenVSX / VS Code Marketplace | .vsix | Extension ID (publisher.name) and version |
| Go modules | .zip | Module path (e.g. github.com/org/repo) and version (e.g. v1.0.0) |
| PyPI | .whl, .tar.gz, .zip | None — name and version are parsed from the filename |
| Conda | .tar.bz2, .conda | Platform (e.g. linux-64) — name, version, and build are read from info/index.json |
Select the registry, fill in any extra fields, choose the file, and click Upload.
Go module zip format
The zip must follow the standard Go module layout — every entry must be prefixed with {module}@{version}/. Running go mod zip produces this layout automatically.
CLI (npm, Cargo, Maven, Terraform, NuGet)
For registry types without a browser-friendly binary format, the CLI instructions tab shows ready-to-paste commands pre-filled with your registry name. See the full publishing guide for each ecosystem's complete setup steps.
Permissions
| Permission | What it grants |
|---|---|
releases:read | List versions, download release assets and metadata |
source:read | Download source archives (tarballs, .crate, module .zip) |
* | All permissions (admin) |
Role inheritance: admin ⊃ user ⊃ anonymous. Your administrator can assign additional permissions to OIDC groups or Kubernetes service account namespaces on top of your role.
Troubleshooting
403 Forbidden on download: Your token is missing or your role doesn't have releases:read or source:read for this registry. Check with your administrator.
403 Forbidden on publish — "registry is not in local or hybrid mode": Publishing is disabled on this registry. Ask your administrator to enable mode = "local" or mode = "hybrid".
409 Conflict on publish: The version already exists. Bump the version in your package manifest.
cargo publish fails with "invalid token": Verify the index URL in .cargo/config.toml ends with /registry/:
sparse+https://batlehub.example.com/proxy/internal/registry/Go: disabled by GOPROXY=...off: The proxy can't reach the upstream or the module doesn't exist there. Remove ,off from GOPROXY to allow direct fallback, or check that the upstream is reachable from the BatleHub server.
dotnet nuget push returns 401: BatleHub accepts the --api-key value as a Bearer token (the X-NuGet-ApiKey header is transparently normalised to Authorization: Bearer). Make sure the token has releases:write or admin permissions on the registry.