crates/jera/.nornir/assets/multimachine-result-flow.svg
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="500" viewBox="0 0 900 500" font-family="ui-sans-serif,system-ui,sans-serif">
<defs>
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#6fd3f5"/>
</marker>
<marker id="arrN" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#c9a24b"/>
</marker>
<marker id="arrP" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
<path d="M0 0 L10 5 L0 10 z" fill="#a98fe0"/>
</marker>
</defs>
<style>
.box{fill:#0f1720;stroke:#6fd3f5;stroke-width:1.5;rx:8}
.grp{fill:#0c1a12;stroke:#5fbf7f;stroke-width:1.4;rx:8}
.res{fill:#141020;stroke:#a98fe0;stroke-width:1.5;rx:8}
.led{fill:#1a1409;stroke:#c9a24b;stroke-width:1.4;rx:8}
.t{fill:#dfe9f0;font-size:13px}
.h{fill:#6fd3f5;font-size:13px;font-weight:700}
.hg{fill:#7fe0a0;font-size:13px;font-weight:700}
.hp{fill:#c3aef0;font-size:13px;font-weight:700}
.hl{fill:#d9b968;font-size:13px;font-weight:700}
.s{fill:#9fb3c0;font-size:11px}
.e{stroke:#6fd3f5;stroke-width:1.6;fill:none}
.en{stroke:#c9a24b;stroke-width:1.6;fill:none;stroke-dasharray:4 3}
.wire{stroke:#a98fe0;stroke-width:1.8;fill:none;stroke-dasharray:6 4}
</style>
<text x="20" y="26" class="h" font-size="15">jera::agent — full dispatch → run → result (stages 4–5 + wire auth)</text>
<text x="20" y="44" class="s">placement floors a heavy unit to Loki/Odin · a per-group gate serializes the fleet · the authed wire runs it · the result records against its node</text>
<!-- WorkUnit + ResourceReq / exclusive (stage 5) -->
<rect class="box" x="20" y="76" width="230" height="132"/>
<text x="32" y="100" class="h">WorkUnit</text>
<text x="32" y="120" class="t">payload · group · required_caps</text>
<rect class="res" x="32" y="130" width="206" height="66"/>
<text x="42" y="149" class="hp">ResourceReq (stage 5)</text>
<text x="42" y="167" class="s">min_cores · min_mem_mb · tags</text>
<text x="42" y="182" class="s">heavy() ≥ 8 cores · exclusive()</text>
<!-- Scheduler: placement + gate -->
<rect class="box" x="300" y="76" width="230" height="132"/>
<text x="312" y="100" class="h">Scheduler (control-plane)</text>
<text x="312" y="122" class="t">match: idle ∧ group ∧ caps⊇</text>
<text x="312" y="141" class="t">∧ ResourceReq floor cleared</text>
<text x="312" y="163" class="t">dispatch_scheduled → per-group</text>
<text x="312" y="181" class="t">exclusive gate (1 permit)</text>
<text x="312" y="197" class="s">heavy → Loki/Odin, NEVER the NUC</text>
<path class="e" d="M250 142 H300" marker-end="url(#arr)"/>
<!-- authed wire -->
<rect class="box" x="300" y="248" width="230" height="86"/>
<text x="312" y="272" class="h">GrpcAgent → serve_authed</text>
<text x="312" y="292" class="t">Bearer auth_interceptor (L5)</text>
<text x="312" y="311" class="t">connect_with_token</text>
<text x="312" y="328" class="s">no/wrong token → Offline, never scheduled</text>
<path class="e" d="M415 208 V248" marker-end="url(#arr)"/>
<!-- worker box -->
<rect class="grp" x="580" y="240" width="300" height="100"/>
<text x="594" y="263" class="hg">worker box · run_worker(bind, spec, token)</text>
<text x="594" y="285" class="t">LocalAgent runs the payload</text>
<text x="594" y="304" class="t">Process→Exe · Container→OCI · Vm→KVM</text>
<text x="594" y="321" class="s"><host> agent --group … (leaf entrypoint, L5a)</text>
<path class="wire" d="M530 291 H580" marker-end="url(#arrP)"/>
<text x="536" y="283" class="hp" font-size="11">authed gRPC</text>
<!-- Roster seeds the fleet -->
<rect class="grp" x="580" y="76" width="300" height="132"/>
<text x="594" y="99" class="hg">Roster — durable fleet</text>
<text x="594" y="120" class="t">Loki 12c · Odin 32c · NUC 6c · Njord</text>
<text x="594" y="139" class="t">live(now) → seeds schedulable agents</text>
<text x="594" y="158" class="t">heartbeat · evict_dead · load/save</text>
<text x="594" y="180" class="s">the NUC is in the fleet but a heavy</text>
<text x="594" y="195" class="s">ResourceReq never places on it</text>
<path class="e" d="M580 140 C555 140 545 142 530 143" marker-end="url(#arr)"/>
<text x="536" y="132" class="s">live agents</text>
<!-- WorkResult return (stage 4) -->
<rect class="res" x="580" y="384" width="300" height="96"/>
<text x="594" y="408" class="hp">WorkResult (stage 4)</text>
<text x="594" y="429" class="t">status · node · exit_code · result_ref</text>
<text x="594" y="448" class="t">serde round-trips over the return wire</text>
<text x="594" y="466" class="s">result_ref = nornir-flight ticket (bulk bytes)</text>
<path class="wire" d="M730 340 V384" marker-end="url(#arrP)"/>
<text x="738" y="366" class="s">returns</text>
<!-- central ledger -->
<rect class="led" x="20" y="384" width="510" height="96"/>
<text x="32" y="408" class="hl">JobStore.submit → JobRecord ledger (redb)</text>
<text x="32" y="430" class="t">to_job_record: status folded · node STAMPED · timing filled</text>
<text x="32" y="449" class="t">recorded against the RIGHT node (odin) · reuses thin Jobs.Submit</text>
<text x="32" y="467" class="s">the central ledger now shows each machine's jobs + results</text>
<path class="en" d="M580 432 H530" marker-end="url(#arrN)"/>
<text x="540" y="424" class="s">record</text>
</svg>