Gunnar

nordisk/holger

.nornir/embed-guide.md

# Embed holger in your own process — guide

> **Class:** `guide` · **Function:** `embed`. Architecture: [`embed-design.md`](embed-design.md).
> You don't have to run holger as a server — link `holger-server-lib`, parse (or generate)
> a RON config, and drive it through the `Holger` functional API. No gRPC, no socket. This
> is exactly what nornir's `embed-holger` feature does.

## Generate the dev-pair config and wire it

```rust
use holger_server_lib::{read_ron_config, wire_holger, Holger};
use holger_server_lib::dev_pair_ron;   // re-exported from lib root
use std::path::Path;

// One writable cache mirror (/cache) + one rehearsal registry (/sparring),
// both backed by on-disk .znippy archives under `data_dir`.
let ron = dev_pair_ron(Path::new("/tmp/holger-dev"), "127.0.0.1:50051", "127.0.0.1:8080");
std::fs::write("/tmp/holger-dev/holger.ron", &ron)?;

let mut holger: Holger = read_ron_config("/tmp/holger-dev/holger.ron")?;
wire_holger(&mut holger)?;             // resolves the repo→endpoint→storage graph
```

## Drive it directly — no network

```rust
use holger_server_lib::ArtifactId;     // re-exported for in-process consumers

// names of the configured repos: ["crates-io", "cache", "sparring"]
for name in holger.list_repositories() { println!("repo: {name}"); }

// store + fetch bytes through a writable repo's backend
let id = ArtifactId { namespace: None, name: "serde".into(), version: "1.0.0".into() };
holger.put("sparring", &id, b"...crate bytes...")?;
let bytes = holger.fetch("sparring", &id)?;     // Some(...) — read-only repos error on put
```

## Enumerate a repo's contents (the viz-pane path)

`fetch`/`put` are by-id. To *list* what's in a repo, reach the backend directly — it's an
`Arc<dyn RepositoryBackendTrait>`, and the trait + entry types are re-exported so you need no
separate `holger-traits` dep:

```rust
use holger_server_lib::{RepositoryBackendTrait, ArtifactEntry};

for repo in &holger.repositories {
    if let Some(backend) = &repo.backend_repository {
        let entries: Vec<ArtifactEntry> = backend.list(None, 100)?;      // crates in the .znippy
        let files:   Vec<String>        = backend.archive_files(None)?;  // raw archive paths
        // ...
    }
}
```

> Writable-repo gotcha (fixed in `8c9e458`): `list`/`archive_files` now read via
> `open_reader()`, so a **writable** dev-mode repo (`/cache`, `/sparring`) enumerates its
> on-disk crates. If you fork an older holger, port that fix or your dev-pair will look empty.

## When to embed vs run a server

Embed when the consumer *is* the security boundary (a single process that wants a registry
without a socket) — the host owns auth/transport. Run `holger-server` when independent
clients over the network must reach it. The embedded path adds no egress of its own unless
the embedded config wires a writable+upstream repo (the dev-pair `/cache` deliberately does,
for the dev-mirror use case).