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

nordisk/nornir

.nornir/assets/server-workspace-flow.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1320 720" font-family="'DejaVu Sans', 'Segoe UI', sans-serif">
  <defs>
    <linearGradient id="board" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#0b1c2e"/><stop offset="1" stop-color="#06111d"/>
    </linearGradient>
    <linearGradient id="chip" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#14384f"/><stop offset="1" stop-color="#0e2a3e"/>
    </linearGradient>
    <linearGradient id="rpcchip" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#3a2a14"/><stop offset="1" stop-color="#2a1e0c"/>
    </linearGradient>
    <linearGradient id="king" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#2d2410"/><stop offset="1" stop-color="#1c1607"/>
    </linearGradient>
    <linearGradient id="store" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#17402c"/><stop offset="1" stop-color="#0c2418"/>
    </linearGradient>
    <marker id="arrow" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto">
      <path d="M0,0 L7,3 L0,6 Z" fill="#5fb6d6"/>
    </marker>
  </defs>

  <!-- PCB substrate -->
  <rect x="0" y="0" width="1320" height="720" fill="url(#board)"/>
  <g stroke="#143a52" stroke-width="1"><line x1="0" y1="64" x2="1320" y2="64"/><line x1="0" y1="690" x2="1320" y2="690"/></g>
  <g fill="#16415c"><circle cx="40" cy="40" r="2"/><circle cx="1280" cy="40" r="2"/><circle cx="40" cy="700" r="2"/><circle cx="1280" cy="700" r="2"/></g>

  <text x="40" y="40" fill="#cfe6f2" font-size="22" font-weight="bold">nornir-server — how the server learns a workspace</text>
  <text x="40" y="56" fill="#5f8aa3" font-size="12">redb registry is the source of truth · descriptor = git ssh URL or local path · empty registry is a valid resting state</text>

  <!-- ===== TOP ROW: register → server → resolve descriptor ===== -->
  <!-- Operator -->
  <rect x="40" y="110" width="220" height="96" rx="8" fill="url(#chip)" stroke="#2c6e8f" stroke-width="1.5"/>
  <text x="150" y="138" fill="#dff0f8" font-size="14" font-weight="bold" text-anchor="middle">Operator (CLI / viz)</text>
  <text x="150" y="160" fill="#9fc4d6" font-size="11.5" text-anchor="middle">nornir workspace register</text>
  <text x="150" y="178" fill="#9fc4d6" font-size="11.5" text-anchor="middle">&lt;name&gt; --descriptor X</text>
  <text x="150" y="196" fill="#e0a85a" font-size="11" text-anchor="middle">X = git ssh URL | local path</text>

  <!-- gRPC: Workspaces.Register -->
  <rect x="320" y="110" width="210" height="96" rx="8" fill="url(#rpcchip)" stroke="#9a7330" stroke-width="1.5"/>
  <text x="425" y="138" fill="#f3dca6" font-size="14" font-weight="bold" text-anchor="middle">nornir-server</text>
  <text x="425" y="160" fill="#e6c88a" font-size="11.5" text-anchor="middle">Workspaces.Register (gRPC)</text>
  <text x="425" y="180" fill="#bfa874" font-size="11" text-anchor="middle">authenticated port</text>
  <text x="425" y="196" fill="#bfa874" font-size="11" text-anchor="middle">--addr + --token only</text>

  <!-- Resolve descriptor -->
  <rect x="590" y="96" width="240" height="124" rx="8" fill="url(#chip)" stroke="#2c6e8f" stroke-width="1.5"/>
  <text x="710" y="122" fill="#dff0f8" font-size="14" font-weight="bold" text-anchor="middle">resolve descriptor</text>
  <text x="710" y="146" fill="#9fc4d6" font-size="11.5" text-anchor="middle">git URL → clone the workspace</text>
  <text x="710" y="162" fill="#9fc4d6" font-size="11.5" text-anchor="middle">repo (gix + russh)</text>
  <text x="710" y="184" fill="#9fc4d6" font-size="11.5" text-anchor="middle">local path → read its dir /</text>
  <text x="710" y="200" fill="#9fc4d6" font-size="11.5" text-anchor="middle">nornir-workspace.toml</text>

  <!-- ===== CENTER: registry.redb (the KING) ===== -->
  <rect x="430" y="290" width="460" height="130" rx="10" fill="url(#king)" stroke="#d9b25a" stroke-width="2.5"/>
  <text x="660" y="320" fill="#f6e3b0" font-size="17" font-weight="bold" text-anchor="middle">👑 registry.redb — the source of truth</text>
  <text x="660" y="344" fill="#e8d6a6" font-size="12" text-anchor="middle">&lt;home&gt;/.nornir/workspaces/registry.redb · single-writer (the server owns the lock)</text>
  <text x="660" y="368" fill="#cdbf95" font-size="12" text-anchor="middle">row: name · mode · poll · members (git URLs) · descriptor_content (the raw TOML)</text>
  <text x="660" y="390" fill="#cdbf95" font-size="12" text-anchor="middle">no descriptors/*.toml sibling file · idempotent upsert (re-register = update)</text>
  <text x="660" y="410" fill="#9bd6a0" font-size="11.5" text-anchor="middle">wipe it → 0 workspaces, a valid resting state · re-add re-populates from the row's git URLs</text>

  <!-- ===== BOTTOM ROW: poll/build → republish → warehouse → viz ===== -->
  <!-- poll + clone members -->
  <rect x="40" y="500" width="250" height="110" rx="8" fill="url(#chip)" stroke="#2c6e8f" stroke-width="1.5"/>
  <text x="165" y="528" fill="#dff0f8" font-size="14" font-weight="bold" text-anchor="middle">eager build + poll loop (60s)</text>
  <text x="165" y="552" fill="#9fc4d6" font-size="11.5" text-anchor="middle">ls-remote → on HEAD change,</text>
  <text x="165" y="568" fill="#9fc4d6" font-size="11.5" text-anchor="middle">clone/fetch each member</text>
  <text x="165" y="586" fill="#9fc4d6" font-size="11.5" text-anchor="middle">(gix + russh) → &lt;root&gt;/&lt;ws&gt;/git</text>

  <!-- republish -->
  <rect x="350" y="500" width="240" height="110" rx="8" fill="url(#chip)" stroke="#2c6e8f" stroke-width="1.5"/>
  <text x="470" y="528" fill="#dff0f8" font-size="14" font-weight="bold" text-anchor="middle">republish hook</text>
  <text x="470" y="552" fill="#9fc4d6" font-size="11.5" text-anchor="middle">knowledge scan (syn) +</text>
  <text x="470" y="568" fill="#9fc4d6" font-size="11.5" text-anchor="middle">code index + snapshot</text>
  <text x="470" y="586" fill="#9fc4d6" font-size="11.5" text-anchor="middle">(deep_scan from the row)</text>

  <!-- warehouse -->
  <rect x="650" y="500" width="240" height="110" rx="8" fill="url(#store)" stroke="#3a8a5c" stroke-width="1.5"/>
  <text x="770" y="528" fill="#d8f0e0" font-size="14" font-weight="bold" text-anchor="middle">per-workspace warehouse</text>
  <text x="770" y="552" fill="#a6d4b6" font-size="11.5" text-anchor="middle">Apache Iceberg over skade</text>
  <text x="770" y="568" fill="#a6d4b6" font-size="11.5" text-anchor="middle">(redb catalog) — $HOME-derived</text>
  <text x="770" y="586" fill="#a6d4b6" font-size="11.5" text-anchor="middle">&lt;root&gt;/&lt;ws&gt;/builds/warehouse</text>

  <!-- viz / mcp -->
  <rect x="950" y="500" width="240" height="110" rx="8" fill="url(#rpcchip)" stroke="#9a7330" stroke-width="1.5"/>
  <text x="1070" y="528" fill="#f3dca6" font-size="14" font-weight="bold" text-anchor="middle">viz / MCP (thin)</text>
  <text x="1070" y="552" fill="#e6c88a" font-size="11.5" text-anchor="middle">read it back over gRPC</text>
  <text x="1070" y="568" fill="#bfa874" font-size="11.5" text-anchor="middle">Viz.* / Knowledge.* / Mimir.*</text>
  <text x="1070" y="586" fill="#bfa874" font-size="11.5" text-anchor="middle">(read-only, lock-tolerant)</text>

  <!-- ===== TRACES ===== -->
  <g stroke="#3f7e9c" stroke-width="2" fill="none" marker-end="url(#arrow)">
    <path d="M260,158 L316,158"/>                 <!-- operator → server -->
    <path d="M530,158 L586,158"/>                 <!-- server → resolve -->
    <path d="M710,220 L710,260 L660,260 L660,286"/> <!-- resolve → registry -->
    <path d="M460,206 L460,250 L640,250 L640,286"/> <!-- server upsert → registry -->
    <path d="M540,420 L165,420 L165,496"/>        <!-- registry → poll/build (members) -->
    <path d="M290,555 L346,555"/>                 <!-- poll → republish -->
    <path d="M590,555 L646,555"/>                 <!-- republish → warehouse -->
    <path d="M890,555 L946,555"/>                 <!-- warehouse → viz -->
  </g>
  <!-- poll loop self-arrow -->
  <g stroke="#5f8aa3" stroke-width="1.5" fill="none" stroke-dasharray="4 3" marker-end="url(#arrow)">
    <path d="M780,420 L780,470 L470,470 L470,496"/> <!-- registry → republish (descriptor/deep_scan) -->
  </g>
  <text x="980" y="445" fill="#6f9ab2" font-size="11">poll loop re-runs on each HEAD change → live updates over gRPC</text>
</svg>