Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/holger

.nornir/registry-design.md

The plugin registry — design

Class: design · Function: registry (uses nothing; used by serve, embed, proxy). The whole-system view is design.md; the why is registry-reasoning.md.

The contract — RepositoryBackendTrait

Defined once in traits/src/lib.rs; every backend implements it. The engine holds backends only as Arc<dyn RepositoryBackendTrait> and dispatches through the vtable — never a concrete-type match on the hot path.

method purpose
name() -> &str configured repo name — the routing key
format() -> ArtifactFormat which ecosystem (Rust, Maven3, Docker, …)
is_writable() -> bool znippy-* = read-only; file-* = writable
fetch(id) -> Option<Vec<u8>> resolve an ArtifactId to bytes (direct / gRPC)
put(id, data) store bytes (writable backends only)
handle_http2_request(suburl, body) -> (status, headers, bytes) serve the native HTTP registry protocol
fetch_many_with_upstreams(upstreams, ids) batched fetch + upstream fall-through (default impl)

Two adjacent contracts complete the picture (traits/src/lib.rs): ConnectorTrait (the agent side — see agent-design) and HolgerObject (a transport-agnostic fetch/put/list/health facade with LocalHolger and RemoteHolger impls). ArtifactFormat::znippy_type_id() pins each format to znippy's DenseUnion discriminant; traits/src/lib.rs tests that the numbers never drift from znippy (the single source of truth).

The config graph & wiring

Config is a directed graph in RON (holger.ron): Repository nodes reference StorageEndpoint and ExposedEndpoint nodes by name. wire_holger() (server/lib/src/lib.rs) resolves it in passes:

  1. index every node by name into lookup maps;
  2. instantiate_backends() — for each repo, backend_from_config() (server/lib/src/repository.rs) matches ron_repo_type → a concrete backend struct, boxed as Arc<dyn RepositoryBackendTrait>. This match is the registry — the one place that knows the type→backend mapping;
  3. wire each repo's IO to its storage + exposed endpoints;
  4. reverse-link each exposed endpoint to the repos that write to it;
  5. build a FastRoutes table per exposed endpoint.

FastRoutes (server/lib/src/exposed/fast_routes.rs) is the dispatch table: routes are bucketed by the first byte of the repo name into two [usize; 256] arrays, so lookup(name) is O(1) and lock-free on the hot path. The same FastRoutes is shared by the gRPC service and LocalHolger.

The backend families

All backends share the trait and the storage floor; the only per-family code is the ArtifactId→path mapping and the protocol framing.

A · real registry protocol — handle_http2_request synthesizes the index on demand:

backend protocol archive path
znippy-python PEP 503 /simple/ HTML packages/{norm}/{file}
znippy-npm packument JSON + tarball {pkg}/-/{pkg}-{ver}.tgz
znippy-nuget NuGet V3 service + flat-container {id}/{ver}/{id}.{ver}.nupkg
znippy-go GOPROXY @v/{list,.info,.mod,.zip} {module}/@v/{ver}.zip
znippy-docker OCI Registry V2 manifests + blobs v2/{name}/{manifests|blobs}/{ref}
znippy-helm index.yaml (pre-baked or generated from Chart.yaml) {chart}-{ver}.tgz
znippy-composer Composer v2 p2/{vendor}/{pkg}.json dist/{vendor}/{pkg}/{ref}.zip
znippy-rust / file-rust Cargo sparse index + config.json {crate}-{ver}.crate
znippy-gem RubyGems compact-index gems/{name}-{ver}.gem

B · path passthrough — the client knows the layout; holger serves bytes verbatim with content-type sniffing: znippy-{maven, deb, rpm, conda}.

C · native — znippy-package serves znippy's own {name}-{ver}.znippy (GET / lists packages).

file-rust (Cargo sparse) and file-oci (OCI v2 CAS) are the only writable backends; every znippy-* backend is read-only (put() errors) by construction.

Per-backend status

One crate per ecosystem under server/repository/*, all behind the trait. Real protocol today: python, npm, nuget, go, docker, helm, composer, rust, gem (RubyGems compact-index), conda (real generated repodata.json). Path-passthrough: maven, deb, rpm. Writable: file-rust, file-oci. Depth and the exact route tables per backend are enumerated in design.md §6's status table (history) — this file states the current shape.