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

nordisk/holger

.nornir/search-design.md

Cross-repo search — design

Class: design · Function: search (uses registry for each backend's list() / fetch() / archive_files(), and the in-process Holger facade for the CLI adapter). The whole-system view is design.md; the parity gap this closes is feature-complete-gaps.md §8 (search / metadata / REST — previously all ❌, the cheapest high-value operability win) and artifactory-parity-design.md §H.

What this is

The cross-repo search answers the Artifactory/Nexus "search" table stakes: find an artifact across every configured repository by name, coordinate, checksum, or path. It ships as server/lib/src/search.rs — a pure function over the repo list it is handed:

search_repos(repos: &[(String, Arc<dyn RepositoryBackendTrait>)], query: &SearchQuery)
    -> SearchResults

No transport, no scheduler, no mutation. The only I/O is the backend calls it is given, so it is unit-testable with in-memory mocks and reused verbatim by all three surfaces (gRPC RPC, HTTP door, CLI verb).

The matching axes (AND-composed)

Axis Field Match Backed by
Name name case-insensitive substring on artifact name list()
Namespace / GAVC group namespace case-insensitive substring (a namespace-less artifact never matches) list()
Version version exact (versions are exact tokens) list()
Checksum checksum lowercase-hex sha256 of the content, case-insensitive fetch() + sha256
Path path case-insensitive substring on a raw archive path → PathHits archive_files()

name + namespace + version are the GAVC (GroupId·ArtifactId·Version) coordinate axes mapped onto holger's ArtifactId { namespace, name, version }. All supplied criteria are AND-ed. A bare query lists everything (bounded by limit); a path-only query yields only path hits.

The chosen design: one pure engine over list(), three thin surfaces

The gap doc names the shape exactly: "backends expose list_files(); a thin cross-repo search verb (gRPC + a read-only HTTP endpoint) is the cheapest high-value operability win." So:

Why checksum is the one expensive axis

ArtifactEntry (the list() row) carries no digest, so a content-checksum search must read the bytes and hash them. The engine only does this for candidates that already pass the cheap axes, and only when a checksum is supplied — so the common name/coordinate search never fetches a body. Searching by digest is genuinely useful (dedup / provenance lookup: "which coordinate holds these bytes?"), and it is honest — it verifies the real content, not a stored label.

What is deliberately NOT here (honest scope)

Testing (Law #4)