docs(quartz/relay): update component diagram to the merged design

Reframe from a forward-looking plan diff to the current architecture, and fix
the onAuthenticated signature to (event): Boolean (pubKey was dropped). All
structural elements already matched the merged code.
This commit is contained in:
Claude
2026-06-04 18:29:52 +00:00
parent 0791ae9d40
commit d2e64a0339
2 changed files with 14 additions and 14 deletions
@@ -10,7 +10,7 @@
<rect x="0" y="0" width="1200" height="1010" fill="#f7f8fa"/>
<text x="600" y="38" text-anchor="middle" font-size="24" font-weight="700" fill="#0f172a">Building a Relay with Quartz — components &amp; data flow</text>
<text x="600" y="62" text-anchor="middle" font-size="14" fill="#475569">after the plan: auth identity lives in the connection scope, not in a policy</text>
<text x="600" y="62" text-anchor="middle" font-size="14" fill="#475569">auth identity lives in the engine-owned connection scope, not in a policy</text>
<!-- ============ TRANSPORT ============ -->
<rect x="40" y="86" width="360" height="66" rx="10" fill="#eef0f3" stroke="#6b7280" stroke-width="1.6"/>
@@ -51,7 +51,7 @@
<text x="56" y="318" font-size="12" fill="#374151">accept(EVENT/REQ/COUNT/AUTH)</text>
<text x="56" y="334" font-size="12" fill="#374151">→ allow · reject · rewrite filters</text>
<text x="56" y="356" font-size="12" fill="#374151">onConnect(scope, send) ← reads scope to gate</text>
<text x="56" y="372" font-size="12" fill="#374151">onAuthenticated() : Boolean → "record this id?"</text>
<text x="56" y="372" font-size="12" fill="#374151">onAuthenticated(event) : Boolean → "record this id?"</text>
<line x1="56" y1="384" x2="404" y2="384" stroke="#eecfa3" stroke-width="1.2"/>
<text x="56" y="404" font-size="11.5" font-style="italic" fill="#92600a">FullAuthPolicy: per-connection — may hold scope ref</text>
<text x="56" y="420" font-size="11.5" font-style="italic" fill="#92600a">VerifyPolicy / EmptyPolicy: shared singletons —</text>
@@ -94,14 +94,14 @@
<line x1="600" y1="318" x2="600" y2="368" stroke="#059669" stroke-width="2.2" marker-end="url(#arrow)"/>
<text x="590" y="346" text-anchor="end" font-size="10.8" font-weight="700" fill="#047857">creates &amp; owns</text>
<line x1="690" y1="318" x2="690" y2="368" stroke="#059669" stroke-width="2.2" marker-end="url(#arrow)"/>
<text x="700" y="340" font-size="10.8" font-weight="700" fill="#047857">WRITE: add(pubkey)</text>
<text x="700" y="340" font-size="10.8" font-weight="700" fill="#047857">WRITE: add(event.pubKey)</text>
<text x="700" y="354" font-size="10" fill="#047857">on verified AUTH</text>
<!-- RelaySession <-> Policy (decisions) -->
<line x1="446" y1="258" x2="424" y2="258" stroke="#334155" stroke-width="1.8" marker-end="url(#arrow)"/>
<line x1="424" y1="276" x2="446" y2="276" stroke="#334155" stroke-width="1.8" marker-end="url(#arrow)"/>
<text x="435" y="240" text-anchor="middle" font-size="10.5" fill="#334155">accept(cmd)</text>
<text x="435" y="296" text-anchor="middle" font-size="10.5" fill="#334155">onAuthenticated()→Bool</text>
<text x="435" y="296" text-anchor="middle" font-size="10.5" fill="#334155">onAuthenticated(event)→Bool</text>
<!-- Scope -> Policy (READ: gate) -->
<line x1="446" y1="430" x2="424" y2="392" stroke="#0e7490" stroke-width="1.8" stroke-dasharray="5 3" marker-end="url(#arrowDash)"/>
@@ -126,23 +126,23 @@
<!-- ===================== PLAN CALLOUT ===================== -->
<rect x="40" y="636" width="1120" height="350" rx="12" fill="#fff8e1" stroke="#f59e0b" stroke-width="1.8"/>
<text x="60" y="664" font-size="15" font-weight="700" fill="#92600a">What the plan changes (vs. the shipped branch)</text>
<text x="60" y="664" font-size="15" font-weight="700" fill="#92600a">How authentication flows (current design)</text>
<text x="60" y="694" font-size="13" font-weight="700" fill="#065f46">Ownership moves: auth identity is SCOPE, not POLICY</text>
<text x="60" y="714" font-size="12.5" fill="#374151">• the connection scope (engine-owned) now holds authenticatedUsers — it was a field on FullAuthPolicy.</text>
<text x="60" y="733" font-size="12.5" fill="#374151">• RequestContext.authenticatedUsers reads that backing set directly (no policy involved).</text>
<text x="60" y="694" font-size="13" font-weight="700" fill="#065f46">Ownership: auth identity is SCOPE, not POLICY</text>
<text x="60" y="714" font-size="12.5" fill="#374151">• the engine-owned connection scope holds authenticatedUsers; the policy runs the auth logic but stores nothing.</text>
<text x="60" y="733" font-size="12.5" fill="#374151">• RequestContext.authenticatedUsers reads that backing set directly; it is one set per RelaySession (per connection).</text>
<text x="60" y="763" font-size="13" font-weight="700" fill="#047857">WRITE path (green): engine-side, single commit</text>
<text x="60" y="783" font-size="12.5" fill="#374151">• RelaySession.handleAuth: policy.accept(AuthCmd) verifies → onAuthenticated() returns Boolean →</text>
<text x="60" y="802" font-size="12.5" fill="#374151"> only on true does the engine add(pubkey). PassThrough/Empty return false → unverified ids never recorded.</text>
<text x="60" y="783" font-size="12.5" fill="#374151">• RelaySession.handleAuth: policy.accept(AuthCmd) verifies → onAuthenticated(event): Boolean →</text>
<text x="60" y="802" font-size="12.5" fill="#374151"> only on true does the engine add(event.pubKey). PassThrough/Empty return false → unverified ids never recorded.</text>
<text x="60" y="832" font-size="13" font-weight="700" fill="#0e7490">READ path (teal): policy reads, source reads</text>
<text x="60" y="852" font-size="12.5" fill="#374151">• gating policies read scope.authenticatedUsers (scope injected via onConnect(scope, send)).</text>
<text x="60" y="871" font-size="12.5" fill="#374151">• EventSource reads ctx.authenticatedUsers for caller-relative results.</text>
<text x="60" y="901" font-size="13" font-weight="700" fill="#b91c1c">Deleted</text>
<text x="60" y="921" font-size="12.5" fill="#374151">• AuthScopedPolicy marker PolicyStack.authenticatedUsers union RequestContext `as? AuthScopedPolicy` downcast</text>
<text x="60" y="901" font-size="13" font-weight="700" fill="#065f46">One place, by design</text>
<text x="60" y="921" font-size="12.5" fill="#374151"> auth state lives only in the scope — no AuthScopedPolicy marker, no PolicyStack union, no RequestContext downcast.</text>
<text x="60" y="951" font-size="13" font-weight="700" fill="#92600a">Signature changes (source-breaking, in-tree SPI)</text>
<text x="60" y="971" font-size="12.5" fill="#374151">• IRelayPolicy.onConnect(send) → onConnect(scope, send) • IRelayPolicy.onAuthenticated(...) → returns Boolean</text>
<text x="60" y="951" font-size="13" font-weight="700" fill="#92600a">Policy contract</text>
<text x="60" y="971" font-size="12.5" fill="#374151">• IRelayPolicy.onConnect(scope, send) • IRelayPolicy.onAuthenticated(event): Boolean</text>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 KiB

After

Width:  |  Height:  |  Size: 613 KiB