- Rust 93.8%
- Shell 3.9%
- TypeScript 1.5%
- Python 0.5%
- Dockerfile 0.2%
* chore: release v1.2.1 * security(ui): redact proxy_upstreams for anonymous dashboard callers (#934) Extract `build_dashboard_response(state, authenticated)` from `api_dashboard` — when the caller is not authenticated, `proxy_upstreams` is an empty vec for every mount point, preventing upstream registry topology disclosure in air-gap deployments. Add opportunistic auth on the middleware's open web-surface path: if the request carries credentials on a publicly-browsable page (anonymous_read=true), validate them and insert the real identity so downstream handlers can distinguish authenticated from anonymous. * test(auth): cover opportunistic auth on open web surfaces (#934) Add tests for try_basic_auth unit function and opportunistic authentication middleware path exercised when anonymous_read=true. Fixes diff-coverage threshold by covering lines 255-274, 621-629 in auth/mod.rs. |
||
|---|---|---|
| .clusterfuzzlite | ||
| .config | ||
| .github | ||
| deploy | ||
| dist | ||
| docs-ru | ||
| fuzz | ||
| nora-registry | ||
| scripts | ||
| tests | ||
| .dockerignore | ||
| .git-blame-ignore-revs | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .trivyignore | ||
| _typos.toml | ||
| ARCHITECTURE.md | ||
| artifacthub-repo.yml | ||
| BENCHMARKS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| cliff.toml | ||
| clippy.toml | ||
| CODE_OF_CONDUCT.md | ||
| COMPAT.md | ||
| CONTRIBUTING.md | ||
| deny.toml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| GOVERNANCE.md | ||
| LICENSE | ||
| llms.txt | ||
| Makefile | ||
| MONITORING.md | ||
| README.md | ||
| renovate.json | ||
| ROADMAP.md | ||
| rust-toolchain.toml | ||
| SECURITY.md | ||
| tarpaulin.toml | ||
NORA
The artifact registry that grows with you. Starts with docker run, scales with your needs.
docker run -d -p 4000:4000 -v nora-data:/data getnora/nora:latest
Open http://localhost:4000/ui/ — your registry is ready.
Why NORA
- Zero-config — single binary, no database, no dependencies.
docker runand it works. - 15 registries — Docker, Maven, npm, PyPI, Cargo, Go, Raw, RubyGems, Terraform, Ansible Galaxy, NuGet, Pub (Dart/Flutter), Conan (C/C++), RPM (yum/dnf), Debian/APT.
- Secure by default — OpenSSF Scorecard, signed releases, SBOM, fuzz testing, 1200+ tests.
< 30 MB binary | < 50 MB RAM idle | 3s startup | 15 registries
Supported Registries
All endpoints require authentication. Anonymous read is opt-in via anonymous_read: true.
| Format | Pull (proxy/cache) | Push/Publish | Default Upstream | Notes |
|---|---|---|---|---|
| Docker Registry v2 | ✅ | ✅ | registry-1.docker.io |
hosted + proxy; cache on when docker.upstreams non-empty (Docker Hub by default) |
| Maven | ✅ | ✅ | repo1.maven.org/maven2 |
hosted + proxy |
| npm | ✅ | ✅ | registry.npmjs.org |
hosted + proxy |
| Cargo | ✅ | ✅ | crates.io (sparse index) |
hosted + proxy (sparse index) |
| PyPI | ✅ | ✅ | pypi.org/simple/ |
hosted + proxy |
| Go Modules | ✅ | — | proxy.golang.org |
proxy only (modules immutable, push not in protocol) |
| Raw files | ❌ | ✅ | — (no upstream) | hosted only; conditional PUT (ETag/If-Match — local backend only; If-None-Match: * works on any backend) |
| RubyGems | ✅ | ❌ | rubygems.org |
proxy only — gem push not implemented in NORA v1.1.0 |
| Terraform | ✅ | — | registry.terraform.io |
proxy only; client configuration notes in COMPAT.md |
| Ansible Galaxy | ✅ | ❌ | galaxy.ansible.com |
proxy only — ansible-galaxy collection publish not implemented |
| NuGet | ✅ | ❌ | api.nuget.org |
proxy only — dotnet nuget push not implemented |
| Pub (Dart/Flutter) | ✅ | ❌ | pub.dev |
proxy only — dart pub publish not implemented |
| Conan (C/C++) | ⚠️ | ❌ | center2.conan.io |
proxy only; Conan client compatibility tracked in COMPAT.md |
| RPM (yum/dnf) | ⚠️ | ✅ | — (none by default) | hosted; pull-through via config.registries.rpm.proxies (off by default); auto-generates repodata/ |
| Debian/APT | ⚠️ | ✅ | — (none by default) | hosted; pull-through via config.registries.deb.proxies (off by default); flat & structured layouts; auto-generates Packages/Release/InRelease |
Helm charts work via the Docker/OCI endpoint —
helm push/pullwith--plain-httpor behind TLS reverse proxy.
Pull/Push legend: ✅ supported · ⚠️ partial (pull-through available but off by default, or client compatibility issue) · ❌ not implemented in NORA v1.1.0 · — not applicable (protocol has no push). Per-format details and cache strategy in COMPAT.md.
Quick Start
Docker (Recommended)
docker run -d -p 4000:4000 -v nora-data:/data getnora/nora:latest
Binary
# x86_64
curl -fsSL https://github.com/getnora-io/nora/releases/latest/download/nora-linux-amd64 -o nora
# ARM64 (Raspberry Pi, Graviton, Apple Silicon VMs)
curl -fsSL https://github.com/getnora-io/nora/releases/latest/download/nora-linux-arm64 -o nora
chmod +x nora && ./nora
./nora listens on 127.0.0.1:4000. To expose it on a network, set the bind
address and the public URL clients should use for download links:
NORA_HOST=0.0.0.0 NORA_PUBLIC_URL=https://registry.example.com ./nora
Kubernetes (Helm)
helm repo add nora https://getnora-io.github.io/helm-charts
helm install nora nora/nora
From Source
cargo install nora-registry
nora
Usage
# Docker
docker tag myapp:latest localhost:4000/myapp:latest
docker push localhost:4000/myapp:latest
# npm
npm config set registry http://localhost:4000/npm/
npm publish
# Go
GOPROXY=http://localhost:4000/go go get golang.org/x/text@latest
See full documentation for all registries.
Features
- Web UI — dashboard with search, browse, i18n (EN/RU)
- Proxy & Cache — transparent proxy to upstream registries with local cache
- Curation — blocklist, allowlist, namespace isolation, integrity verification, min-release-age filter, digest quarantine
- Token RBAC — read/write/admin roles, expiry tracking, deferred last_used flush
- Mirror CLI — offline sync for air-gapped environments (
nora mirror) - Backup & Restore —
nora backup/nora restore - S3 Storage — AWS S3, Ceph RGW, any S3-compatible backend
- Prometheus Metrics —
/metricsendpoint, Grafana dashboard - Rate Limiting — configurable per-endpoint rate limits
Configuration
NORA works out of the box. For advanced setup — auth, S3, retention, curation — see getnora.dev/configuration.
# Auth
docker run -d -p 4000:4000 \
-v nora-data:/data \
-v ./users.htpasswd:/data/users.htpasswd \
-e NORA_AUTH_ENABLED=true \
getnora/nora:latest
# Curation — block packages younger than 7 days
docker run -d -p 4000:4000 \
-v nora-data:/data \
-e NORA_CURATION_MODE=enforce \
-e NORA_CURATION_MIN_RELEASE_AGE=7d \
-e NORA_CURATION_ALLOWLIST_PATH=/data/allowlist.json \
getnora/nora:latest
Performance
| Metric | NORA | Nexus | JFrog |
|---|---|---|---|
| Startup | < 3s | 30-60s | 30-60s |
| Memory | < 50 MB idle | 2-4 GB | 2-4 GB |
| Binary | < 30 MB | 600+ MB | 1+ GB |
Roadmap
Mirror CLI✅ v0.4.0Garbage Collection & Retention✅ v0.6.0Helm Chart✅ v0.6.1Signed releases & SBOM✅ v0.6.4Curation layer & 13 registry formats✅ v0.7.0Min Release Age✅ v0.7.1Hash Pin Store, auth rate limiting, Cache-Control✅ v0.8.0Outbound proxy, structured audit log✅ v0.8.3Circuit breaker, OIDC, hot reload, arm64, streaming uploads✅ v0.9.0NuGet V3 stabilization, Cargo ETag, 1049 tests✅ v0.9.1Prometheus metrics, Ansible Galaxy v3, security fixes, 1086 tests✅ v0.9.2Security hardening, null byte protection, config refactor, 1204 tests✅ v0.9.3Multi-upstream PyPI, conditional-request revalidation, single-flight coalescing, per-registry metrics✅ v0.9.4Digest quarantine across all registries, trusted upstream dates, token access-control hardening✅ v0.9.5- Image Signing Policy — cosign verification on upstream pulls
- Semver contract — stable API, configuration format, and storage layout
See ROADMAP.md for the full roadmap and CHANGELOG.md for release history.
Security & Trust
See SECURITY.md for vulnerability reporting.
Documentation
Full documentation: https://getnora.dev
Author
Created and maintained by Pavel Volkov
Contributing
NORA welcomes contributions! See CONTRIBUTING.md for guidelines.
License
MIT License — see LICENSE
Copyright (c) 2026 The NORA Authors