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

nordisk/holger

.nornir/consolidation-jera-embed-design.md

Consolidation — the jera job ledger + the facett-holger / holger-embed pane

Class: design. Companion guides: consolidation-jera-embed-guide.md (how to use it) and consolidation-jera-embed-for-idiots.md (plain language). Extends the constellation designs in workspace_nordisk/.nornir/ (consolidation-panels-maps-design.md §1–§2, wasm-ui-panels-design.md §3).

This lands three additive consolidation seams in holger — no working path removed, no behaviour changed for an unconfigured caller. holger already adopts skidbladnir (service/systemd); these close the remaining gaps the 2026-07-12 survey flagged.


1. The durable job ledger (jera) — every promotion is a tracked job

Gap closed: holger ran its real background work (promotions) with no durable ledger — a seal() happened and left no listable record of the operation. The constellation law (consolidation-panels-maps-design.md §1) is: route tracked work through jera::jobs, not ad-hoc spawning. jera is the one shared job ledger (a redb list of long-running ops, running → done|failed + timing), already used by nornir and korp. It is a bounded operational view — jera keeps jobs.redb to the most recent jera::jobs::CAP (= 2000) terminal jobs so a long-running server stays lean; holger's permanent promotion record remains its append-only audit log (holger-audit). The ledger answers "what did the server run recently?", the audit log answers "what was ever promoted?".

Design (chosen): the PromotionEngine gains an optional jera::jobs::JobStore.

Where <data> resolves. All five operational stores — audit log, job ledger, PROD store, SBOM store, property store — derive from one anchor: Holger::data_root(), the parent of the resolved audit.dir. Resolution is Holger::resolved_audit_dir():

audit.dir $HOLGER_DATA resolves to
absolute (/srv/audit) anything /srv/audit — explicit config always wins
relative (default holger-audit) set $HOLGER_DATA/holger-audit
relative unset CWD-relative (last resort)

$HOLGER_DATA is what the installer already establishes (~/.holger user scope, /home/holger/.holger system scope; holger-server install also sets WorkingDirectory=), so an installed server resolves identically with or without the env var. The env branch exists for the embedded case: holger running in-process inside dwarves or nornir inherits their CWD, and a purely CWD-relative default scattered a fresh audit log + job ledger into whatever directory the host started in. Policy is unit-tested (audit_dir_anchor_tests).

jera is a default-features = false dependency of holger-server-lib — the ledger only, none of jera's bollard-container / KVM-boot runners, so the airgap core stays lean.

Why not a new holger-local ledger? That would duplicate jera. The law is one crate per concern; holger depends on it.

Bench: holger.promote_ledger (bench-scenarios, feature promote-ledger) seals a burst of distinct artifacts through the real engine with the ledger attached and reports seals/sec + MB/s of the full hot path (blake3 + blob + ref + running → done row).

Test: promote::tests::{seal_records_a_done_job_in_the_ledger, refused_reseal_records_a_failed_job, seal_without_ledger_writes_no_job} — the ledger records done/failed, and the no-ledger path is untouched.


2. The shared pane — facett-holger component + holger-embed adapter (native, no wasm)

Gap closed: holger's registry-browser pane was welded into the app (and a duplicate lived in holger-demo/panel.rs), with no facett-holger component crate; and the one cross-app embed (nornir's in-process Holger tab, nornir/src/holger_embed.rs) was a bespoke direct linkage, not the reusable <guest>-embed shape. The constellation standard (consolidation-panels-maps-design.md §2, wasm-ui-panels-design.md §3) is two pieces:

  1. facett-<app> component crate — the pane's facett-Elm body, separable from the app shell.
  2. <guest>-embed adapter crate — fn panel(source) -> Box<dyn Panel>: links the component

    • a data source; a host drops the returned panel into a tab. Backend (2): native, no wasm.

2.1 facett-holger (the component — lives in facett/)

The holger registry & artifact browser as one facett_core::Facet: a repository roster over the selected repo's artifact listing, both via facett_table::Table. Backend-agnostic — fed a serde RegistryModel (repos / artifacts / selected_repo); no holger, network, or FS dep. RegistryBrowser::from_fixture() (sample data, browser-safe) and from_model(model) (live data). It is the reusable unit: holger-demo mounts it, and the embed adapter hosts it in a peer app. The interim holger-demo/panel.rs duplicate is deleted — holger-demo now consumes the crate.

2.2 holger-embed (the adapter — lives in holger/)

The reusable <guest>-embed generalization of nornir's bespoke read_registry:

facett_core::Facet (title/ui/state_json) is the constellation's current Panel contract; when the design's dedicated Panel trait lands, panel's return type upgrades with no call-site change. The guest's ui runs directly in the host's egui frame — full native perf, no serialize boundary, no wasm.

nornir stays working (additive): nornir's existing in-process Holger tab (nornir/src/holger_embed.rs) is left untouched. holger-embed is the clean template it (and dwarves' facett-brokkrview, korp's panes) migrate onto next — a follow-up in nornir's repo, not this change.

Test: holger-embed's read_model_enumerates_dev_pair_and_feeds_the_panel (the functional-API walk → a live panel opened on the published crate) + fixture_panel_reports_holger_state. The component ships one nornir-robotui scenario + testmatrix row (facett-holger/tests/robot.rs).


3. Relationship