<svg width="100%" viewBox="0 0 760 420" role="img" xmlns="http://www.w3.org/2000/svg">
  <title>Chat — one persistent SSE stream for output, fire-and-forget POSTs for input</title>
  <desc>EventSource can't set headers, so the JWT rides as a query param; sudo-api fans plugin pushes out to every open tab for the user.</desc>
  <style>
    text{font-family:"Anthropic Sans",-apple-system,system-ui,"Segoe UI",sans-serif}
    .t{font-size:14px;font-weight:500;text-anchor:middle;dominant-baseline:central}
    .s{font-size:12px;text-anchor:middle;dominant-baseline:central}
    .lbl{font-size:11px;fill:#9c9a92;text-anchor:middle}
    rect{stroke-width:.5}
    .neutral{fill:#444441;stroke:#b4b2a9} .nt{fill:#d3d1c7} .ns{fill:#b4b2a9}
    .blue{fill:#1f3b7a;stroke:#8aaef0}    .bt{fill:#cfdcf4} .bs{fill:#8aaef0}
    .orange{fill:#712b13;stroke:#f0997b}  .ot{fill:#f5c4b3} .os{fill:#f0997b}
    .arrow{fill:none;stroke:#9c9a92;stroke-width:1.5}
    .arrow-ok{fill:none;stroke:#1d9e75;stroke-width:1.5}
  </style>
  <defs>
    <marker id="a" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
      <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
    </marker>
  </defs>

  <rect class="neutral" x="40" y="40" width="200" height="100" rx="8"/>
  <text class="t nt" x="140" y="64">Browser (any tab)</text>
  <text class="s ns" x="140" y="90">opens GET /chat/stream</text>
  <text class="s ns" x="140" y="110">posts POST /chat/turn</text>
  <text class="s ns" x="140" y="128">renders incoming deltas</text>

  <rect class="neutral" x="40" y="160" width="200" height="60" rx="8"/>
  <text class="t nt" x="140" y="184">Another tab</text>
  <text class="s ns" x="140" y="204">also subscribed to stream</text>

  <rect class="neutral" x="40" y="240" width="200" height="60" rx="8"/>
  <text class="t nt" x="140" y="264">…and another</text>
  <text class="s ns" x="140" y="284">all see the same fan-out</text>

  <rect class="blue" x="320" y="120" width="240" height="120" rx="8"/>
  <text class="t bt" x="440" y="148">sudo-api</text>
  <text class="s bs" x="440" y="172">holds open SSE connections</text>
  <text class="s bs" x="440" y="190">forwards turn POSTs to plugin</text>
  <text class="s bs" x="440" y="208">fans /v1/internal/chat/push</text>
  <text class="s bs" x="440" y="226">to every open tab</text>

  <rect class="orange" x="620" y="120" width="120" height="120" rx="8"/>
  <text class="t ot" x="680" y="160">hermes</text>
  <text class="s os" x="680" y="186">sudo_chat</text>
  <text class="s os" x="680" y="204">plugin</text>
  <text class="s os" x="680" y="222">:8652</text>

  <path class="arrow"    d="M 240  80 C 280  80, 290 130, 318 130" marker-end="url(#a)"/>
  <text class="lbl"      x="270"  y="68">GET stream  ·  POST turn</text>
  <path class="arrow"    d="M 240 190 L 318 190" marker-end="url(#a)"/>
  <path class="arrow"    d="M 240 260 C 280 260, 290 210, 318 210" marker-end="url(#a)"/>

  <path class="arrow"    d="M 560 150 L 618 150" marker-end="url(#a)"/>
  <text class="lbl"      x="588" y="140">/inbound</text>
  <path class="arrow-ok" d="M 620 220 L 562 220" marker-end="url(#a)"/>
  <text class="lbl"      x="588" y="240">/chat/push (deltas)</text>

  <rect x="320" y="280" width="420" height="84" rx="8" fill="none" stroke="#5a5852" stroke-width=".5" stroke-dasharray="5 4" opacity=".7"/>
  <text class="s ns" x="530" y="306" text-anchor="middle">Why two connections?</text>
  <text class="s ns" x="530" y="328" text-anchor="middle">EventSource can't set headers → JWT rides as ?token=… on the stream URL.</text>
  <text class="s ns" x="530" y="350" text-anchor="middle">POST /chat/turn uses a normal Authorization header.</text>
</svg>
