diff --git a/quartz/plans/2026-06-04-auth-scope-relay-components.svg b/quartz/plans/2026-06-04-auth-scope-relay-components.svg index 801319fd75..31d9a1af73 100644 --- a/quartz/plans/2026-06-04-auth-scope-relay-components.svg +++ b/quartz/plans/2026-06-04-auth-scope-relay-components.svg @@ -10,7 +10,7 @@ Building a Relay with Quartz — components & data flow - after the plan: auth identity lives in the connection scope, not in a policy + auth identity lives in the engine-owned connection scope, not in a policy @@ -51,7 +51,7 @@ accept(EVENT/REQ/COUNT/AUTH) → allow · reject · rewrite filters onConnect(scope, send) ← reads scope to gate - onAuthenticated() : Boolean → "record this id?" + onAuthenticated(event) : Boolean → "record this id?" FullAuthPolicy: per-connection — may hold scope ref VerifyPolicy / EmptyPolicy: shared singletons — @@ -94,14 +94,14 @@ creates & owns - WRITE: add(pubkey) + WRITE: add(event.pubKey) on verified AUTH accept(cmd) - onAuthenticated()→Bool + onAuthenticated(event)→Bool @@ -126,23 +126,23 @@ - What the plan changes (vs. the shipped branch) + How authentication flows (current design) - Ownership moves: auth identity is SCOPE, not POLICY - • the connection scope (engine-owned) now holds authenticatedUsers — it was a field on FullAuthPolicy. - • RequestContext.authenticatedUsers reads that backing set directly (no policy involved). + Ownership: auth identity is SCOPE, not POLICY + • the engine-owned connection scope holds authenticatedUsers; the policy runs the auth logic but stores nothing. + • RequestContext.authenticatedUsers reads that backing set directly; it is one set per RelaySession (per connection). WRITE path (green): engine-side, single commit - • RelaySession.handleAuth: policy.accept(AuthCmd) verifies → onAuthenticated() returns Boolean → - only on true does the engine add(pubkey). PassThrough/Empty return false → unverified ids never recorded. + • RelaySession.handleAuth: policy.accept(AuthCmd) verifies → onAuthenticated(event): Boolean → + only on true does the engine add(event.pubKey). PassThrough/Empty return false → unverified ids never recorded. READ path (teal): policy reads, source reads • gating policies read scope.authenticatedUsers (scope injected via onConnect(scope, send)). • EventSource reads ctx.authenticatedUsers for caller-relative results. - Deleted - • AuthScopedPolicy marker • PolicyStack.authenticatedUsers union • RequestContext `as? AuthScopedPolicy` downcast + One place, by design + • auth state lives only in the scope — no AuthScopedPolicy marker, no PolicyStack union, no RequestContext downcast. - Signature changes (source-breaking, in-tree SPI) - • IRelayPolicy.onConnect(send) → onConnect(scope, send) • IRelayPolicy.onAuthenticated(...) → returns Boolean + Policy contract + • IRelayPolicy.onConnect(scope, send) • IRelayPolicy.onAuthenticated(event): Boolean diff --git a/quartz/plans/auth-scope-relay-components.png b/quartz/plans/auth-scope-relay-components.png index bfae704c6e..0972cd0f34 100644 Binary files a/quartz/plans/auth-scope-relay-components.png and b/quartz/plans/auth-scope-relay-components.png differ