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

nordisk/nornir

.nornir/cli-command-map.md

CLI command map — every nornir verb, its gRPC RPC, and its handler

The complete, code-derived map of the nornir CLI surface: every command and subcommand, whether it can run thin (gRPC to a nornir-server) or only fat (direct warehouse on disk), the exact Service.Rpc it calls in thin mode, and the server handler behind that RPC. This is the CLI↔gRPC↔handler half of the wiring architecture-wiring.md draws for the viz↔table half — together they are the textual form of the EPIC ARCH board.

Source code: src/bin/nornir.rs (the clap Cli/Cmd + every *Op subcommand enum + the *_remote thin paths), proto/nornir.proto (the gRPC services + RPCs), src/bin/nornir-server.rs (the tonic handlers). Companion: cli-client-modes.md (the mode rule), mode-matrix.md, mcp-tools.md (the MCP twin).

1. The thin / fat duality

nornir is one core library with several faces (CLI, nornir-server, nornir-mcp, nornir viz). The CLI itself runs in one of two modes per invocation:

The switch is purely the global flags (they work in any position and apply to every subcommand), each falling back to an env var:

Global flag Env fallback Effect
--server <url> $NORNIR_SERVER sets thin mode; a bare host:port gets http:// prepended
--token <tok> $NORNIR_SERVER_TOKEN, then <home>/.nornir/token bearer token for the server (tonic interceptor)
--workspace <name> $NORNIR_WORKSPACE, then cwd workspace_<name>/ picks the workspace; in thin mode it must be one the server serves

server_target() is just NORNIR_SERVER (the --server flag exports it in main before dispatch); each thin-capable verb checks it and, if set, returns through its *_remote gRPC path instead of the embedded path. Standing in a workspace_<name>/ directory auto-resolves the workspace with no flag.

1a. Version identity — nornir --version

Every nornir build carries one canonical identity line:

nornir v<CARGO_PKG_VERSION> (<git-short-sha>)

e.g. nornir v0.5.4 (9b1a9a8). --version / -V prints exactly this and exits immediately — clap short-circuits before any subcommand dispatch, so it never touches the warehouse, a server, or the MCP boot.

The string is defined once in the library and reused everywhere so the faces can never disagree:

surface value
nornir --version / -V nornir <VERSION_LINE> (clap version = nornir::VERSION_LINE)
Health.Ping.version (gRPC) nornir::VERSION_LINE
viz ℹ️ About panel client_version (CARGO_PKG_VERSION) + build_sha (the git hash)

2. Command ↔ gRPC ↔ handler table

Every CLI command and subcommand. Mode is both (has a *_remote thin path and a fat embedded path), fat-only (no server routing — runs locally / spawns cargo / writes local config), or thin-only (no embedded path). For thin-capable verbs the gRPC RPC column names the Service.Rpc; the handler is that service's tonic impl in src/bin/nornir-server.rs (e.g. Release.GateAll → the gate_all method on the Release service impl).

Command Mode — gRPC Service.Rpc (thin) Fat behavior (direct warehouse / local) What it does
commands fat-only recursive clap leaf paths (--json → CommandOutcome) list every CLI command (the CLI twin of MCP tools/list)
repos both — Repos.List lists nornir.toml repo keys list monitored repos (server's vs local config)
guard status both — Guard.Status guard::status over local paths report AI-agent guard-rail (chmod -w) state
guard apply both — Guard.Apply guard::apply chmod -w the forbidden paths
guard release both — Guard.Release guard::release chmod +w (release the guard)
guard verify fat-only (Guard.Verify exists for server self-check) guard::verify verify guard invariants
bench run fat-only (results submit via Bench.Submit) runs benches, query_bench_* + append run benches for a repo, persist bench_telemetry/bench_runs
bench history-show both — Bench.History bench_history.jsonl + query_bench_runs show bench history for a repo
release trace both — Release.Trace regression trace over bench_history trace a regression to a commit
release events both — Viz.ReleaseEvents query_release_events → release_events dump release-pipeline events
release impacted-crates fat-only dep graph + changed_since_last_release crates impacted by a change

| test run | both (results via Telemetry.SubmitTestResults) | nornir-testmatrix detect+run, append test_results | run a repo's tests, persist the matrix | | test all | both (submit per row) | run tests for all workspace repos | full matrix run | | test history | both — Viz.TestResults | query_test_results → test_results | show the test-result history/matrix | | test coverage | fat-only (AUT5; MCP twin test_coverage) | autonom surface − covered → surface_coverage | completeness-gate coverage report | | coverage (top-level) | fat-only | cargo-llvm-cov over a repo → coverage/coverage_fn tables (--boundary view) | source line/region coverage (maven-report style) | | docs init | both — Docs.Init | docs::init scaffolds .nornir/ | scaffold a repo's docs | | docs render | both — Docs.Render | docs::render (typst) | regenerate README/CHANGELOG | | docs check | both — Docs.Check | docs::check freshness | verify docs are current | | docs export | both — Docs.Export | docs::export (PDF/HTML/MD) | export docs in a format | | docs book | both — Docs.Book | docs::book whole-doc book | render the full documentation book | | docs history | both — Docs.History | docs_exports table scan | show doc-export history | | docs index | fat-only | builds docs BM25 index | (re)build the docs search index | | docs search | fat-only (server twin is Search.Query) | BM25 over docs index | search the docs | | introspect depgraph | both — Mimir.Svg | dep_graph → SVG | per-repo dep-graph SVG | | introspect symbols | both — Introspect.Symbols | DWARF symbol extraction | extract symbols from a binary | | introspect symbol-lookup | both — Introspect.SymbolLookup | symbol search | find symbols by name | | introspect defined-in | both — Introspect.DefinedIn | symbol → file/line | where a symbol is defined | | introspect callgraph | fat-only | DWARF/syn call graph | build the call graph | | introspect callers | both — Introspect.Callers | callers_of | who calls a symbol | | introspect callees | both — Introspect.Callees | callees_of | what a symbol calls | | introspect path-between | both — Introspect.PathBetween | path_between | call path between two symbols | | knowledge scan | fat-only (server scans live clones) | knowledge::scan_all → symbol_facts+call_edges+feature_gate_facts | scan source into knowledge facts | | knowledge query (lookup/defined-in/callers/callees/call-path) | both — Knowledge.SymbolLookup/DefinedIn/Callers/Callees/CallPath | query the symbol_facts/call_edges tables | query the knowledge graph | | watch <repo> | fat-only (feature inotify, Linux) | knowledge::watch::watch → debounced knowledge::delta::SymbolDelta::rescan → append_symbol_scan | live-tree knowledge map: kernel file events, not a timer poll. For a tree that changes WITHOUT git; a git-driven server keeps the monitor poll. Without the feature it reports so and exits. | | scip (ingest/…) | fat-only (feature scip) | ingest a rust-analyzer scip index → resolved defs+refs, SHA-keyed | RESOLVED knowledge map (IDE-grade, opt-in) | | map <repo> | fat-only | renders the repo's knowledge map | architecture/knowledge map for a repo | | arch generate <repo> | fat-only | arch::generate → historize architecture_wiring (MCP twin arch_graph) | generate the wiring graph (component·gRPC·core·table) and persist it | | arch svg <repo> | fat-only (MCP twin arch_svg) | arch::generate + to_svg, prints/persists | emit the skade-style wiring SVG | | arch history <repo> | fat-only | arch::warehouse::list_arch_wiring | list historized wiring rows (by git sha) | | funnel submit/idea | both — Funnel.SubmitIdea | funnel::Store append funnel_events | submit an idea into the funnel DAG | | funnel error | both — Funnel.SubmitIdea (error kind) | funnel::Store | record an error/blocker node | | funnel test | both — Funnel.SubmitIdea | funnel::Store | submit a test idea | | funnel plan / plan-from | both — Funnel.CreatePlan | funnel::Store | create a plan from idea(s) | | funnel node | both — Funnel.AddNode | funnel::Store | add a node to a plan | | funnel link | both — Funnel.Link | funnel::Store | link two nodes | | funnel status | both — Funnel.SetStatus | funnel::Store | set a node's status | | funnel next | both — Funnel.Next | funnel::next | what to work on next | | funnel show | both — Funnel.Show | funnel::Store dump | dump the funnel DAG | | funnel history | both — Funnel.History | funnel_events scan | funnel event history | | funnel decompose | fat-only | funnel::decompose | auto-decompose an idea into a plan | | funnel demo | fat-only | seeds demo data | funnel demo seeding | | funnel nuke | fat-only | clears funnel state | reset the funnel | | mimir deps-of | both — Mimir.DepsOf | deps_of over the dep graph | a repo's dependencies | | mimir dependents-of | both — Mimir.DependentsOf | dependents_of | a repo's dependents | | mimir affected | both — Mimir.AffectedByChange | affected_by_change | blast radius of a change | | mimir build-order | both — Mimir.BuildOrder | topo sort | dependency build order | | mimir dep-path | both — Mimir.DepPath | dep_path | path between two repos | | mimir external-users | both — Mimir.ExternalDepUsers | external_dep_users | who uses an external crate | | mimir overview | both — Mimir.RepoOverview | repo_overview | repo summary (deps/release/docs) | | (mimir svg / changed) | both — Mimir.Svg / Mimir.ChangedSinceLastRelease | dep_graph SVG / changed_since_last_release | dep-graph SVG / changed-set | | diagram timeline | both — Viz.Timeline | build_timeline → release_lineage+dep_graph_edges | release/dep timeline diagram | | diagram lane-summary | fat-only | timeline model | per-lane summary diagram | | diagram depgraph | fat-only | dep_graph_edges | dep-graph diagram | | diagram snapshot-edges | fat-only | dep snapshot edges | snapshot-edge diagram | | diagram gate-matrix | fat-only | gate results | gate-matrix diagram | | diagram release-versions | fat-only | release_lineage | release-versions diagram | | diagram bench-history | fat-only | bench_history | bench-history diagram | | diagram bench-compare | fat-only | bench_runs | bench-compare diagram | | bakeoff run | fat-only (submit via Telemetry.SubmitBakeoff) | runs models, append agent_model_runs | run a prompt across local models (H5) | | bakeoff leaderboard | both — Viz.BakeoffResults | query_agent_model_runs → agent_model_runs | model leaderboard | | warehouse import-jsonl | fat-only | append rows from JSONL | import legacy JSONL into the warehouse | | warehouse query | fat-only (read-only open; tolerates a locked live catalog via snapshot) | warehouse::sql::query → DataFusion over native iceberg-datafusion TableProviders | run SQL (SELECT … WHERE / JOIN / GROUP BY) over the warehouse tables; --format json | | warehouse bench-query | both — Warehouse.Tables + Warehouse.Scan | query_bench_runs → bench_runs | typed bench-run reader for a repo (Parquet → JSON) | | warehouse compact | fat-only (operator op; emits WAREHOUSE_COMPACT job) | maintenance::compact_all → replace snapshot | merge small data files → few large SORTED files per partition | | warehouse expire | fat-only (operator op; emits WAREHOUSE_EXPIRE job) | maintenance::expire_all → RemoveSnapshots + orphan GC | drop old snapshots + reclaim their unreferenced files | | index build | fat-only | builds tantivy code index | (re)build the code search index | | index search | both — Search.Query | BM25 over the index | full-text code search | | index stats | both — Index.Stats | index stats | code-index stats | | index snapshot | both — Index.Snapshot | snapshot the index | snapshot the index to the warehouse | | index restore | fat-only (IndexOp::Restore opens the Iceberg warehouse directly — the Index service has no Restore/Get RPC, only Stats/Snapshot/UploadSnapshot) | restore_from_iceberg → blobs back to disk | restore the index | | index upload | both — Index.UploadSnapshot (stream) | upload index blob | push an index snapshot to the server | | vector index | fat-only | embeds into embeddings | build semantic embeddings (feature vector) | | vector search | both — Vector.Search | semantic/hybrid query | search by meaning | | vector stats | fat-only | embedding stats | embedding-store stats | | vector doctor | fat-only | env/HW check | diagnose the vector backend | | vector bench | fat-only | benches embedding | embedding throughput bench | | vector setup-cuda | fat-only | sets up CUDA dir | provision a CUDA build dir | | vector setup-rocm | fat-only | pins a ROCm runtime set (+ ROCm libonnxruntime.so) into /opt/nornir/rocm | provision an AMD ROCm build dir | | robot run | fat-only | drives the viz headlessly (nornir-robotui) | robot-UI test driver (feature robot) | | workspace ls | both — Workspaces.List | local workspace registry | list workspaces (all served, multi-ws) | | workspace show <name> | both — Workspaces.Get | registry record | show a workspace's members | | workspace register | both — Workspaces.Register | record + eager fetch/build | register a workspace | | workspace add | both — Workspaces.Register (member add) | edit descriptor | add a member repo to a workspace | | workspace rm <name> | both — Workspaces.Remove | remove from registry | remove a workspace | | workspace fetch <name> | both — Workspaces.Fetch | clone members + rebuild warehouse | fetch a workspace's remotes now | | workspace populate <name> | both — Workspaces.Fetch (background) | instant clone, async build | populate (fast clone, deferred build) | | jobs | both — Viz.Jobs (server jobs.redb) | local warehouse jobs.redb (--kind/--running/--limit/--json) | the unified job ledger (release/bench/test/docs/coverage/container runs) | | container | fat-only | spawns podman/docker run … AS a tracked job (stdout/stderr+exit captured) | run a container as a tracked nornir job | | cache (run/…) | fat-only | runs the embedded holger proxy (/cache crates.io mirror + /sparring) as a tracked job | local registry cargo can build through | | airgap (pack/verify/unfold/…) | fat-only (MCP twins airgap_pack/airgap_verify/…) | skidbladnir airgap: pack→sign→verify→unfold→install in dep order | offline lake bundle pipeline (EPIC AIRGAP) | | root | fat-only | prints <home>/.nornir (derived from $HOME) | print the resolved nornir data root | | serve | fat-only (this is the server) | runs nornir-server (tonic) | start the gRPC server | | viz | thin-or-fat at launch | execs nornir-ui (egui) | launch the visualizer (21 panes) | | install add/update/remove | fat-only | writes systemd unit + user; remove ALWAYS erases the DATA and ALWAYS keeps the ssh deploy key + token (--wipe-identity is the one opt-in that destroys them; --purge/--keep-identity are deprecated no-ops) | install / refresh-binary / uninstall the server as a service | | key (gen/show/…) | fat-only | manages <home>/.nornir/token | manage the bearer token | | security (scan) | fat-only (server twin Mimir.SecurityScan) | security::scan → vuln_findings+sbom_components | SBOM + vuln + license scan |

The dedicated Viz.* data RPCs (Timeline, ReleaseEvents, TestResults, BakeoffResults, BenchTelemetry, Knowledge, TestMatrix, CloneEvents, Architecture, Jobs, Coverage, HolgerRegistry — see the service Viz in proto/nornir.proto from line 809, the authoritative list) are thin mirrors of the fat query fns — the same data the viz panes use (see architecture-wiring.md §2a). The CLI verbs above (release events, test history, bakeoff leaderboard, diagram timeline) ride those same RPCs, so CLI ≡ viz ≡ MCP over one server API.

3. Wiring map — status of the EPIC ARCH generator

The plan item for this is EPIC ARCH in nordisk_plan.md §"living architecture diagrams: UI buttons ↔ gRPC backends, wired (skade-style SVG)" (also tracked as AUT7 under EPIC AUTONOM). It specifies, across ARCH1–ARCH5:

Status in code: the generator EXISTS and ships today — the plan checkboxes ([ ]) are stale drift, not an accurate status. Concretely:

So: yes, a CLI command produces the wiring SVG today — nornir arch svg <repo>. It covers the CLI/viz↔gRPC↔core-fn↔warehouse-table boundary as a component/gRPC/core/table circuit board (the auto-generated twin of architecture-wiring.md §2). The human-authored doc is the acceptance fixture the generator must reproduce. (For reference, crate-level dep graphs come from a separate path — mimir svg / introspect depgraph / the dep_graph_svg MCP tool — and are not the same as this fn-level wiring board.)

The table in §2 above is the textual form of that map for the CLI surface: each row is one CLI↔gRPC↔handler edge, exactly the enumeration ARCH1 specifies for the UI surface but for the command line.

4. The laws this map reflects