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

nordisk/dwarves

.nornir/ui-embedding-and-standalone.md

dwarves — standalone service, cross-embedding UI, and the extraction from nornir

Decided 2026-07-11. Answers: how does dwarves run standalone, how do nornir and dwarves each show the other's panels without duplicating code (even thin code), and should the UI be WASM?

1. End state: pure-nornir + pure-dwarves, cross-embedding over thin gRPC

They will each want to see the other: nornir wants a CI/CD panel, dwarves wants warehouse panels. The rule is no duplicated code, even thin. Solution = wasm-component panels (see §2, the decided approach), with the facett component crates below as the enabling Elm seam + the interim native path:

Crates:

2. THE decision: UI panels are WASM COMPONENTS loaded by every host (revised 2026-07-11)

Constellation-wide design + phased plan: this section is the seed. The full design — trait-first: the Panel trait is primary, with four backends (native in-process; native cross-host via a thin adapter, which is the primary app↔app embedding path and uses NO wasm; a wasm Component only when the browser is the target; headless for tests) — plus the WIT panel world + base host imports for the browser backend, the whole-constellation pane inventory, and the phased rollout — lives in workspace_nordisk/.nornir/wasm-ui-panels-design.md. Cross-embedding (dwarves⇄nornir⇄holger) is native via that thin adapter + the thin/fat gRPC split in §1 below; wasm is the browser escape hatch, not the cross-embedding mechanism.

Rickard's call, and the right one for a polyrepo constellation: a UI panel is built ONCE as a wasm component, and EVERY host loads that same artifact — the native egui fat client (via a wasm runtime, e.g. wasmtime) AND a web page (browser wasm engine). The panel's code stays in its home repo (nornir's panels stay in nornir); dwarves loads nornir's panel wasm at runtime WITHOUT source-depending on nornir. Precedent: znippy already runs wasm that calls back into a native "base" (multithread support via host imports) — same boundary shape.

Why this beats build-time crate-linking here: nordisk is polyrepo with independent release cadences. Crate-linking (§1) forces dwarves to pin + rebuild on nornir's UI-crate version. A wasm ARTIFACT decouples it — deploy the panel, hosts load it, no cross-repo rebuild. One artifact, three hosts (native-nornir, native-dwarves, web), code in one place.

Enabling seam = facett-Elm. A panel wasm component:

Honest costs (decided eyes-open):

Rollout: (1) define the WIT panel world (exports+imports above); (2) build ONE pilot panel as a wasm component + a native wasmtime host loader and PROVE the paint-list perf; (3) migrate panels; (4) crate-linking stays the interim for our own native-only sharing until the wasm ABI is proven. HTML/JS is rejected — panels stay Rust→wasm (JS only as a binding).

3. Standalone dwarves service — reuse, do not copy nornir's server

dwarves must run standalone (systemd service + gRPC server + thin client + agent), the way holger (first) and nornir (more tested) already do. Get it by REUSE:

4. Extraction plan (Brokkr/Sindri + CI/CD panes → dwarves; remove from nornir)

  1. Move the brokkr and sindri crates out of nornir into the dwarves workspace (they were already published from nornir — this is a repo move + repoint, additive per polyrepo rules).
  2. Rip the CI/CD / release UI panes out of nornir/src/viz into a facett-cicd Elm component crate. nornir keeps embedding it (thin → dwarves-server) so the release tab still shows in nornir, but the code lives in dwarves.
  3. Standalone service for dwarves via Skidbladnir + the extracted edda service scaffold (§3).
  4. For now, remove dwarves/forge-specific build code from nornir. End state: pure-nornir (warehouse) + pure-dwarves (CI/CD), each embedding the other's panel as a shared facett component over thin gRPC.
  5. Both hosts compile their panels to WASM for web embedding (§2).

Sequencing note: do this AFTER the current fat release frees the repos; the brokkr/sindri move is a repo move + version-repoint, so it must not race an in-flight publish.