Commit Graph

102 Commits

Author SHA1 Message Date
Wes
b949ef49b8 fix: add Go and Raw registries to metrics detection and health endpoint (#97)
detect_registry() in metrics.rs was missing branches for /go/ and /raw/
paths, causing all requests to those registries to be labeled "other" in
Prometheus metrics. dashboard_metrics.rs already lists all seven
registries, so this was an oversight.

Also adds go and raw fields to RegistriesHealth so the /health endpoint
reports all seven registries consistently.

Fixes the test_detect_registry_go_path test which previously asserted
the wrong behavior ("other" instead of "go"), and adds tests for raw
path detection and prefix collision safety.

// ticktockbent

Co-authored-by: DevITWay | Pavel Volkov <devitway@gmail.com>
2026-04-05 23:09:23 +00:00
a0c9861cb1 feat: nora mirror v0.4 — yarn.lock, --json, Docker images (#94)
* feat: add yarn.lock support and --json output for nora mirror (#43, #44)

- Add yarn.lock v1 parser with scoped packages, multiple ranges, dedup
- Add --json flag for machine-readable mirror output (CI pipelines)
- Add MirrorFormat::Yarn variant
- MirrorResult now implements Serialize
- 12 new tests (10 yarn.lock + 2 json serialization)

Closes #43, closes #44

* feat: add Docker image mirroring (nora mirror docker) (#41)

Add nora mirror docker command to fetch images from upstream registries
(Docker Hub, ghcr.io, etc.) and push them into NORA. Supports:
- Image references: name:tag, name@digest, registry/name:tag
- --images (comma-separated) and --images-file (one per line)
- Manifest list resolution (picks linux/amd64)
- Layer dedup via HEAD check before fetch
- Monolithic blob upload to NORA

Makes fetch_blob_from_upstream and fetch_manifest_from_upstream pub in
registry/docker.rs for reuse from mirror module.

20 new unit tests for image ref parsing, manifest detection, blob extraction.
2026-04-05 23:05:11 +03:00
9d49f9e5e6 feat: add total_size_bytes to /health endpoint (#42) (#93)
Add StorageBackend::total_size() trait method with efficient implementations:
- LocalStorage: spawn_blocking dir walk (no async overhead)
- S3Storage: list + stat iteration

Health endpoint now returns storage.total_size_bytes in JSON response.

Closes #42
2026-04-05 19:48:39 +00:00
acd2736248 fix: unify env var naming, add Go env overrides (#39) (#90)
- Rename NORA_DOCKER_UPSTREAMS to NORA_DOCKER_PROXIES (backward compat preserved)
- Add NORA_GO_PROXY, NORA_GO_PROXY_AUTH, NORA_GO_PROXY_TIMEOUT, NORA_GO_PROXY_TIMEOUT_ZIP, NORA_GO_MAX_ZIP_SIZE env overrides
- Update warn_plaintext_credentials to mention NORA_DOCKER_PROXIES
- Add tests for new and deprecated env vars
2026-04-05 14:50:37 +03:00
e0bd8341bc quality: integration tests, 466 tests total, 43% coverage (#87) 2026-04-05 13:54:52 +03:00
ac3a8a7c43 quality: MSRV, tarpaulin config, proptest for parsers (#84)
* fix: proxy dedup, multi-registry GC, TOCTOU and credential hygiene

- Deduplicate proxy_fetch/proxy_fetch_text into generic proxy_fetch_core
  with response extractor closure (removes ~50 lines of copy-paste)
- GC now scans all registry prefixes, not just docker/
- Add tracing::warn to fire-and-forget cache writes in docker proxy
- Mark S3 credentials as skip_serializing to prevent accidental leaks
- Remove TOCTOU race in LocalStorage get/delete (redundant exists check)

* chore: clean up root directory structure

- Move Dockerfile.astra and Dockerfile.redos to deploy/ (niche builds
  should not clutter the project root)
- Harden .gitignore to exclude session files, working notes, and
  internal review scripts

* refactor(metrics): replace 13 atomic fields with CounterMap

Per-registry download/upload counters were 13 individual AtomicU64
fields, each duplicated across new(), with_persistence(), save(),
record_download(), record_upload(), and get_registry_* (6 touch points
per counter). Adding a new registry required changes in 6+ places.

Now uses CounterMap (HashMap<String, AtomicU64>) for per-registry
counters. Adding a new registry = one entry in REGISTRIES const.
Added Go registry to REGISTRIES, gaining go metrics for free.

* quality: add MSRV, tarpaulin config, proptest for parsers

- Set rust-version = 1.75 in workspace Cargo.toml (MSRV policy)
- Add tarpaulin.toml: llvm engine, fail-under=25, json+html output
- Add coverage/ to .gitignore
- Update CI to use tarpaulin.toml instead of inline flags
- Add proptest dev-dependency and property tests:
  - validation.rs: 16 tests (never-panics + invariants for all 4 validators)
  - pypi.rs: 5 tests (extract_filename never-panics + format assertions)

* test: add unit tests for 14 modules, coverage 21% → 30%

Add 149 new tests across auth, backup, gc, metrics, mirror parsers,
docker (manifest detection, session cleanup, metadata serde),
docker_auth (token cache), maven, npm, pypi (normalize, rewrite, extract),
raw (content-type guessing), request_id, and s3 (URI encoding).

Update tarpaulin.toml: raise fail-under to 30, exclude UI/main from
coverage reporting as they require integration tests.

* bench: add criterion benchmarks for validation and manifest parsing

Add parsing benchmark suite with 14 benchmarks covering:
- Storage key, Docker name, digest, and reference validation
- Docker manifest media type detection (v2, OCI index, minimal, invalid)

Run with: cargo bench --package nora-registry --bench parsing

* test: add 48 integration tests via tower oneshot

Add integration tests for all HTTP handlers:
- health (3), raw (7), cargo (4), maven (4), request_id (2)
- pypi (5), npm (5), docker (12), auth (6)

Create test_helpers.rs with TestContext pattern.
Add tower and http-body-util dev-dependencies.
Update tarpaulin fail-under 30 to 40.

Coverage: 29.5% to 43.3% (2089/4825 lines)

* fix: clean clippy warnings in tests, fix flaky audit test

Add #[allow(clippy::unwrap_used)] to 18 test modules.
Fix 3 additional clippy lints: writeln_empty_string, needless_update,
unnecessary_get_then_check.
Fix flaky audit test: replace single sleep(50ms) with retry loop (max 1s).
Prefix unused token variable with underscore.

cargo clippy --all-targets = 0 warnings (was 245 errors)
2026-04-05 10:01:50 +03:00
35a9e34a3e fix: proxy dedup, multi-registry GC, TOCTOU and credential hygiene (#83)
- Deduplicate proxy_fetch/proxy_fetch_text into generic proxy_fetch_core
  with response extractor closure (removes ~50 lines of copy-paste)
- GC now scans all registry prefixes, not just docker/
- Add tracing::warn to fire-and-forget cache writes in docker proxy
- Mark S3 credentials as skip_serializing to prevent accidental leaks
- Remove TOCTOU race in LocalStorage get/delete (redundant exists check)
2026-04-02 12:56:54 +00:00
be7e882391 fix(io): replace blocking I/O with async in hot paths
Three subsystems were using std::fs (blocking) inside async context,
which stalls the tokio runtime thread during I/O:

- DashboardMetrics::save(): now uses tokio::fs::write + rename
- TokenStore::flush_last_used(): now uses tokio::fs for batch updates
- AuditLog::log(): moved file write to spawn_blocking (fire-and-forget)

The background task and shutdown handler now properly .await the
async save/flush methods. AuditLog writer wrapped in Arc for
cross-thread access from spawn_blocking.
2026-04-02 15:27:23 +03:00
848f5f5571 refactor(docker): move upload sessions from global static to AppState
Upload sessions were stored in a global LazyLock<RwLock<HashMap>>,
making them impossible to test in isolation and invisible to other
parts of the system. Multi-instance deployments would also lose
sessions started on a different node.

Changes:
- Move upload_sessions into AppState as Arc<RwLock<HashMap>>
- Add State extractor to start_upload, patch_blob and their _ns wrappers
- Expire sessions in the existing 30s background task (alongside metrics)
- Make UploadSession and cleanup_expired_sessions pub for AppState access
2026-04-02 15:27:23 +03:00
0cd79e680f perf(tokens): add in-memory verification cache with 5min TTL
Token verification previously ran Argon2id + disk read on every
authenticated request. Under load this becomes the bottleneck
(~100ms per Argon2 verify on a single core).

Changes:
- Add in-memory cache (SHA256 -> user/role/expiry) with 5 minute TTL
- Defer last_used timestamp writes to batch flush every 30 seconds
- Invalidate cache entry on token revoke
- Background task flushes pending last_used alongside metrics persist

First verify_token call per token: full Argon2 + disk (unchanged).
Subsequent calls within TTL: HashMap lookup only (sub-microsecond).
2026-04-02 15:27:23 +03:00
7c8964f8fa fix(deps): update sha2 0.10→0.11, hmac 0.12→0.13 (#75)
Breaking API changes in digest crate ecosystem:
- sha2 digest returns Array instead of GenericArray
- Replace format!("{:x}", digest) with hex::encode(digest)
- Add digest::KeyInit trait import for Hmac
- Update all hash formatting in docker, npm, s3, tokens
2026-03-31 19:36:29 +00:00
06e9ba4262 fix: add config validation at startup (#73)
Validate configuration after loading and env overrides:
- Error (panic): port=0, empty storage path (local), empty S3 bucket
- Warning (log): rate limit values=0, body_limit=0

Prevents confusing runtime errors from invalid config.
12 new unit tests for all validation branches.
2026-03-31 21:46:08 +03:00
bb125db074 fix: code quality hardening — unwrap removal, unsafe forbid, Go/Raw tests (#72)
* fix: remove unwrap() from production code, improve error handling

- Replace unwrap() with proper error handling in npm, mirror, validation
- Add input validation to cargo registry (crate name + version)
- Improve expect() messages with descriptive context in metrics, rate_limit
- Remove unnecessary clone() in error.rs, docker.rs, npm.rs, dashboard_metrics
- Add #![deny(clippy::unwrap_used)] to prevent future unwrap in prod code
- Add let-else pattern for safer null checks in validation.rs

* docs: update SECURITY.md — add 0.3.x to supported versions

* security: forbid unsafe code at crate level

Add #![forbid(unsafe_code)] to both lib.rs and main.rs.
NORA has zero unsafe blocks — this prevents future additions
without removing the forbid attribute (stronger than deny).

* build: add rust-toolchain.toml, Dockerfile HEALTHCHECK

- Pin toolchain to stable with clippy + rustfmt components
- Add Docker HEALTHCHECK for standalone deployments (wget /health)

* test: add Go proxy and Raw registry integration tests

Go proxy tests: list, .info, .mod, @latest, path traversal, 404
Raw registry tests: upload/download, HEAD, 404, path traversal,
overwrite, delete, binary data (10KB)
2026-03-31 21:15:59 +03:00
14f41ec10c fix: add Raw to sidebar, update footer stats (32MB, 7 registries), fix badges (#64) 2026-03-27 23:08:48 +03:00
5d1c07db51 docs: add Go module proxy support to README (#62)
* docs: add Go module proxy to README, update dashboard GIF

- Add Go Modules to supported registries table
- Add Go usage example (GOPROXY)
- Add Go config.toml example
- Add /go/ endpoint to endpoints table
- Update dashboard GIF with 6 registry cards in one row
- Fix registries count: 6 package registries

* feat(ui): add Raw storage to dashboard, sidebar, and list pages

- Raw Storage card on dashboard with file count and size
- Raw in sidebar navigation with file icon
- Raw list and detail pages (/ui/raw)
- Raw mount point in mount points table
- Grid updated to 7 columns for all registry cards
- README: 7 registries, add Go module proxy docs

* docs: add product badges (release, image size, downloads)
2026-03-27 22:01:41 +03:00
c8dc141b2f feat: add Go module proxy (GOPROXY protocol) (#59)
* feat: add Go module proxy (GOPROXY protocol) (#47)

Implements caching proxy for Go modules with 5 standard endpoints:
- GET /go/{module}/@v/list — list versions
- GET /go/{module}/@v/{version}.info — version metadata
- GET /go/{module}/@v/{version}.mod — go.mod file
- GET /go/{module}/@v/{version}.zip — module zip
- GET /go/{module}/@latest — latest version info

Features:
- Module path encoding/decoding per Go spec (!x → X)
- Immutable caching (.info/.mod/.zip never overwritten)
- Mutable endpoints (@v/list, @latest) refreshed from upstream
- Configurable upstream (default: proxy.golang.org)
- Separate timeout for .zip downloads (default: 120s)
- Size limit for zips (default: 100MB)
- Path traversal protection
- Dashboard integration (stats, mount points, index)
- 25 unit tests (encoding, path splitting, safety, content-type)

Closes #47

* style: cargo fmt

* feat(ui): add Go pages, compact cards, fix icons

- Go in sidebar + list/detail pages with go get command
- Dashboard: fix fallback icon (was Docker whale for Go)
- Compact registry cards: lg:grid-cols-6, all 6 in one row
- Cargo icon: crate boxes instead of truck
- Go icon: stylized Go text (sidebar + dashboard)

* fix(go): URL-decode path + send encoded paths to upstream

Go client sends %21 for ! in module paths. Axum wildcard does not
auto-decode, so we percent-decode manually. Upstream proxy.golang.org
expects encoded paths (with !), not decoded uppercase.

Tested: full Pusk build (22 modules, 135MB cached) including
SherClockHolmes/webpush-go with triple uppercase encoding.

* style: cargo fmt
2026-03-27 21:16:00 +03:00
d909a62ac5 feat: upstream proxy retry + Maven proxy-only (#56)
* docs: add DCO, governance model, roles, vulnerability credit policy

* security: migrate token hashing from SHA256 to Argon2id

- Replace unsalted SHA256 with Argon2id (salted) for API token hashing
- Fix TOCTOU race: replace exists()+read() with read()+match on error
- Set chmod 600 on token files and 700 on token storage directory
- Auto-migrate legacy SHA256 tokens to Argon2id on first verification
- Add regression tests: argon2 format, legacy migration, file permissions

* feat: add retry with timeout for upstream proxy, mark Maven proxy-only

- Add shared proxy_fetch() and proxy_fetch_text() with 1 retry on 5xx/timeout
- Replace duplicated fetch_from_proxy in maven.rs, npm.rs, pypi.rs
- Mark Maven as proxy-only in README (no full repository manager support)
- Existing timeout config (30s maven/npm/pypi, 60s docker) preserved
- 4xx errors fail immediately without retry
2026-03-25 01:56:59 +03:00
432e8d35af security: migrate token hashing from SHA256 to Argon2id (#55)
* docs: add DCO, governance model, roles, vulnerability credit policy

* security: migrate token hashing from SHA256 to Argon2id

- Replace unsalted SHA256 with Argon2id (salted) for API token hashing
- Fix TOCTOU race: replace exists()+read() with read()+match on error
- Set chmod 600 on token files and 700 on token storage directory
- Auto-migrate legacy SHA256 tokens to Argon2id on first verification
- Add regression tests: argon2 format, legacy migration, file permissions
2026-03-24 22:56:43 +00:00
8fc741c8db feat: add anonymous read mode (NORA_AUTH_ANONYMOUS_READ)
When auth is enabled with anonymous_read=true, GET/HEAD requests
are allowed without credentials (pull/download), while write
operations (PUT/POST/DELETE/PATCH) still require authentication.

Use case: public demo registries, read-only mirrors.

Config: NORA_AUTH_ANONYMOUS_READ=true or auth.anonymous_read=true
2026-03-20 22:48:41 +00:00
9709471485 fix: address code review findings
- Pin slsa-github-generator and codeql-action by SHA (not tag)
- Replace anonymous tuple with GroupedActivity struct for readability
- Replace unwrap() with if-let for safety
- Add warning message on attestation failure instead of silent || true
- Fix clippy: map_or -> is_some_and
2026-03-20 22:14:16 +00:00
9f951ccc82 ui: fix table cell padding alignment
Add px-4 to all td cells in Mount Points and Activity tables
to match th header padding. Remove non-functional px-4 from
tbody elements (CSS padding does not apply to tbody).
2026-03-20 19:44:14 +00:00
ceb75e0ce3 ui: group consecutive identical activity entries
Repeated cache hits for the same artifact now show as
"artifact (x4)" instead of 4 identical rows.
Reduces visual noise in dashboard activity log.
2026-03-20 19:23:41 +00:00
35e930295c test: add 82 unit tests across 7 modules
Coverage targets:
- activity_log: ActionType display, ActivityLog push/recent/all/bounded
- audit: AuditEntry, AuditLog write/read with tempdir
- config: defaults for all sub-configs, env overrides, TOML parsing
- dashboard_metrics: record_download/upload, cache_hit_rate, persistence
- error: constructors, Display, IntoResponse for all variants
- metrics: detect_registry for all protocol paths
- repo_index: paginate, RegistryIndex basics, RepoIndex invalidate

Total tests: 103 -> 185
2026-03-20 10:08:49 +00:00
fa2cd45ed3 security: harden Docker registry and container runtime
- Verify blob digest (SHA256) on upload, reject mismatches (DIGEST_INVALID)
- Reject sha512 digests (only sha256 supported)
- Add upload session limits: max 100 concurrent, 2GB per session, 30min TTL
- Bind upload sessions to repository name (prevent session fixation)
- Filter .meta.json from Docker tag list (fix ArgoCD Image Updater recursion)
- Fix catalog to show namespaced images (library/alpine instead of library)
- Add security headers: CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy
- Run containers as non-root user (USER nora) in all 3 Dockerfiles
- Add configurable NORA_MAX_UPLOAD_SESSIONS and NORA_MAX_UPLOAD_SESSION_SIZE_MB
2026-03-19 08:29:28 +00:00
58f98949e4 style: clean up code comments 2026-03-18 11:23:11 +00:00
67b55fba92 chore: repo cleanup — remove dead crates from workspace, stale files, duplicate assets
- Remove nora-cli and nora-storage from workspace (stub crates, not used)
- Remove root install.sh (duplicate of dist/install.sh)
- Remove root logo.jpg (duplicate of ui/logo.jpg)
- Remove committed SBOM .cdx.json files (generated by CI in release)
- Remove stale .githooks/ (real hook is in .git/hooks/)
- Update version in docs-ru to 0.2.32
- Add *.cdx.json to .gitignore
2026-03-18 11:20:22 +00:00
9de623a14e fix: Docker dashboard for namespaced images, library/ auto-prepend for Hub official images (v0.2.32)
Docker dashboard:
- build_docker_index now finds manifests segment by position, not fixed index
- Correctly indexes library/alpine, grafana/grafana, and other namespaced images

Docker proxy:
- Auto-prepend library/ for single-segment names when upstream returns 404
- Applies to both manifests and blobs
- nginx, alpine, node now work without explicit library/ prefix
- Cached under original name for future local hits
2026-03-18 08:07:53 +00:00
cb37813f11 security: add cargo-fuzz targets and ClusterFuzzLite config
Fuzz targets:
- fuzz_validation: storage key, Docker name, digest, reference validators
- fuzz_docker_manifest: Docker/OCI manifest media type detection

Infrastructure:
- lib.rs exposing validation module and docker_fuzz for fuzz harnesses
- ClusterFuzzLite project config (libfuzzer + ASan)
2026-03-17 11:20:17 +00:00
c7e15b47aa docs: add CycloneDX SBOM (238 components, 0 vulnerabilities) 2026-03-16 13:29:59 +00:00
b7d303bf54 feat: nora mirror CLI + systemd + install script
nora mirror:
- Pre-fetch dependencies through NORA proxy cache
- npm: --lockfile (v1/v2/v3) and --packages with --all-versions
- pip: requirements.txt parser
- cargo: Cargo.lock parser
- maven: dependency:list output parser
- Concurrent downloads (--concurrency, default 8)
- Progress bar with indicatif
- Health check before start

dist/:
- nora.service — systemd unit with security hardening
- nora.env.example — environment configuration template
- install.sh — automated install (binary + user + systemd + config)

Tested: 103 tests pass, 0 clippy warnings, cargo audit clean.
Smoke: mirrored 70 npm packages from real lockfile in 5.4s.
2026-03-16 13:27:37 +00:00
01027888cb feat: npm full proxy — URL rewriting, scoped packages, publish, integrity cache (v0.2.31)
npm proxy:
- Rewrite tarball URLs in metadata to point to NORA (was broken — tarballs bypassed NORA)
- Scoped packages (@scope/package) full support in handler and repo index
- Metadata cache TTL (NORA_NPM_METADATA_TTL, default 300s) with stale-while-revalidate
- proxy_auth now wired into fetch_from_proxy (was configured but unused)

npm publish:
- PUT /npm/{package} — accepts standard npm publish payload
- Version immutability — 409 Conflict on duplicate version
- Tarball URL rewriting in published metadata

Security:
- SHA256 integrity verification on cached tarballs (immutable cache)
- Attachment filename validation (path traversal protection)
- Package name mismatch detection (URL vs payload)

Config:
- npm.metadata_ttl — configurable cache TTL (env: NORA_NPM_METADATA_TTL)
2026-03-16 12:32:16 +00:00
b2be7102fe style: cargo fmt 2026-03-16 08:58:27 +00:00
a9996b6b54 fix: dashboard — docker namespaced repos, npm proxy cache, upstream display (v0.2.30) 2026-03-16 08:55:33 +00:00
e4890b457b v0.2.29: upstream auth, remove dead code, version bump
- Remove unused DockerAuth::fetch_with_auth() method
- Fix basic_auth_header docstring
- Bump to v0.2.29
2026-03-15 21:42:49 +00:00
7345dfc7e7 refactor: extract basic_auth_header helper, add plaintext credential warnings
- basic_auth_header() in config.rs replaces 6 inline STANDARD.encode calls
- warn_plaintext_credentials() logs warning at startup if auth is in config.toml
- All protocol handlers use shared helper instead of duplicating base64 logic
2026-03-15 21:37:51 +00:00
e02e63a972 feat: upstream auth for all protocols (Docker, Maven, npm, PyPI)
Wire up basic auth credentials for upstream registry proxying:
- Docker: pass configured auth to Bearer token requests
- Maven: support url|auth format in NORA_MAVEN_PROXIES env var
- npm: add NORA_NPM_PROXY_AUTH env var
- PyPI: add NORA_PYPI_PROXY_AUTH env var
- Mask credentials in logs (never log plaintext passwords)

Config examples:
  NORA_DOCKER_UPSTREAMS="https://registry.corp.com|user:pass"
  NORA_MAVEN_PROXIES="https://nexus.corp.com/maven2|user:pass"
  NORA_NPM_PROXY_AUTH="user:pass"
  NORA_PYPI_PROXY_AUTH="user:pass"
2026-03-15 21:29:20 +00:00
582e8fbed1 fix: cargo fmt, add .gitleaks.toml allowlist for doc examples
- remove extra blank lines in openapi.rs and secrets/mod.rs
- allowlist commit 92155cf (curl -u admin:yourpassword in README)
2026-03-15 19:27:36 +00:00
233b83f902 security: make CI gates blocking, add smoke test, clean up dead code
- gitleaks, cargo audit, trivy fs now block pipeline on findings
- add smoke test (docker run + curl /health) in release workflow
- deny.toml: add review date to RUSTSEC-2025-0119 ignore
- remove unused validation functions (maven, npm, crate)
- replace blanket #![allow(dead_code)] with targeted allows
2026-03-15 19:25:00 +00:00
26d30b622d style: cargo fmt 2026-03-13 16:59:54 +00:00
61de6c6ddd fix: persist dashboard metrics and count versions instead of repos
Metrics (downloads, uploads, cache hits) were stored in-memory only
and reset to zero on every restart. Now they persist to metrics.json
in the storage directory with:
- Load on startup from {storage_path}/metrics.json
- Background save every 30 seconds
- Final save on graceful shutdown
- Atomic writes (tmp + rename) to prevent corruption

Artifact count on dashboard now shows total tags/versions across
all registries instead of just counting unique repository names.
This matches user expectations when pushing multiple tags to the
same image (e.g. myapp:v1, myapp:v2 now shows 2, not 1).
2026-03-13 15:43:03 +00:00
dependabot[bot]
95c6e403a8 chore(deps): bump bcrypt from 0.18.0 to 0.19.0
Bumps [bcrypt](https://github.com/Keats/rust-bcrypt) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/Keats/rust-bcrypt/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: bcrypt
  dependency-version: 0.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 04:25:38 +00:00
8278297b4a feat: configurable body limit + Docker delete API
- Add body_limit_mb to ServerConfig (default 2048MB, env NORA_BODY_LIMIT_MB)
- Replace hardcoded 100MB DefaultBodyLimit with config value
- Add DELETE /v2/{name}/manifests/{reference} endpoint (Docker Registry V2 spec)
- Add DELETE /v2/{name}/blobs/{digest} endpoint
- Add namespace-qualified variants for both DELETE endpoints
- Return 202 Accepted on success, 404 with MANIFEST_UNKNOWN/BLOB_UNKNOWN errors
- Audit log integration for delete operations

Fixes: 413 Payload Too Large on Docker push >100MB
2026-03-03 22:25:41 +00:00
8da4c4278a style: cargo fmt
DevITWay
2026-03-03 11:03:40 +00:00
07de85d4f8 fix: detect OCI manifest media type for Helm chart support
Distinguish OCI vs Docker manifests by checking config.mediaType
instead of assuming all schemaVersion 2 manifests are Docker.
Enables helm push/pull via OCI protocol.

DevITWay
2026-03-03 10:56:52 +00:00
402d2321ef feat: add RBAC (read/write/admin) and persistent audit log
- Add Role enum to tokens: Read, Write, Admin (default: Read)
- Enforce role-based access in auth middleware (read-only tokens blocked from PUT/POST/DELETE)
- Add role field to token create/list/verify API
- Add persistent audit log (append-only JSONL) for all registry operations
- Audit logging across all registries: docker, npm, maven, pypi, cargo, raw

DevITWay
2026-03-03 10:40:59 +00:00
f560e5f76b feat: add gc command and fix Docker-Content-Digest for Helm OCI
- Add nora gc --dry-run command for orphaned blob cleanup
- Fix Docker-Content-Digest header in blob upload response (enables Helm OCI push)
- Mark-and-sweep GC: list blobs, parse manifests, find/delete orphans

DevITWay
2026-03-03 10:28:39 +00:00
45c3e276dc Merge pull request #8 from getnora-io/dependabot/cargo/indicatif-0.18.4
chore(deps): bump indicatif from 0.17.11 to 0.18.4
2026-03-03 12:13:33 +03:00
dependabot[bot]
f4e53b85dd chore(deps): bump indicatif from 0.17.11 to 0.18.4
Bumps [indicatif](https://github.com/console-rs/indicatif) from 0.17.11 to 0.18.4.
- [Release notes](https://github.com/console-rs/indicatif/releases)
- [Commits](https://github.com/console-rs/indicatif/compare/0.17.11...0.18.4)

---
updated-dependencies:
- dependency-name: indicatif
  dependency-version: 0.18.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-03 09:13:21 +00:00
05d89d5153 Merge pull request #18 from getnora-io/dependabot/cargo/bcrypt-0.18.0
chore(deps): bump bcrypt from 0.17.1 to 0.18.0
2026-03-03 12:13:20 +03:00
7f8e3cfe68 fix(rate-limit): add NORA_RATE_LIMIT_ENABLED flag and SmartIpKeyExtractor
- Add enabled field to RateLimitConfig (default: true, env: NORA_RATE_LIMIT_ENABLED)
- Skip rate limiter layers entirely when disabled
- Replace PeerIpKeyExtractor with SmartIpKeyExtractor for upload/general routes
  to correctly identify clients behind reverse proxies and Docker bridge networks
- Keep PeerIpKeyExtractor for auth routes (stricter brute-force protection)

Root cause: PeerIpKeyExtractor saw all Docker bridge traffic as single IP (172.17.0.1),
exhausting GCRA bucket for all clients simultaneously. With burst=1M, recovery time
reached 84000+ seconds.
2026-03-03 08:51:33 +00:00