RenoP is a lightweight, self-hosted artifact repository server for individuals and small teams. It ships as a single binary with an embedded web UI and supports multiple registry protocols out of the box.
- Features
- Supported Registry Protocols
- Quick Start
- Authentication
- Configuration
- Repository Types
- Mirror Proxying
- Storage Backends
- GPG Signature Verification
- Outbound Proxy
- Message Center
- Audit Log
- System Service Installation
- Building from Source
- HTTP API Reference
- Price and Contribution
- License
- Maven repository — Release, snapshot, and private repositories with Maven 2 layout
- Cargo (Rust) registry — Sparse index protocol, crate ownership, yank/unyank, and documentation uploads
- Docker / OCI registry — Full OCI Distribution Specification v1.1.0 with Bearer token authentication
- Mirror proxying — Upstream proxy with local caching, negative caching, per-artifact allowlists, and TTL
- Multi-backend storage — Local disk or any S3-compatible object store (per-repository)
- GPG signature verification — Enforce detached
.ascsignatures before accepting Maven artifacts - Web management UI — Browser, upload, user management, token management, and repository settings
- FIDO2 / WebAuthn — Passwordless login and MFA with hardware security keys
- In-app message center — Durable per-user inbox with admin broadcast notifications
- Activity audit log — Immutable per-user action history with manager visibility
- Javadoc and Cargo-doc preview — In-browser viewing of extracted documentation jars and Cargo doc tarballs
- Embedded SVG badges — Latest-version badges for Maven artifacts
- Online and offline updater — One-click or automated binary updates without external tools
- System service integration —
--installand--uninstallsupport for Windows Services, systemd, OpenRC, macOS LaunchDaemons, and BSD rc.d - Debug profiling — Optional pprof heap, allocation, and goroutine dumps behind an authenticated endpoint
| Protocol | URL prefix | Specification |
|---|---|---|
| Maven 2 | /{repo}/ |
Maven repository layout |
| Cargo sparse index | /{repo}/ |
RFC 3239 |
| Docker / OCI Registry | /v2/ |
OCI Distribution Spec v1.1.0 |
| Javadoc preview | /javadoc/{repo}/ |
RenoP extension |
| Cargo-doc preview | /cargodoc/{repo}/ |
RenoP extension |
Download a release archive, extract it, and run the binary.
# Unix-like systems
./renop
# Windows
renop.exeRenoP listens on 0.0.0.0:3000 by default.
Set the admin password before first start:
RENOP_DEFAULT_ADMIN_PASSWORD='your-secure-password' ./renopIf the variable is not set, a randomly generated password is printed to the server log on the very first startup. Open
http://localhost:3000 to access the web UI.
Default Maven repository endpoints:
http://localhost:3000/releases
http://localhost:3000/snapshots
http://localhost:3000/private
Use one of these URLs in Maven's <repositories> or <distributionManagement> configuration.
Cargo sparse registry:
# .cargo/config.toml
[registries.my-registry]
index = "sparse+http://localhost:3000/rust-id/"
credential-provider = "cargo:token"Docker registry:
docker login localhost:3000
docker pull localhost:3000/my-image:latest
docker push localhost:3000/my-image:latestRenoP supports four credential types:
| Method | Use case | Details |
|---|---|---|
| Session cookie | Browser / web UI | Set after POST /api/auth/login. renop_session HttpOnly cookie, 7-day idle timeout. |
| Session header | API clients that hold a session | Authorization: Session <session-id> |
| Basic auth | Maven clients, CI/CD | Authorization: Basic base64(username:password) or Authorization: Basic base64(username:upload-token) |
| Bearer token | Upload-token-only clients | Authorization: Bearer <upload-token> or ?token=<upload-token> (GET/HEAD only) |
FIDO2 / WebAuthn is also supported as a passwordless login mechanism. Register a hardware key from the web UI profile page.
Every account carries a set of permission strings. Built-in shortcuts:
| Permission | Meaning |
|---|---|
manager |
Full administrative access |
canview:* |
Read access to all repositories |
canupdate:* |
Write access to all repositories |
canview:<repo> |
Read access to a specific repository |
canupdate:<repo> |
Write access to a specific repository |
Upload tokens inherit a subset of the account's permissions and may be scoped further by the manager.
RenoP reads its configuration and state file locations from environment variables. Paths are relative to the working directory unless they are absolute.
| Variable | Default | Purpose |
|---|---|---|
RENOP_CONFIG |
config.yaml |
Server, frontend, storage, database, updater, proxy, and audit settings |
RENOP_REPOSITORIES |
repositories.yaml |
Repository definitions, mirrors, and per-repository S3 settings |
RENOP_TOKENS |
tokens.yaml |
Account definitions and upload tokens |
RENOP_INDEX |
index.json |
Persisted Maven artifact index (rebuilt on startup or on demand) |
RENOP_SESSIONS |
sessions.bin |
Persisted browser sessions (legacy sessions.json is migrated automatically) |
RENOP_DEFAULT_ADMIN_PASSWORD |
(generated) | Initial password for the admin account on first startup |
Most settings can be changed live from the management UI without restarting the server. Changes to the listener address, TLS certificates, or debug mode require a restart.
# Root-level storage settings (also the StorageConfig domain in the settings API)
storage_path: storage # Base directory for on-disk artifact storage
enable_javadoc_preview: true # Enable in-browser Javadoc and Cargo-doc preview
javadoc_extract_path: "" # Override extraction directory (defaults to storage_path/.javadoc)
max_javadoc_size_mb: 48 # Maximum size (MiB) of a documentation archive accepted for extraction
frontend:
id: my-repo # Internal instance identifier
title: My Repository # Browser tab and UI title
description: "" # Short description shown on the landing page
organization_website: "" # URL linked from the organization logo
organization_logo: /svg/logo.svg
background_url: "" # Public WebP <= 5 MiB; served as a login-page background
icp_license: "" # Optional: ICP filing number (displayed in footer, China)
public_security_filing: "" # Optional: Public security filing number (China)
legal_notice_url: "" # Optional: Absolute URL for the legal notice footer link
server:
host: 0.0.0.0
port: 3000
ssl_enabled: false
ssl_cert_path: ""
ssl_key_path: ""
domains: # Canonical public hostnames; used for CORS and cookie domain
- localhost
cors_origins: [ ] # Additional CORS origins (supports *.example.com wildcards and *)
trusted_proxies: [ ] # Trusted reverse-proxy IP ranges for X-Forwarded-For
cdn_ip_header: X-Forwarded-For # Header used to extract the real client IP
file_cache_size_mb: 16 # In-memory LRU cache for hot artifact reads (0 = disabled)
max_active_requests: 2000 # Concurrency limit; excess requests receive 429
enable_compression: false # Brotli/gzip response compression
debug_mode: false # Enable pprof dump endpoints (restart required)
gpg:
key_servers: # HTTPS-only key servers for GPG key resolution
- https://keyserver.ubuntu.com
- https://keys.openpgp.org
- https://pgp.mit.edu
audit_log:
retention_days: 14 # Delete entries older than this many days
max_rows: 10000 # Delete oldest entries when total exceeds this count
updater:
channel: release # release | nightly
mode: manual # manual | auto_check | auto_install | safe_install
database:
driver: sqlite3 # sqlite3 | mysql | postgres
dsn: renop.db # SQLite file path, or a standard DSN for MySQL/PostgreSQL
max_open_conns: 25
max_idle_conns: 25
conn_max_lifetime_sec: 300
proxy:
selected: "" # Name of the active outbound proxy (empty = direct)
proxies:
- name: corp-proxy
url: http://proxy.example.com:8080 # HTTP, HTTPS, or SOCKS5
username: ""
password: ""repositories:
releases:
name: releases
visibility: PUBLIC # PUBLIC | HIDDEN | PRIVATE
allow_redeployment: true # Allow overwriting an existing artifact version
require_gpg_signature: false # Require detached .asc signatures for Maven artifacts
mirrors: # Zero or more upstream mirrors
- name: central
url: https://repo1.maven.org/maven2
persist: true # Cache downloaded artifacts to local storage
cache_ttl_secs: 3600 # How long a cached miss is considered valid
negative_cache: true # Cache 404 responses to avoid repeated upstream requests
timeout_secs: 30 # HTTP request timeout
proxy: "" # Named proxy override (empty = global proxy; "direct" = bypass)
authorization: null # null | {method: basic, login: user, password: pass}
allow_artifacts: [ ] # Group ID prefix allowlist (empty = all)
deny_artifacts: [ ] # Group ID prefix denylist
s3:
enabled: false
endpoint: "" # S3-compatible endpoint URL
bucket: ""
key_prefix: "" # Optional object key prefix within the bucket
region: auto
access_key_id: ""
secret_access_key: ""
force_path_style: true # Required for MinIO and most non-AWS S3 providers
redirect_downloads: false # Issue signed redirects instead of proxying blob downloads
rust-id:
name: rust-id
format: cargo # Mark this repository as a Cargo sparse registry
visibility: PUBLIC
mirrors: [ ]
s3:
enabled: false
# ... (same S3 fields as above)Reserved repository names (cannot be used as repository names): css, js, svg, api, javadocs,
assets, cargodoc, v2.
Maven repositories follow the standard Maven 2 directory layout (groupId/artifactId/version/artifactId-version.jar).
RenoP supports:
- Release, snapshot, and private visibility modes
- Automatic
maven-metadata.xmlgeneration and update on upload - Embedded Javadoc preview (from
.jarfiles containing a Javadoc directory) - Checksum generation (
md5,sha1,sha256,sha512) - GPG signature enforcement via detached
.ascfiles
Cargo repositories implement
the sparse index protocol. Set format: cargo in
repositories.yaml and configure Cargo clients with:
# .cargo/config.toml
[registries.my-registry]
index = "sparse+http://localhost:3000/<repo-name>/"
credential-provider = "cargo:token"Cargo-specific operations supported:
- Publish new crate versions (
cargo publish) - Yank and unyank specific versions (
cargo yank,cargo yank --undo) - Per-crate ownership management with invitation-based workflows
- Cargo-doc tarball upload and sandboxed in-browser viewing at
/cargodoc/{repo}/{crate}/{version}/ - Crate search and version listing
RenoP implements the OCI Distribution Specification v1.1.0
at /v2/. Authentication uses the Bearer token challenge flow.
docker login localhost:3000
docker pull localhost:3000/my-namespace/my-image:tag
docker push localhost:3000/my-namespace/my-image:tagEach repository can define one or more upstream mirrors. When a requested artifact is not found locally, RenoP fetches it from the mirrors in order and optionally caches the result.
Cache behavior:
- Positive hits (artifact found upstream) are cached on disk if
persist: true. - Negative hits (upstream returns 404) are cached for
cache_ttl_secswhennegative_cache: true. - Cached files are served on subsequent requests without contacting the upstream.
Artifact filtering:
allow_artifactsis a list of Maven group ID prefixes. Only artifacts whose group ID starts with one of these values are fetched from this mirror. An empty list allows all group IDs.deny_artifactsblocks specific prefixes regardless of the allowlist.
Per-mirror proxy routing:
| Value | Behavior |
|---|---|
"" |
Follow the global proxy.selected setting |
"direct" |
Bypass the global proxy for this mirror |
"<name>" |
Use a named proxy from proxy.proxies |
Artifacts are stored under storage_path using the same Maven layout as the repository URL:
{storage_path}/{repo_name}/{groupId-with-slashes}/{artifactId}/{version}/{file}
Set s3.enabled: true in a repository definition to store new uploads in an S3 bucket. Existing files on local disk
continue to be served from disk. Set redirect_downloads: true to issue pre-signed URLs to clients instead of proxying
blob downloads through RenoP.
Compatible providers: AWS S3, MinIO, Cloudflare R2, Wasabi, Backblaze B2, and any S3-compatible provider.
When require_gpg_signature: true is set on a repository, RenoP requires a detached OpenPGP signature file (.asc) to
accompany every uploaded Maven artifact. Artifacts submitted without a valid, matching signature are quarantined under
.renop.tmp.gpg/ and the upload is rejected.
Key lookup uses the HTTPS key servers listed under server.gpg.key_servers. Only HTTPS origins are accepted; embedded
paths, credentials, query strings, or fragments in key server URLs are rejected at configuration time.
RenoP can route upstream mirror traffic through an HTTP, HTTPS, or SOCKS5 proxy. Define one or more named proxies under
proxy.proxies and set proxy.selected to the name of the globally active one. Individual mirrors can override this on
a per-mirror basis using the proxy field.
RenoP includes a durable per-user inbox. Messages are persisted in the database and survive server restarts. Managers can send broadcast announcements to all users or to a specific list of recipients.
Supported message kinds:
| Kind | Description |
|---|---|
announcement |
Administrator broadcast to one or more users |
team_invite |
Cargo crate ownership invitation requiring a response |
system |
Internal server event |
Messages with a pending action (e.g., a team invitation awaiting acceptance or decline) cannot be deleted until the action is resolved.
Endpoints under GET/POST/DELETE /api/messages/... require an authenticated session. Admin endpoints
(GET /api/messages/admin/users, POST /api/messages/admin) require the manager permission.
Every significant API action is recorded in the audit log with the following fields: acting username, operator (which may differ when an admin acts on behalf of another user), authentication method, session ID, client IP, a machine-readable action code, and a human-readable description.
- Users can view their own log at
GET /api/profile/audit-logs. - Managers can view and clear any user's log at
GET /api/users/{username}/audit-logs. - Users cannot delete their own log entries (
DELETE /api/profile/audit-logsalways returns403).
Retention is controlled by server.audit_log.retention_days and server.audit_log.max_rows.
RenoP can register itself as a platform service to start automatically at boot.
# Run as administrator / root
./renop --install
# Remove the service registration
./renop --uninstall| Platform | Service backend |
|---|---|
| Windows | Windows Service Control Manager (SCM) |
| Linux with systemd | systemd unit file |
| Linux with OpenRC | OpenRC init script |
| macOS | LaunchDaemons plist |
| BSD | rc.d script |
RenoP must be built with the custom Go fork. The standard Go toolchain will not work because RenoP relies on runtime and standard library modifications from that fork. You also need PowerShell 7 and Node.js 18+.
Toolchain setup:
- Check the required Go version in
go.mod. - Download the newest release tag beginning with
go<version>for your OS and architecture from the fork's GitHub releases page. - Verify the download against
SHA256SUMSin the same release. - Extract the archive, set
GOROOTto the extractedgo/directory, and addGOROOT/bintoPATH. - Confirm with:
go version
Build commands:
# Full matrix release build (all OS/arch combinations, produces .zip archives)
pwsh ./build.ps1
# Packaged release build for the current OS/arch only
pwsh ./build.ps1 c
# Local development build (unzipped binary, no frontend rebuild)
pwsh ./build.ps1 c nb
# Cross-compile for a single OS only
pwsh ./build.ps1 s
# Release build with explicit version string
pwsh ./build.ps1 -Version v1.2.3 -Development falseFrontend and protobuf generation:
pnpm install --frozen-lockfile
pnpm --filter renop-html build
go generate ./frontend
# Regenerate protobuf Go bindings
protoc -I proto --go_out=. --go_opt=module=renop proto/api/v1/api.protoRun the test suite (requires the frontend dist/ to be present):
go test ./...The management API is mounted at /api. Maven artifact storage is served directly at /{repo}/{path}. The Docker/OCI
registry is at /v2/. Cargo endpoints are sub-paths of the repository URL.
Most management endpoints use application/x-protobuf (Protocol Buffers) for request and response bodies. Schema
definitions are in proto/api/v1/api.proto. Error responses are generally plain text; always
prefer the HTTP status code over the body for programmatic handling.
- OpenAPI 3.0.3 specification:
openapi.yaml - Hosted API documentation:
docs/api/README
| Tag | Routes | Notes |
|---|---|---|
auth |
POST /api/auth/login, /api/auth/logout, /api/auth/me, /api/auth/profile/... |
Session management and FIDO2 |
tokens |
GET/PUT/DELETE /api/tokens/{name}, /api/tokens/{name}/sessions/... |
Account CRUD (manager-only) |
maven |
GET /api/maven/details/..., /api/maven/versions/..., /api/maven/latest/... |
File metadata and version helpers |
storage |
GET/HEAD/PUT/POST/DELETE /{repo}/{path} |
Raw artifact access (Maven layout) |
cargo |
/{repo}/api/v1/..., /{repo}/config.json |
Cargo sparse registry |
docker |
/v2/... |
OCI Distribution Spec v1.1.0 |
javadoc |
/javadoc/{repo}/{path} |
Javadoc jar preview |
status |
GET /api/status/health, /api/status/instance, /api/status/snapshots |
Health and metrics |
settings |
GET/PUT /api/settings/domain/{name}, /api/settings/maven/repositories/... |
Configuration management |
updater |
POST /api/updater/check, /api/updater/install, /api/updater/restart |
Update management |
messages |
GET/POST/DELETE /api/messages/... |
User inbox and admin broadcast |
audit |
GET /api/profile/audit-logs, GET/DELETE /api/users/{username}/audit-logs |
Activity log |
debug |
GET /api/debug/memory/... |
pprof dumps (requires debug_mode) |
RenoP is completely free for all users — individuals, teams, businesses, and non-profit organizations of any size.
RenoP guarantees no hidden data collection, no advertisements, no annoying pop-ups, and no subscription tiers. RenoP has only this one free community edition.
If you would like to contribute:
- Report issues — Submit valid bug reports via GitHub Issues
- Fix issues — Submit pull requests with tests
- Sponsor — Support ongoing development via Patreon
- Star — Give the repository a star on GitHub
If you intend to maintain a long-term fork, please consider whether you can sustain it and maintain a reasonable stability standard for your users. Opening an issue or submitting a pull request is almost always a better alternative.
RenoP is licensed under the Mozilla Public License 2.0 and is marked as incompatible with secondary licenses.
Third-party components retain their own licenses. Copyright notices, SPDX identifiers, Apache NOTICE excerpts, and
full license texts are in THIRD_PARTY_NOTICES.md (also shipped in release archives). RenoP
used Reposilite as a design reference only; its code is not redistributed
here.