<svg width="100%" viewBox="0 0 880 480" role="img" xmlns="http://www.w3.org/2000/svg">
  <title>Auth model — every hop has its own bearer</title>
  <desc>Browser uses Supabase JWT; Pi uses a 1-year device JWT; voice-bridge uses PROVISIONER_INTERNAL_TOKEN to sudo-api; sudo-api and voice-bridge use per-user API_SERVER_KEY derived as HMAC(JWT_SECRET, user_id) to hermes; Pi and voice-bridge use scoped LiveKit tokens.</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}
    rect{stroke-width:.5}
    .neutral{fill:#444441;stroke:#b4b2a9} .nt{fill:#d3d1c7} .ns{fill:#b4b2a9}
    .green{fill:#27500a;stroke:#97c459}   .gt{fill:#c0dd97} .gs{fill:#97c459}
    .blue{fill:#1f3b7a;stroke:#8aaef0}    .bt{fill:#cfdcf4} .bs{fill:#8aaef0}
    .orange{fill:#712b13;stroke:#f0997b}  .ot{fill:#f5c4b3} .os{fill:#f0997b}
    .purple{fill:#3c3489;stroke:#afa9ec}  .pt{fill:#ceccf6} .ps{fill:#afa9ec}
    .arrow{fill:none;stroke:#9c9a92;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="56" rx="8"/>
  <text class="t nt" x="140" y="62">👤  Browser</text>
  <text class="s ns" x="140" y="80">Supabase JWT</text>

  <rect class="green" x="40"  y="140" width="200" height="56" rx="8"/>
  <text class="t gt" x="140" y="162">🎤  Pi (sudoedge)</text>
  <text class="s gs" x="140" y="180">Device JWT (HS256, 1y)</text>

  <rect class="blue" x="340" y="40"  width="200" height="56" rx="8"/>
  <text class="t bt" x="440" y="62">sudo-api</text>
  <text class="s bs" x="440" y="80">verifies Supabase + Device JWT</text>

  <rect class="blue" x="340" y="140" width="200" height="56" rx="8"/>
  <text class="t bt" x="440" y="162">voice-bridge</text>
  <text class="s bs" x="440" y="180">internal bearer to sudo-api</text>

  <rect class="blue" x="340" y="240" width="200" height="56" rx="8"/>
  <text class="t bt" x="440" y="262">livekit-server</text>
  <text class="s bs" x="440" y="280">scoped LiveKit tokens</text>

  <rect class="orange" x="640" y="90" width="220" height="84" rx="8"/>
  <text class="t ot" x="750" y="116">hermes (per-user)</text>
  <text class="s os" x="750" y="138">API_SERVER_KEY =</text>
  <text class="s os" x="750" y="156">HMAC(JWT_SECRET, user_id)</text>

  <rect class="purple" x="640" y="220" width="220" height="56" rx="8"/>
  <text class="t pt" x="750" y="242">Supabase Auth</text>
  <text class="s ps" x="750" y="260">issues + verifies JWTs</text>

  <path class="arrow" d="M 240 68  L 338 68"  marker-end="url(#a)"/>
  <text class="lbl"   x="246" y="58">Bearer Supabase JWT</text>

  <path class="arrow" d="M 240 168 L 338 168" marker-end="url(#a)"/>
  <text class="lbl"   x="246" y="158">Bearer Device JWT</text>

  <path class="arrow" d="M 240 175 C 280 175, 290 268, 338 268" marker-end="url(#a)" stroke-dasharray="4 3"/>
  <text class="lbl"   x="246" y="240">LiveKit token (room-scoped)</text>

  <path class="arrow" d="M 440 196 L 440 238" marker-end="url(#a)"/>
  <text class="lbl"   x="452" y="222">agent LiveKit token</text>

  <path class="arrow" d="M 540 70  C 620 70,  600 120, 640 120" marker-end="url(#a)"/>
  <text class="lbl"   x="556" y="106">API_SERVER_KEY</text>

  <path class="arrow" d="M 540 168 C 620 168, 600 152, 640 152" marker-end="url(#a)"/>
  <text class="lbl"   x="556" y="148">API_SERVER_KEY</text>

  <path class="arrow" d="M 540 78  C 600 78,  610 244, 640 244" marker-end="url(#a)" stroke-dasharray="4 3"/>
  <text class="lbl"   x="612" y="220">asyncpg</text>

  <rect x="40" y="340" width="820" height="110" rx="8" fill="none" stroke="#5a5852" stroke-dasharray="5 4" opacity=".7" stroke-width=".5"/>
  <text x="60" y="362" style="font-size:13px;font-weight:500;fill:#c2c0b6">The three API trust zones inside sudo-api</text>
  <text class="s ns" x="200" y="392" text-anchor="middle">/v1/me/*  ·  Supabase JWT</text>
  <text class="s ns" x="200" y="420" text-anchor="middle">things a logged-in user does</text>
  <text class="s ns" x="450" y="392" text-anchor="middle">/v1/internal/*  ·  PROVISIONER_INTERNAL_TOKEN</text>
  <text class="s ns" x="450" y="420" text-anchor="middle">service-to-service only · Caddy blocks the public path</text>
  <text class="s ns" x="720" y="392" text-anchor="middle">/v1/admin/*  ·  Supabase JWT + ADMIN_EMAILS</text>
  <text class="s ns" x="720" y="420" text-anchor="middle">allowlisted admin operations</text>
</svg>
