feat(nip17): support gift-wrapped private direct messages
Generalize the AUTH-gated, recipient-only delivery rule from NIP-04 to also cover NIP-17 kind 1059 gift wraps. When the relay is configured to require AUTH for kind 1059, only the AUTH'd recipient named in the event's `p` tag receives it; otherwise gift wraps broadcast like any regular event. - relay/event.py: add `is_seal`, `is_gift_wrap`, `is_private_message` helpers (kinds 13, 1059) - relay/client_connection.py: rename `_is_direct_message_for_other` -> `_is_private_event_for_other`; key off `is_private_message` so the same gating applies to kinds 4 and 1059 - relay/relay.py: advertise NIPs 17, 44, 59 in NIP-11 supported_nips - README: document NIP-17/44/59 transport-level support - tests/test_nip17.py: unit tests for kind classification, AUTH-gated 1059 delivery (recipient vs non-recipient vs unauthenticated), and regression coverage for kind 4 gating NIP-44 (encryption) and NIP-59 (wrap/seal) are client-side concerns; the relay treats payloads as opaque ciphertext and stores kind 1059 like any regular event. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b74af2628e
commit
4811fcf352
5 changed files with 166 additions and 8 deletions
|
|
@ -69,7 +69,7 @@ class NostrClientConnection:
|
|||
self.event_validator.get_client_config = get_client_config
|
||||
|
||||
async def notify_event(self, event: NostrEvent) -> bool:
|
||||
if self._is_direct_message_for_other(event):
|
||||
if self._is_private_event_for_other(event):
|
||||
return False
|
||||
|
||||
for nostr_filter in self.filters:
|
||||
|
|
@ -83,13 +83,14 @@ class NostrClientConnection:
|
|||
)
|
||||
return False
|
||||
|
||||
def _is_direct_message_for_other(self, event: NostrEvent) -> bool:
|
||||
def _is_private_event_for_other(self, event: NostrEvent) -> bool:
|
||||
"""
|
||||
Direct messages are not inteded to be boradcast (even if encrypted).
|
||||
If the server requires AUTH for kind '4' then direct message will be
|
||||
sent only to the intended client.
|
||||
p-tagged events that carry a single intended recipient (NIP-04 kind 4
|
||||
direct messages and NIP-17 kind 1059 gift wraps) should not be
|
||||
broadcast to arbitrary subscribers when the relay enforces AUTH for
|
||||
that kind. Deliver only to the AUTH'd recipient named in a `p` tag.
|
||||
"""
|
||||
if not event.is_direct_message:
|
||||
if not event.is_private_message:
|
||||
return False
|
||||
if not self.config.event_requires_auth(event.kind):
|
||||
return False
|
||||
|
|
@ -317,7 +318,7 @@ class NostrClientConnection:
|
|||
nostr_filter.enforce_limit(self.config.limit_per_filter)
|
||||
self.filters.append(nostr_filter)
|
||||
events = await get_events(self.relay_id, nostr_filter)
|
||||
events = [e for e in events if not self._is_direct_message_for_other(e)]
|
||||
events = [e for e in events if not self._is_private_event_for_other(e)]
|
||||
serialized_events = [
|
||||
event.serialize_response(subscription_id) for event in events
|
||||
]
|
||||
|
|
|
|||
|
|
@ -64,6 +64,21 @@ class NostrEvent(BaseModel):
|
|||
def is_delete_event(self) -> bool:
|
||||
return self.kind == 5
|
||||
|
||||
@property
|
||||
def is_seal(self) -> bool:
|
||||
return self.kind == 13
|
||||
|
||||
@property
|
||||
def is_gift_wrap(self) -> bool:
|
||||
return self.kind == 1059
|
||||
|
||||
@property
|
||||
def is_private_message(self) -> bool:
|
||||
# Kinds whose payload addresses a single recipient via a `p` tag and is
|
||||
# not meant to be broadcast to other subscribers when AUTH is enforced.
|
||||
# NIP-04 (kind 4) and NIP-17 (kind 1059 gift wrap).
|
||||
return self.is_direct_message or self.is_gift_wrap
|
||||
|
||||
@property
|
||||
def is_regular_event(self) -> bool:
|
||||
return self.kind >= 1000 and self.kind < 10000
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ class NostrRelay(BaseModel):
|
|||
) -> dict:
|
||||
return {
|
||||
"contact": "https://t.me/lnbits",
|
||||
"supported_nips": [1, 2, 4, 9, 11, 15, 16, 20, 22, 28, 42],
|
||||
"supported_nips": [1, 2, 4, 9, 11, 15, 16, 17, 20, 22, 28, 42, 44, 59],
|
||||
"software": "LNbits",
|
||||
"version": "",
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue