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

nordisk/holger

.nornir/assets/ui_grpc_flow.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 660" 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="rail" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#173d57"/><stop offset="1" stop-color="#0c2438"/>
    </linearGradient>
    <linearGradient id="gate" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" stop-color="#3a2a14"/><stop offset="1" stop-color="#2a1d0c"/>
    </linearGradient>
    <marker id="arr" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0 0 L7 3 L0 6 Z" fill="#6fd3f5"/></marker>
    <marker id="arrd" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0 0 L7 3 L0 6 Z" fill="#7fb0cc"/></marker>
    <marker id="arrs" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto"><path d="M0 0 L7 3 L0 6 Z" fill="#e0b86a"/></marker>
  </defs>

  <rect x="0" y="0" width="1180" height="660" fill="url(#board)"/>
  <g stroke="#143a52" stroke-width="1"><line x1="0" y1="74" x2="1180" y2="74"/><line x1="0" y1="620" x2="1180" y2="620"/></g>
  <g fill="#16415c"><circle cx="40" cy="40" r="2"/><circle cx="1140" cy="40" r="2"/><circle cx="40" cy="640" r="2"/><circle cx="1140" cy="640" r="2"/></g>

  <text x="40" y="44" fill="#eaf6ff" font-size="22" font-weight="700">Building a UI client over gRPC</text>
  <text x="430" y="44" fill="#9fc9e0" font-size="14.5">tonic channel + auth interceptor &#8594; RepositoryService / ArchiveService &#8594; FastRoutes &#8594; backend &#8594; ZnippyReader</text>

  <!-- UI client (3 faces) -->
  <rect x="36" y="120" width="206" height="150" rx="10" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
  <text x="139" y="150" fill="#eaf6ff" font-size="15" font-weight="700" text-anchor="middle">UI client</text>
  <text x="139" y="174" fill="#9fc9e0" font-size="11.5" text-anchor="middle">egui panel</text>
  <text x="139" y="194" fill="#9fc9e0" font-size="11.5" text-anchor="middle">web (grpc-web)</text>
  <text x="139" y="214" fill="#9fc9e0" font-size="11.5" text-anchor="middle">CLI / SDK</text>
  <text x="139" y="244" fill="#86b3cc" font-size="10" text-anchor="middle">tab &#8594; RPC &#8594; backend</text>
  <text x="139" y="259" fill="#86b3cc" font-size="10" text-anchor="middle">render-from-rows</text>

  <!-- non-rust native path -->
  <rect x="36" y="470" width="206" height="92" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
  <text x="139" y="500" fill="#eaf6ff" font-size="13.5" font-weight="700" text-anchor="middle">native tool</text>
  <text x="139" y="520" fill="#9fc9e0" font-size="10.5" text-anchor="middle">cargo / pip / npm / docker</text>
  <text x="139" y="538" fill="#86b3cc" font-size="10" text-anchor="middle">HTTP/2 registry protocol</text>
  <text x="139" y="554" fill="#86b3cc" font-size="10" text-anchor="middle">&#8594; handle_http2_request</text>

  <!-- tonic channel + auth interceptor -->
  <rect x="300" y="120" width="214" height="150" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
  <text x="407" y="150" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">tonic Channel</text>
  <text x="407" y="171" fill="#9fc9e0" font-size="10.5" text-anchor="middle">Channel::from_shared(url)</text>
  <text x="407" y="188" fill="#9fc9e0" font-size="10.5" text-anchor="middle">.tls_config(&#8230;) &#183; .connect()</text>
  <rect x="318" y="202" width="178" height="56" rx="8" fill="url(#gate)" stroke="#e0b86a" stroke-width="2"/>
  <text x="407" y="223" fill="#ffe9c2" font-size="12" font-weight="700" text-anchor="middle">auth interceptor</text>
  <text x="407" y="240" fill="#e9cfa0" font-size="9.5" text-anchor="middle">Bearer &lt;token&gt; (OIDC)</text>
  <text x="407" y="252" fill="#e9cfa0" font-size="9.5" text-anchor="middle">or mTLS client cert &#8594; CN</text>

  <!-- holger services -->
  <rect x="572" y="108" width="226" height="96" rx="10" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
  <text x="685" y="136" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">RepositoryService</text>
  <text x="685" y="156" fill="#9fc9e0" font-size="10" text-anchor="middle">FetchArtifact &#183; ListArtifacts</text>
  <text x="685" y="172" fill="#9fc9e0" font-size="10" text-anchor="middle">PutArtifact &#183; StreamArtifact</text>
  <text x="685" y="192" fill="#86b3cc" font-size="9.5" text-anchor="middle">unary + server-stream (64&#8201;KB chunks)</text>

  <rect x="572" y="216" width="226" height="74" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
  <text x="685" y="242" fill="#eaf6ff" font-size="14" font-weight="700" text-anchor="middle">ArchiveService</text>
  <text x="685" y="261" fill="#9fc9e0" font-size="10" text-anchor="middle">ListArchiveFiles &#183; ArchiveInfo</text>
  <text x="685" y="279" fill="#86b3cc" font-size="9.5" text-anchor="middle">browse a znippy archive's tree</text>

  <rect x="572" y="302" width="226" height="62" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
  <text x="685" y="326" fill="#eaf6ff" font-size="14" font-weight="700" text-anchor="middle">AdminService</text>
  <text x="685" y="345" fill="#9fc9e0" font-size="10" text-anchor="middle">Health &#183; ListRepositories</text>

  <!-- FastRoutes -->
  <rect x="852" y="160" width="170" height="96" rx="10" fill="url(#chip)" stroke="#7fc8ea" stroke-width="2.5"/>
  <text x="937" y="190" fill="#eaf6ff" font-size="14.5" font-weight="700" text-anchor="middle">FastRoutes</text>
  <text x="937" y="210" fill="#9fc9e0" font-size="10.5" text-anchor="middle">lookup(repo_name)</text>
  <text x="937" y="228" fill="#86b3cc" font-size="9.5" text-anchor="middle">O(1) first-byte bucket</text>
  <text x="937" y="244" fill="#86b3cc" font-size="9.5" text-anchor="middle">&#8594; Arc&lt;dyn Backend&gt;</text>

  <!-- backend -->
  <rect x="852" y="300" width="170" height="86" rx="10" fill="url(#chip)" stroke="#5aa6cf" stroke-width="2"/>
  <text x="937" y="328" fill="#eaf6ff" font-size="14" font-weight="700" text-anchor="middle">backend</text>
  <text x="937" y="347" fill="#9fc9e0" font-size="10" text-anchor="middle">RepositoryBackendTrait</text>
  <text x="937" y="364" fill="#86b3cc" font-size="9.5" text-anchor="middle">fetch / put &#183; id &#8594; path</text>
  <text x="937" y="379" fill="#86b3cc" font-size="9.5" text-anchor="middle">is_writable() gate</text>

  <!-- storage rail -->
  <rect x="300" y="572" width="722" height="52" rx="10" fill="url(#rail)" stroke="#7fc8ea" stroke-width="2"/>
  <text x="326" y="597" fill="#dff1ff" font-size="15" font-weight="700">ZnippyReader / storage</text>
  <text x="540" y="596" fill="#bcd9ec" font-size="12">extract_file(path) &#8226; random access &#8226; no full decompress &#8226; blake3-verified</text>
  <text x="540" y="614" fill="#7fa8c4" font-size="10" font-family="'DejaVu Sans Mono', monospace">immutable znippy archive (read) &#183; {repo}/{path} tree (file-rust, writable)</text>

  <!-- ── arrows: request path (cyan) ── -->
  <g stroke="#6fd3f5" stroke-width="3" fill="none">
    <path d="M242 195 H298" marker-end="url(#arr)"/>
    <path d="M514 178 H540 V156 H570" marker-end="url(#arr)"/>
    <path d="M514 210 H548 V253 H570" marker-end="url(#arr)"/>
    <path d="M514 230 H560 V333 H570" marker-end="url(#arr)"/>
    <path d="M798 156 H826 V208 H850" marker-end="url(#arr)"/>
    <path d="M937 256 V298" marker-end="url(#arr)"/>
    <path d="M937 386 V572" marker-end="url(#arr)"/>
  </g>

  <!-- streaming path label (amber dash, RepositoryService.StreamArtifact) -->
  <path d="M852 330 H540 V366 H685 V206" stroke="#e0b86a" stroke-width="2.5" fill="none" stroke-dasharray="2 5"/>
  <text x="468" y="402" fill="#e0b86a" font-size="10.5">stream: chunk-by-chunk &#8594; progress bar</text>

  <!-- native-tool door into backend (bypasses tonic) -->
  <path d="M242 510 H826 V392" stroke="#5aa6cf" stroke-width="2" fill="none" stroke-dasharray="6 4" marker-end="url(#arr)"/>
  <text x="470" y="528" fill="#86b3cc" font-size="10.5">native path: HTTP/2 gateway &#8594; same backend (no tonic stubs)</text>

  <!-- return bytes (dashed back up to UI) -->
  <path d="M850 350 H812 V452 H407 V270" stroke="#7fb0cc" stroke-width="2.5" fill="none" stroke-dasharray="6 4" marker-end="url(#arrd)"/>
  <text x="430" y="446" fill="#86b3cc" font-size="11">bytes / rows returned &#8594; render in the UI</text>
</svg>