The nornir workspace — guide
Class:
guide· audience: a techie standing up or driving a nornir workspace. The what is inworkspace-design.md; the why inworkspace-reasoning.md; the cold-shell, every-keystroke version isworkspace-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:
- 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
- Write the descriptor —
workspace_<name>/nornir-workspace.toml, one[repos.<member>]per repo (seef1). - 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
- 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:
pathonly → a fat checkout that must already exist on disk (relative to the descriptor dir; absolute honored verbatim). Missing + nogit→ hard error.gitonly → a member the server (ornornir workspace fetch) clones for you.- both → a fat member with a remote fallback: use the local checkout when present, else clone from
gitintopath. - A repo may belong to more than one workspace (e.g.
znippyis a dependency member ofworkspace_holgerand the primary member ofworkspace_znippy); each workspace gets its own warehouse + MCP scope.
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
--descriptoris optional — omit it and stand in the workspace dir; nornir walks up from the cwd to findnornir-workspace.toml. Pass it explicitly for a path or a git URL from elsewhere.--lazyrecords the row only and leaves population to the poll loop (scripted / daemon flows). Re-runningregister/addany time is idempotent — it re-fetches changed members and rebuilds (the manual version of the poll loop).- Modes:
--monitored(server polls git),--external(fat external checkout,git/stays empty), or neither (pushed— a thin client sends its source).--monitoredand--externalare mutually exclusive.
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