<!-- nornir:class: design -->
# Add warehouse (SSH) → Populate — dwarves control over nornir's warehouse — design
> **Class:** `design` · **Function axis:** `f0` the whole (Add + Populate) · `f1` SSH
> validation + descriptor synth · `f2` the `Workspaces` gRPC client · `f3` the
> `AddWarehouse` form model · `f4` the selector-header UI wiring.
> **Strict:** states only what the feature IS. Sibling how-to: guide; zero-knowledge:
> for-idiots (to be rendered by the Loki bencher `nornir docs book`).
dwarves' warehouse header can now **add a warehouse from an SSH git remote and populate
it** — the exact affordance nornir's own menu has, mirrored into the dwarves cockpit.
## Architecture (amended doctrine)
The warehouse is **nornir's** (it owns the future/present/history). dwarves reaches it
**by gRPC to `nornir-server`, never directly to skade**. So "Add warehouse (SSH) →
Populate" is a **thin gRPC client** in dwarves driving nornir's existing `Workspaces`
RPCs — the UI lives in dwarves, the warehouse-WRITE (clone the SSH members, build the
Iceberg warehouse) happens in nornir. One-directional `dwarves → nornir`; **no crate
cycle** (dwarves never links the `nornir` crate — the `<guest>-embed` pattern, same as
`nornir-funnel-embed` / the Telemetry submit path).
**No new nornir RPC was needed** — nornir already exposes the full seam:
`Workspaces.Register{lazy=false}` (record + EAGER populate: clone members + build the
warehouse inline) and `Workspaces.Fetch{force}` (re-sync). dwarves consumes them.
## The function map (f0)
| part | lives in | what it is |
|---|---|---|
| `f1` SSH + descriptor | [`nornir-warehouse-embed/src/lib.rs`](../crates/nornir-warehouse-embed/src/lib.rs) | `validate_ssh_remote` (L6: accept `git@host:owner/repo.git` / `ssh://…`, REJECT HTTPS) · `repo_name_from_ssh` · `descriptor_for_ssh` (synthesize a one-member `nornir-workspace.toml`) |
| `f2` Workspaces client | same crate + [`proto/workspaces.proto`](../crates/nornir-warehouse-embed/proto/workspaces.proto) | trimmed `nornir.v1.Workspaces` subset (List/Register/Fetch, byte-identical field numbers) → `add_warehouse_by_ssh` (Register eager) · `populate_warehouse` (Fetch force) · `list_warehouses` |
| `f3` form model | same crate | `AddWarehouse` — the drivable `state_json`/`update_json` core (name + ssh_url → Add → Populate), all server I/O injected in tests |
| `f4` UI wiring | [`dwarves-gui/src/warehouse_select.rs`](../dwarves-gui/src/warehouse_select.rs) | `WarehouseSelector::with_add_source` grows an `➕ Add…` control; a successful register `commit_added` → folds the warehouse into the picker + selects it (tabs re-bind) |
## Why it is shaped this way
- **The warehouse-WRITE stays in nornir.** dwarves synthesizes a descriptor and ships
its CONTENT (`RegisterWorkspaceRequest.descriptor_content`); nornir writes it to its
own descriptors dir, clones the SSH member with the server's deploy key, and builds
the warehouse. dwarves holds no warehouse-write logic — it is the client (L5a).
- **SSH-only, enforced at the door (L6).** `validate_ssh_remote` rejects any
`http(s)://` remote and any URL without an `owner/repo` path; the Add button is
disabled until the URL validates, so an HTTPS remote can never reach the server.
- **Eager populate = one click.** Register with `lazy=false` clones + builds inline, so
the warehouse is populated NOW (not after a poll tick). This also fixes the funnel's
`not_served`: once the warehouse is populated, nornir serves it and the funnel loads a
real DAG.
- **Reuse, not a new crate (L5).** The client lives in the existing
`nornir-warehouse-embed` (already the dwarves→nornir warehouse embed), extended with a
second trimmed proto — not a fifth embed crate.
- **Drivable headless (L4).** `AddWarehouse` splits pure state (validation, status
transitions) from I/O; the robot cell drives Add→Populate against a MOCK `Workspaces`
server (no network) and asserts the server received a monitored, eager, **SSH-only**
registration + a forced fetch. RED-when-broken: an HTTPS URL is unaddable; a populated
warehouse must fold into the picker + become selected.
## Place in the constellation
The "active warehouse" spine (`217055c`) gains its onboarding door: a user with a bare
SSH remote can register + populate a warehouse from dwarves without touching nornir's
CLI — dwarves the cockpit, nornir the warehouse.
## Deferred
- **Live `list_warehouses` refresh on open.** The client fn exists; wiring the header to
pre-fill the picker from `Workspaces.List` on startup is a follow-up.
- **Multi-member descriptors.** The synthesizer ships ONE member (the SSH remote); a
multi-repo workspace descriptor (or pasting a full `nornir-workspace.toml`) is a later
refinement.
- **Pixel `watch` leg.** Driven headless over `state_json`; the visible egui_kittest
`watch` replay is the standing UI follow-up (as with the other dwarves panes).
- PDFs left for the Loki bencher's `nornir docs book` render.