Skip to content

feat(relay): implement NIP-ER event reminder support (kind:30300)#934

Draft
wpfleger96 wants to merge 2 commits into
mainfrom
paul/nip-er-relay-core
Draft

feat(relay): implement NIP-ER event reminder support (kind:30300)#934
wpfleger96 wants to merge 2 commits into
mainfrom
paul/nip-er-relay-core

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Stack

Stack: this PR → #957

This is the foundation PR. #957 (push scheduler) stacks on top of this branch.


Summary

Adds relay-side support for NIP-ER encrypted event reminders (kind:30300). This is a parameterized replaceable, author-only event type that carries a public not_before schedule tag while keeping reminder content NIP-44 encrypted.

Changes

Write path (sprout-core, sprout-relay/handlers/ingest)

  • Register KIND_EVENT_REMINDER (30300) in sprout-core::kind as parameterized replaceable, global-only
  • Add AUTHOR_ONLY_KINDS array for cross-cutting read-path enforcement
  • Validate not_before tag: at most one permitted, decimal digits only, no leading zeros (except "0"), range 0..=9007199254740991 (Number.MAX_SAFE_INTEGER). Optional — terminal-state and bookmark reminders omit it
  • Validate d tag: exactly one required, non-empty
  • Reject events where expiration <= not_before (window would expire before due)
  • Scope to UsersWrite — same as contacts, read-state, engrams

Read path (sprout-relay/handlers/req, count, api/bridge)

  • author_only_filters_authorized(): pre-filter gate rejects exclusive kind:30300 filters unless authors=[self] — returns CLOSED restricted: (WS) or HTTP 403
  • is_author_only_event(): per-event filter silently omits other authors' reminders from mixed-kind result sets
  • filter_can_match_author_only_kinds(): forces COUNT handler onto the slow path (per-event filtering) to prevent leaking aggregate counts
  • Applied consistently across WS REQ, WS COUNT, HTTP /query, HTTP /count, and HTTP /query search paths

Integration tests (e2e_event_reminder.rs)

16 e2e tests covering:

  • Write validation: valid reminders, missing/duplicate/malformed not_before, d-tag validation, expiration ordering, edge cases (0, MAX_SAFE_INTEGER)
  • Read filtering: author self-query succeeds, cross-user query rejected (HTTP 403 + WS CLOSED), mixed-kind filters omit non-author reminders, parameterized replacement semantics

@wpfleger96 wpfleger96 requested a review from a team as a code owner June 9, 2026 23:09
@wpfleger96 wpfleger96 force-pushed the paul/nip-er-relay-core branch from 90eda63 to 512d676 Compare June 9, 2026 23:10
@wpfleger96 wpfleger96 marked this pull request as draft June 10, 2026 00:00
@wpfleger96 wpfleger96 force-pushed the paul/nip-er-relay-core branch 2 times, most recently from 54858c7 to aca85a7 Compare June 10, 2026 01:04
Adds relay-side support for NIP-ER encrypted event reminders:

Write path (ingest):
- Register KIND_EVENT_REMINDER (30300) as parameterized replaceable,
  global-only, and UsersWrite-scoped
- Validate not_before tag: exactly one, decimal digits only, no leading
  zeros, range 0..=MAX_SAFE_INTEGER
- Reject expiration <= not_before when both are present and parseable

Read path (REQ/COUNT/bridge):
- AUTHOR_ONLY_KINDS array gates visibility — only the authenticated
  event author may read kind:30300 events
- Exclusive author-only filters (kinds:[30300]) require authors=[self]
  or get CLOSED/403
- Mixed-kind filters pass the gate but per-event filtering silently
  omits other authors' reminders
- COUNT fast path disabled when author-only kinds are in play to
  prevent leaking aggregate counts

Integration tests (e2e_event_reminder.rs):
- Write validation: valid, missing, duplicate, malformed not_before;
  expiration ordering; edge cases (0, MAX_SAFE_INTEGER)
- Read filtering: author self-query, cross-user rejection (HTTP 403
  and WS CLOSED), mixed-kind omission, replacement semantics

Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Will Pfleger <wpfleger@squareup.com>
Signed-off-by: Will Pfleger <wpfleger@squareup.com>
@wpfleger96 wpfleger96 force-pushed the paul/nip-er-relay-core branch 3 times, most recently from 8432f61 to 6503871 Compare June 10, 2026 18:31
The spec requires not_before on pending reminders only — terminal states
(done/cancelled) and bookmarks omit it. The validator incorrectly
required exactly one not_before on all kind:30300 events, blocking the
entire completion/cancellation lifecycle.

Also adds d-tag structure validation per spec: reject zero, empty, or
duplicate d tags.

Co-authored-by: Will Pfleger <will@pfleger.dev>
Signed-off-by: Will Pfleger <will@pfleger.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant