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

nordisk/nornir

.nornir/workspace-guide.md

The nornir workspace — guide

Class: guide · audience: a techie standing up or driving a nornir workspace. The what is in workspace-design.md; the why in workspace-reasoning.md; the cold-shell, every-keystroke version is workspace-for-idiots.md. This file is the workflow.

f0 — zero to a populated workspace (the whole workflow)

Four moves take a set of repos to a monitored, warehouse-backed workspace:

  1. Scaffold the control plane. A workspace_<name>/ repo, sibling to your code checkouts, holding config + descriptor only (no code, no data):
mkdir -p workspace_<name>/release workspace_<name>/.nornir/logs
  1. Write the descriptor — workspace_<name>/nornir-workspace.toml, one [repos.<member>] per repo (see f1).
  2. Register it — eager by default (records + clones + builds the warehouse now):
cd workspace_<name>                       # so --descriptor can be discovered
nornir workspace add <name> --monitored --poll 60s
  1. Confirm it populated:
nornir workspace ls --status              # green = every member cloned + built
nornir workspace events <name>            # per-member clone outcomes, if not

After that, nornir viz (standing in the workspace, or thin against a server that has it) reads the warehouse; the poll loop re-fetches + rebuilds on every git HEAD change. nornir root prints where the data lives (<home>/.nornir/workspaces/<name>/).

f1 — writing the descriptor

nornir-workspace.toml names the members and gives each a source. One [repos.<member>] table per repo:

[workspace]
name = "<name>"
# deep_scan = true   # opt-in: republish also scans the FULL transitive dep closure

[repos.<primary>]
git = "https://codeberg.org/<org>/<primary>"     # public → HTTPS, no credential

[repos.nornir]
git = "git@codeberg.org:<org>/nornir.git"        # private → SSH deploy key
# branch = "main"                                # optional pinned ref

[repos.korp]
path = "../korp"                                 # a local (fat) checkout…
git  = "git@codeberg.org:<org>/korp.git"         # …auto-materialized from here if the path is absent

Rules of thumb:

f2 — registering (eager) vs recording (lazy)

register and its alias add both do the eager record → fetch → build inline. You see three phases and a ready warehouse:

nornir workspace register <name> --monitored --poll 60s
# ① registered <name> (N member(s))
# ② fetching   N member(s)…
# ③ building   warehouse…
# ✓ registered <name> — populated now (snapshot abc123…) at ~/.nornir/workspaces/<name>/builds

Thin mode. With NORNIR_SERVER set, register + fetch route over RPC and the server builds its own warehouse. A local descriptor's content is shipped to the server automatically (it can't read your path); a git-URL descriptor is passed through.

f3 — driving members: fetch, populate, events

# Clone/fetch every git member into git/ and update each member's sync state:
nornir workspace fetch <name>
#   holger           a1b2c3d4e5f6  (changed)
#   znippy           998877665544  (unchanged)
# ✓ fetched <name> — 1 of 2 member(s) changed

nornir workspace fetch <name> --force     # full rebuild even if nothing changed

# Clone + rebuild the warehouse now (server builds async in the background):
nornir workspace populate <name>

# Why didn't a member populate? Read the per-attempt clone/fetch outcomes:
nornir workspace events <name>            # human view
nornir workspace events <name> --json     # machine output

events reads the clone_events warehouse table (over Viz.CloneEvents in thin mode), one line per member attempt with the error chain and timestamp — the drill-down behind the one-word sync_state on the roster.

f4 — inspecting the roster and one workspace

nornir workspace ls                       # NAME · MODE · MEMBERS · LAST SYNCED
nornir workspace ls --status              # + POPULATE column (green / red / stale) + first failing member
nornir workspace ls --json                # uniform CommandOutcome for scripts/autopilot
nornir workspace show <name>              # one workspace's full record

--status rolls each member's latest clone outcome into a workspace verdict: green (all ok), red (≥1 error — names the failing member + its error), stale (never populated). The same projection backs the viz Nornir-tab roster, so CLI and UI read identically.

f5 — removing a workspace

nornir workspace rm <name>                # drop the registry row only (builds/ data stays)
nornir workspace rm <name> --purge        # also delete builds/ (warehouse + catalog.redb + jobs.redb)

--data is an alias for --purge. The name is validated as a single safe path segment before any deletion, and --purge will also clean up an orphaned builds/ dir left by an earlier registry-only rm.

f6 — SSH members: the one-time deploy key

Public HTTPS members clone with no credential. For SSH remotes (git@codeberg.org:…) a fat user generates a per-user key once, then registers its public half as a deploy key on each repo:

nornir key generate      # creates ~/.nornir/ssh (NOT ~/.ssh)
nornir key show          # paste this public key as a codeberg deploy key on each SSH member

The server got its key at nornir install time (/home/nornir/.ssh/id_ed25519) — the same mechanism, one per identity. Skip the key and a fat add of SSH members stops at ③ with "SSH-only members can't be cloned yet".

Server install vs runtime register

There is no install-time workspace flag. Install the service once (bind + auth only), then register workspaces against the running server — they persist in the registry and are re-served on the next boot:

sudo nornir install add --addr 0.0.0.0:7878          # serves 0 workspaces until you register
# then, with NORNIR_SERVER + token set:
nornir workspace register <name> --descriptor /abs/path/nornir-workspace.toml --monitored --poll 60s