diff options
| author | bndw <ben@bdw.to> | 2026-02-14 08:52:59 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-14 08:52:59 -0800 |
| commit | 44aa0591b0eed7851e961ea17bd1c9601570ac24 (patch) | |
| tree | 198cf6958c1f550c0a218296a0414456df68c8e5 /internal/auth/README.md | |
| parent | 756c325223ef744b476ade565cb1970c7717d053 (diff) | |
docs: clarify NIP-98 relationship to NIP-42 and write access control
Explain that the gRPC NIP-98 implementation is effectively NIP-42 for
reads (same pattern: authenticate once, stream many events) and adds
standardized relay access control for writes (beyond event.sig).
Add comparison table showing functional equivalence for streaming reads
and the additional benefits for write access control.
Diffstat (limited to 'internal/auth/README.md')
| -rw-r--r-- | internal/auth/README.md | 64 |
1 files changed, 60 insertions, 4 deletions
diff --git a/internal/auth/README.md b/internal/auth/README.md index adfe260..c41b6cb 100644 --- a/internal/auth/README.md +++ b/internal/auth/README.md | |||
| @@ -6,6 +6,58 @@ This package implements [NIP-98](https://github.com/nostr-protocol/nips/blob/mas | |||
| 6 | 6 | ||
| 7 | NIP-98 provides HTTP authentication using Nostr event signatures instead of bearer tokens or OAuth2. It uses cryptographic signatures to prove the request came from a specific public key, without requiring passwords or centralized identity providers. | 7 | NIP-98 provides HTTP authentication using Nostr event signatures instead of bearer tokens or OAuth2. It uses cryptographic signatures to prove the request came from a specific public key, without requiring passwords or centralized identity providers. |
| 8 | 8 | ||
| 9 | ## Relationship to NIP-42 | ||
| 10 | |||
| 11 | This implementation serves as **NIP-42 for gRPC**, with some important distinctions: | ||
| 12 | |||
| 13 | ### For Reads (Queries/Subscriptions) | ||
| 14 | |||
| 15 | **NIP-42 (WebSocket)**: | ||
| 16 | - Authenticates the **connection** once via challenge-response | ||
| 17 | - All subsequent REQ messages use the authenticated connection | ||
| 18 | - Enables access control for private/restricted events | ||
| 19 | |||
| 20 | **NIP-98 (gRPC - this implementation)**: | ||
| 21 | - Authenticates **streaming RPCs** once at stream establishment | ||
| 22 | - All events streamed over that authenticated connection | ||
| 23 | - Same access control pattern as NIP-42 | ||
| 24 | |||
| 25 | **Result**: Functionally identical! Both authenticate once and stream many events. | ||
| 26 | |||
| 27 | ``` | ||
| 28 | WebSocket + NIP-42: AUTH challenge → Subscribe → 1000 events (no re-auth) | ||
| 29 | gRPC + NIP-98: Subscribe with auth → 1000 events (no re-auth) | ||
| 30 | ``` | ||
| 31 | |||
| 32 | ### For Writes (Publishing Events) | ||
| 33 | |||
| 34 | **NIP-42 (WebSocket)**: | ||
| 35 | - Not applicable - events are self-authenticating via `event.sig` | ||
| 36 | - No connection-level auth for EVENT messages | ||
| 37 | - Relay only verifies the event signature | ||
| 38 | |||
| 39 | **NIP-98 (gRPC - this implementation)**: | ||
| 40 | - Adds **relay access control** on top of event signatures | ||
| 41 | - Proves who is **submitting** (NIP-98) vs who **created** (event.sig) the event | ||
| 42 | - Enables use cases like: | ||
| 43 | - Rate limiting per user | ||
| 44 | - Allow-lists for relay access | ||
| 45 | - Preventing spam/abuse (submitting scraped events) | ||
| 46 | - Verifying submitter matches event author | ||
| 47 | |||
| 48 | **Result**: Standardizes a relay access control pattern beyond base Nostr's self-authenticating events. | ||
| 49 | |||
| 50 | ### Summary | ||
| 51 | |||
| 52 | | Use Case | WebSocket (NIP-42) | gRPC (NIP-98) | | ||
| 53 | |----------|-------------------|---------------| | ||
| 54 | | **Read auth** | Challenge-response, once per connection | Per-RPC auth, once per stream | | ||
| 55 | | **Read pattern** | ✅ Same: authenticate once, stream many | ✅ Same: authenticate once, stream many | | ||
| 56 | | **Write auth** | ❌ N/A (events self-auth) | ✅ Optional relay access control | | ||
| 57 | | **Overhead** | None after initial handshake | None for streams; minimal for unary calls | | ||
| 58 | |||
| 59 | This implementation gives you NIP-42's read authentication pattern plus standardized relay access control for writes. | ||
| 60 | |||
| 9 | ## How It Works | 61 | ## How It Works |
| 10 | 62 | ||
| 11 | ### Authentication Flow | 63 | ### Authentication Flow |
| @@ -167,10 +219,14 @@ authOpts := &auth.InterceptorOptions{ | |||
| 167 | 219 | ||
| 168 | 1. **No passwords**: Uses public key cryptography | 220 | 1. **No passwords**: Uses public key cryptography |
| 169 | 2. **Decentralized**: No central identity provider | 221 | 2. **Decentralized**: No central identity provider |
| 170 | 3. **Per-request auth**: Each request is independently authenticated | 222 | 3. **Nostr ecosystem compatible**: |
| 171 | 4. **Nostr compatible**: Works with existing Nostr identities and tools | 223 | - Same authentication as NIP-42 for WebSocket relays |
| 172 | 5. **Standard pattern**: Uses industry-standard gRPC credentials interface | 224 | - Works with existing Nostr identities (npub/nsec) |
| 173 | 6. **Key rotation**: Easy to rotate keys without server-side updates | 225 | - Compatible with Nostr clients and tools |
| 226 | 4. **Efficient for streaming**: Authenticate once per stream (like NIP-42 for WebSocket) | ||
| 227 | 5. **Standard gRPC pattern**: Drop-in replacement for OAuth2/JWT using `credentials.PerRPCCredentials` | ||
| 228 | 6. **Flexible access control**: Enables relay-level permissions beyond event signatures | ||
| 229 | 7. **Key rotation**: Easy to rotate keys without server-side session management | ||
| 174 | 230 | ||
| 175 | ## Compatibility | 231 | ## Compatibility |
| 176 | 232 | ||
