diff --git a/doc/library-development/ADRs/relay-auth.md b/doc/library-development/ADRs/relay-auth.md new file mode 100644 index 000000000..53ae53e2e --- /dev/null +++ b/doc/library-development/ADRs/relay-auth.md @@ -0,0 +1,75 @@ +# Architecture Decision Record: Relay authentication + +Title: Relay authentication - who a query, subscription or broadcast authenticates as + +## status + +accepted + +Updated on 2026-09-02 + +## contributors + +- Main contributor(s): nogringo + +- Reviewer(s): frnandu, 1leo + +- Final decision made by: frnandu, 1leo, nogringo + +## Context and Problem Statement + +A connection carries at most one identity, immutable for its whole lifetime +(`RelayConnectionKey`). The caller-facing half is missing: `authenticateAs: List?` +cannot say "never be attributable for this request", nor "authenticate before asking". + +And the absent case is the leaky one. With no `authenticateAs`, a request that meets +`auth-required` falls back to the logged account, so the relay decides when an identity is +revealed. + +## Main Proposal + +### RelayAuth + +```dart +sealed class RelayAuth { + const factory RelayAuth.never(); // (url, null), never sends AUTH + const factory RelayAuth.allow(Account a); // (url, null), moves to (url, a) if refused + const factory RelayAuth.require(Account a); // (url, a) from the start +} + +ndk.broadcast.broadcast( + nostrEvent: report, + auth: const RelayAuth.never(), +); +``` + +### RelayAuthHandler + +```dart +typedef RelayAuthHandler = Future Function(RelayAuthRequest); + +sealed class RelayAuthRequest { + final String relayUrl; + final Account? account; + final AuthRefusal? refusal; // null when asked before any refusal +} + +class ReadAuthRequest extends RelayAuthRequest { + final Filter? filter; + final String? requestName; +} + +class WriteAuthRequest extends RelayAuthRequest { + final Nip01Event event; +} + +class AuthRefusal { + final AuthReason reason; // authRequired, restricted, blocked, rateLimited + final String message; // raw relay message +} +``` + +Configured once on the NDK config, consulted only for `allow` and `require`. With no handler, +both authenticate automatically. + +Returning `null` means do not authenticate.