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

nordisk/nornir

.nornir/architecture-wiring.md

nornir architecture wiring — the code-derived map

EPIC ARCH (nordisk plan). The complete, code-derived picture of how every nornir mechanism is wired: ONE core lib with many faces, the sibling crates + ecosystem peers, and — the heart of the ask — which warehouse table each of the 24 viz panes reads/writes and the exact flow path (fat = direct IcebergWarehouse open; thin = which Viz.* / Warehouse.* / … gRPC verb → server → table). Derived by reading src/viz/*, src/bin/nornir-server.rs, src/viz/model.rs, proto/nornir.proto, and cross-checked against warehouse-schema.md (the gold table↔write/read map).

This is the human-authored data model behind EPIC ARCH / AUT7. The long-term plan (ARCH2/ARCH3) is to auto-generate this from call_edges + the new fn→table access fact + the UI-Facet ∩ registry() enumeration, rendered skade-style. Until then, this doc is the source of truth and the spec the generator must reproduce.


0. Where this lived before (gap analysis)

What already existed in .nornir/:

The GAP this doc closes: there was no single per-pane → table(+RPC+flow) map for all 24 viz panes, and no one all-component wiring board that draws the constellation, the thin/fat/server flow, and the pane↔table flow together. viz_thin_rpc.svg showed the mechanism (mode resolve → a token-bearing verb → Arrow batch → table) for one generic pane; warehouse-schema.md listed read-sites in prose. Neither answered "open the Test tab — what does it read, by what path?" for every pane at once. That table + the new architecture_wiring.svg are the new artifacts.

No drift was found between the existing SVGs and code; this doc synchronizes text + picture by embedding the relevant existing SVGs alongside the new per-pane board, so every claim here has a matching figure.


1. Component constellation — one core, many faces

nornir is one core library (nornir, src/lib.rs) with several faces, each a thin binary or surface over the same core:

Face Crate / bin Over the core it… Source
CLI nornir (src/bin/nornir.rs) runs every verb in-process, opening the warehouse direct (the fat path) bin/nornir.rs
gRPC server nornir-server (src/bin/nornir-server.rs) the intended single owner of the iceberg/redb lock; mirrors the CLI/MCP API one-for-one over tonic bin/nornir-server.rs
MCP nornir-mcp (src/bin/nornir-mcp.rs) exposes the core as 70 MCP tools (stdio) for LLM agents; writes mcp_requests telemetry bin/nornir-mcp.rs
viz nornir viz → execs nornir-ui (src/bin/urdr-threads.rs) the egui app; renders 24 panes, fat or thin (see §2) src/viz/*

Sibling crates (in this repo's crates/) and ecosystem peers:

Component Role How it connects
nornir-testmatrix the autonom test-matrix engine (Aspect enum, runners, parsers) feeds the 🧪 Test pane via test_results; the AUTONOM completeness gate
nornir-robotui richer live-drive of the viz (set_field/click_id/inject_event, AccessKit) drives the viz headlessly for robot tests (EPIC R)
skidbladnir (Skíðblaðnir) pack/move/unfold/install an air-gapped bundle (EPIC AIRGAP). Extracted out of crates/ into the sibling repo ../Skidbladnir (crate name skidbladnir, path dep in nornir's Cargo.toml — no crates/nornir-airgap any more) persists airgap_events to the warehouse; reuses the dep-graph topo sort; nornir's airgap CLI verb + airgap_* MCP tools drive it
skade / skade-katalog the Iceberg catalog + data-plane the warehouse runs on IcebergWarehouse IS Apache Iceberg over a single-file skade-katalog redb catalog; the warehouse ground rail. Reuses RedbCatalog::commit_table for schema evolution
znippy (+ znippy-zoomies) Arrow-IPC parallel archives + the GATLING no-barrier worker pool znippy-zoomies is a dep (GATLING + zero-copy law); artifact blobs + live transport are Arrow-IPC; holger serves znippy archives
facett the reusable egui component library the viz renders with every viz pane uses facett components + the facett theme (vendored as facett_theme/facett_ui pending egui-version align)
holger the artifact/file server — a first-class component, not a dep nornir monitors holger as a workspace repo (release/dep graph), and holger is nornir's deploy/registry peer: it serves crate/file artifacts (and every ecosystem's native protocol) out of immutable, blake3 content-addressed znippy archives. See §1a

1a. holger in the picture

Holger (/run/media/rickard/T9/git/holger, codeberg.org/nordisk/holger) is a secure, airgap-first artifact server. Its shape rhymes with nornir's:

How they relate: nornir monitors holger as a workspace repo (it appears in nordisk-constellation.svg as a monitored repo) — so holger gets a dep graph, release lineage, knowledge map, test matrix, etc. like every other nordisk repo. And holger is nornir's release target: where nornir release publishes crates / where skidbladnir (Skíðblaðnir, EPIC AIRGAP; sibling repo ../Skidbladnir) ships a bundle's artifacts to be served on the secure side.

The constellation figure (deps + orchestrator role over the monitored repos holger · skade · znippy · knut · facett · korp · njord):

Figure 1 — the constellation (EPIC J5). nornir orchestrates; holger/skade/znippy/facett are peers.


2. Per-UI-pane → warehouse-table flow (the heart)

The viz has 24 tabs (pub const ALL: [Tab; 24] in src/viz/app.rs — the authoritative header order: Nornir, Timeline, DepGraph, CallGraph, Architecture, Metro, Helix, Bloodstream, Funnel, TimeTravel, LiveRun, Release, Knowledge, Warehouse, WarehouseDeck, Mcp, Search, Gates, Bench, Test, Leaderboard, Security, Holger, Manual). The 16 numbered rows below are the original data-bearing panes; rows 17–21 are five chrome/overview panes (Nornir, Architecture, Metro, Holger, Manual). Three newer tabs — Helix, Bloodstream, and WarehouseDeck — are also in Tab::ALL (24 total) but are not yet broken out as rows here. Each pane is built local(...) (fat) or remote(endpoint, token, workspace) (thin) at construction (UrdrThreadsApp::new matching on Source::Local vs Source::Remote). Its load()/refresh()/hydrate() then either opens the warehouse direct or calls a gRPC verb. Every pane exposes what it renders in state_json()[key] (LAW #6).

Authoritative map — derived from each pane's loader in src/viz/* and the matching Viz.* impl in src/bin/nornir-server.rs (impl VizSvcTrait for VizSvc at line 2817) + proto/nornir.proto (the Viz service from line 809).

# Pane (state_json key) Warehouse table(s) Fat path (local query fn) Thin path (gRPC verb)
1 🧵 Timeline (timeline) release_lineage + dep_graph_edges build_timeline → query_dep_graph_snapshots + release_lineage scan (viz/model.rs) Viz.Timeline
2 🔗 DepGraph (dep_graph) dep_graph_edges from the Timeline model's DepGraphSnapshot.edges (depgraph_layout.rs) Viz.Timeline (dep snapshots ride the timeline)
3 🕸 CallGraph (callgraph) symbol_facts + call_edges IcebergWarehouse::scan_preview(table, 8000) (callgraph.rs) Warehouse.Scan (same two tables)
4 🗂 Funnel (funnel) funnel_events (+ release_lineage/dep_graph_edges for the auto-planner) funnel::Store::open / funnel::history / Store::record (write) (funnel_tab.rs) Funnel.Show / Funnel.History / Funnel.SubmitIdea
5 ⏳ TimeTravel (timetravel) release_lineage + dep_graph_edges (via the Timeline model + artifact pins) build_timeline (consumes the model; no direct table read in timetravel.rs) Viz.Timeline
6 📡 LiveRun (live) release_events (durable) + /tmp ndjson tail (live) query_release_events for the durable hydrate (live.rs) Viz.ReleaseEvents (hydrate) + Release.Progress (live stream)
7 🚀 Release (release) release_events query_release_events(&wh, EventSelector::All) (release_tab.rs) Viz.ReleaseEvents
8 🗺 Knowledge (knowledge) symbol_facts + call_edges (server scans the live clones, not the table) knowledge::scan_all over each member checkout (knowledge.rs) Viz.Knowledge (server runs scan_all over <root>/git/<member>)
9 🗄 Warehouse (warehouse) ALL tables (generic browser) IcebergWarehouse::table_names() + scan_preview(table, 500) (warehouse_tab.rs) Warehouse.Tables + Warehouse.Scan
10 📞 Mcp (mcp) mcp_requests scan_preview("mcp_requests", 50000) → per-tool aggregate (mcp_tab.rs) Warehouse.Scan
11 🔍 Search (search) tantivy code index + embeddings + symbol_facts (not a single Iceberg scan) server-only (no fat path; local shows a hint) Search.Query (BM25) · Vector.Search (semantic) · Index.Stats · Knowledge.SymbolLookup/Callers/Callees/CallPath
12 🚦 Gates (gates) none (gate logic + bench_history.jsonl) server-only Release.GateAll + Release.Trace
13 📈 Bench (bench) bench_telemetry + bench_runs (live panel) ; bench_history.jsonl (history) query_bench_telemetry(None) + query_bench_runs (bench_live.rs); history via JSONL Viz.BenchTelemetry (live) + Bench.History (history)
14 🧪 Test (test) test_results query_test_results(&wh, TestSelector::All) (test_tab.rs) Viz.TestResults
15 🏆 Leaderboard (leaderboard) agent_model_runs query_agent_model_runs(&wh, BakeoffSelector::All) (leaderboard_tab.rs) Viz.BakeoffResults
16 🛡 Security (security) vuln_findings + sbom_components crate::security::scan(<root>/<repo>) (live cargo metadata + OSV, warehouse-first cache) (security_tab.rs) Mimir.SecurityScan
17 🧬 Nornir (nornir) registry (redb) + the in-flight jobs tree (no single Iceberg table; an overview/roster pane) server registry roster + jobs aggregate (nornir_tab.rs / nornir_dashboard.rs) Health.Ping (server status) + the per-pane aggregates
18 🏛 Architecture (architecture) symbol_facts + call_edges + dep_graph_edges (the arch graph) crate::arch graph build over the live clones (arch_tab.rs) Viz.Architecture
19 🚇 Metro (metro) test_results (the button→gRPC→warehouse coverage chains) metro_feed::build_metro_map over the coverage facts (metro_tab.rs) Viz.Coverage
20 📦 Holger (holger) holger married-registry summary (remote holger warehouse, not a local table) remote::fetch_holger_registry (holger_tab.rs) Viz.HolgerRegistry
21 📖 Manual (manual) none — packed book-svg pages on disk ($HOME/.nornir/manual) local book-svg page loader (manual_tab.rs) — (GUI/local only)

2a. The contract the Viz.* RPCs honor

The five dedicated Viz.* data RPCs are thin mirrors of the fat query fns — same data, JSON-serialized — because the server holds the redb lock so a thin viz can't open the warehouse (src/bin/nornir-server.rs):

The write-back RPCs (so a fat CLI run lands in the locked server warehouse) live on the Telemetry service: SubmitReleaseEvents → release_events, SubmitTestResults → test_results, SubmitBakeoff → agent_model_runs, SubmitMcpCalls → mcp_requests. These honor the LAW "persist to warehouse, stream not /tmp".

2b. The pane↔table wiring board (NEW SVG)

Figure 2 — the 23-pane wiring board (this doc's primary new artifact; the SVG still renders the 16 data-bearing panes — regenerate to add the seven overview/chrome panes). Pane → gRPC verb → table → warehouse ground rail. THE pane↔table map.

2c. Known coverage gaps (from the code)


3. Thin / fat / server data flow (the mode example)

The pane↔table map above is parameterized by mode. The three modes (mode-matrix.md):

Figure 3 — fat / server-monitored / thin (EPIC J3b). The example the ask kept as one section.

The mode-resolution → token-bearing verb → Arrow batch → table mechanism for a single thin pane (the generic shape every "Thin path" row in §2 follows):

Figure 4 — the thin-RPC mechanism (EPIC J3b), one pane generalized.


4. The warehouse layers + the two flagship flows

The ground rail of every row in §2 is the IcebergWarehouse — Apache Iceberg (iceberg-rust) over a single-file skade-katalog redb catalog, append-only, so each append mints a snapshot (free time-travel). Its read-acceleration layers:

Figure 5 — warehouse layers (EPIC J3).

The two write-flows that feed the busiest panes:

Figure 6 — release_events DAG flow → 🚀 Release + 📡 LiveRun (EPIC J3).

Figure 7 — test-matrix pipeline → 🧪 Test (EPIC J3b).

Full per-table schema, write-sites and read-sites: warehouse-schema.md.


5. This is the data model behind EPIC ARCH / AUT7

The §2 table is the human-authored version of the graph EPIC ARCH/AUT7 will auto-generate:

When the generator lands, regenerating this doc's §2 table + Figure 2 from the warehouse must reproduce exactly what is hand-derived here — this doc is the acceptance fixture.

6. The generator has landed (ARCH1–5 / AUT7) — how to use it

The auto-generator now exists in code (src/arch/mod.rs + src/arch/warehouse.rs), so §5 is no longer forward-looking. The mapping from plan item to code:

plan item what code proving test
ARCH1 UI-pane ↔ gRPC verb map (Facet impls + *Svc/*SvcTrait handlers → Component / Service.Verb nodes) facet_impls_from_symbols, grpc_handlers_from_symbols (arch/mod.rs) generate_end_to_end_derives_both_maps, grpc_handlers_derived_from_symbols
ARCH2 coarsened owner-fn quotient + fn→table access fact build_graph/classify (arch/mod.rs) + access_scan::scan_source (warehouse/access_scan.rs) test_tab_fetch_collapses_to_component_and_table, self_edges_and_dupes_collapse_away, extracts_named_read_and_write_edges
ARCH3 skade circuit-board SVG, never mermaid (direct-emit, no engine, available in every feature set) ArchGraph::to_svg/render_pcb_svg (arch/mod.rs) svg_is_circuit_board_with_expected_labels (asserts no mermaid)
ARCH4 persist to warehouse architecture_wiring, historized by git sha, deduped on (repo,git_sha,graph_sha256) arch/warehouse.rs + iceberg_schema::architecture_wiring record_then_list_and_load_round_trip, dedup_skips_identical_graph, different_git_sha_makes_new_row
ARCH5 / AUT7 CLI + MCP parity nornir arch generate|svg|trace|history (bin/nornir.rs run_arch); arch_svg / arch_graph / arch_trace MCP tools (bin/nornir-mcp.rs) mcp_every_tool_exercised_and_asserted (coverage gate), trace_from_entrypoint_keeps_only_reachable

CLI usage (fat, in-process):

nornir arch generate <repo>        # scan → coarsen → render → historize (git-sha keyed)
nornir arch svg <repo> [--out f]   # print/write the wiring SVG without historizing extra rows
nornir arch trace <repo> <entry>   # focused sub-board reachable from an entrypoint (the flow trace)
nornir arch history <repo>         # list historized snapshots, newest first

MCP parity (LLM agents, reads the latest historized snapshot from the warehouse): arch_svg{repo} → the board SVG · arch_graph{repo} → the graph JSON · arch_trace{repo,entrypoint} → the reachable sub-board SVG (the visual twin of knowledge_call_path).

Naming note (deviation from the plan prose, by design): the plan sketched the trace surface as nornir flow <entrypoint> + a flow_trace MCP tool and a Typst render under nornir docs. The shipped surface folds the trace into the arch verb (nornir arch trace) and the arch_trace MCP tool — one cohesive arch namespace — and renders a direct-emit skade SVG (no Typst layout engine, so it works in every feature set). The skade-style / no-mermaid / owner-fn-quotient / fn→table / warehouse-historized intent is met verbatim; only the verb/tool names and the (Typst→)SVG mechanism differ.