Make Sprout a standard-compliant Nostr relay alongside its enterprise extensions
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
Sprout uses the Nostr wire protocol (NIP-01) and extends it with enterprise features: NIP-29 relay-based groups, workflows, canvases, forums, agent harness, and audit logging. However, standard Nostr clients (Damus, Amethyst, Coracle) cannot function normally because Sprout rejects most standard event kinds via a hard allowlist in `required_scope_for_kind()`.
PRs #245, #246, and #249 begin addressing this by adding kind:1 (text notes), kind:3 (follow lists), kind:30023 (long-form articles), NIP-16 replaceable event routing, and NIP-33 parameterized replaceable event support. This issue tracks the remaining work to make Sprout a standard-compliant Nostr relay that also has its enterprise/agent collaboration extensions.
The goal: Sprout should be a **superset** — a good Nostr relay that standard clients can connect to, plus all the Slack-replacement and agent features that make it unique.
---
## What standard clients need
Research into Damus, Amethyst, and Coracle source code shows these clients:
- **On login**, fetch kinds 0, 3, 10002, 10050, 10000, 10006, 10009, 10013, 30315 (profiles, follows, relay lists, DM relay lists, mute lists, blocked relays, group lists, interests, user status)
- **For home feeds**, query with multi-author filters (up to 500 authors per batch) for kinds 1, 6, 7, 30023
- **For threads/replies**, query with `#e` tag filters referencing parent events
- **For zap counts**, query kind:9735 with `#e` filters referencing the zapped event
- **For DMs**, use NIP-17 gift wraps (kind:1059, already supported) plus kind:13 seals
- **All three clients** fully support NIP-42 authentication
---
## Remaining work (two PRs)
### PR 4: Accept standard social kinds
Open the kind allowlist for the standard kinds that clients publish and query.
**Regular kinds:**
- kind:6 (NIP-18 reposts) — in every client's home feed filter
- kind:16 (NIP-18 generic reposts)
- kind:9734 / kind:9735 (NIP-57 zap request / zap receipt) — relay role is passive (just store events; the LNURL server handles Lightning)
- kind:13 (NIP-59 seal) — completes the NIP-17 DM flow alongside kind:1059 gift wraps. Must be `#p`-gated on REQ to prevent metadata leakage (same treatment as kind:1059).
**NIP-16 replaceable range (10000–19999) — open the entire range:**
PR #245 already routes `is_replaceable()` kinds through atomic `replace_addressable_event()` with stale-write protection. The allowlist is the only remaining gate. Key kinds unlocked:
- kind:10000 (NIP-51 mute list)
- kind:10002 (NIP-65 relay list metadata — critical for Amethyst's outbox model)
- kind:10050 (NIP-17 DM relay list)
- kind:10009 (NIP-51 group list)
- All other NIP-51 list kinds
**NIP-33 parameterized replaceable range (30000–39999) — open the range with NIP-29 carve-out:**
PR #246 already routes `is_parameterized_replaceable()` kinds through atomic `replace_parameterized_event()`. Open the range with an explicit carve-out for NIP-29 group-state kinds (39000–39003) which remain channel-scoped. Key kinds unlocked:
- kind:30315 (NIP-38 user status — Amethyst fetches on login)
- kind:30078 (NIP-78 app-specific data)
**Estimated effort:** Small (~50 LOC, 1–2 days). The storage infrastructure is already built by PRs #245 and #246.
### PR 5: REQ query pushdown (ids, multi-author, #e)
Fix silent data loss in standard client fetch patterns. The current REQ handler pushes only kinds, single author, since/until, `#p`, and `#d` into SQL. Everything else is post-filtered after a capped `LIMIT` query, which means events that exist in the database are silently not returned.
**`ids` pushdown:**
Clients fetch specific events by ID for thread root resolution, quote resolution, and profile lookups. The `idx_events_id` index exists but is never used by the query builder. Push `ids` into SQL as `AND id IN (...)`.
**Multi-author pushdown:**
Clients send follow-feed queries with up to 500 authors. Currently only single-author is pushed to SQL — multi-author falls through to post-filter, consuming the entire LIMIT window with events from non-followed authors. Push multi-author into SQL as `AND pubkey IN (...)`. For >500 authors, chunk into multiple SQL queries and merge results (no lossy fallback).
**`#e` tag pushdown:**
Clients fetch replies and zap counts via `#e` tag filters. Currently post-filtered — under volume, replies to older events and zap receipts are invisible. Add an `event_references` denormalized table (same pattern as existing `event_mentions` for `#p` tags) with a join-based query path.
**Estimated effort:** Medium (schema change + ~330 LOC, 1–2 weeks).
---
## After these two PRs
Sprout becomes a standard-compliant authenticated Nostr relay with meaningful client interoperability: core social kinds, NIP-16 replaceables, NIP-33 parameterized replaceables, NIP-17 private DMs, NIP-29 relay-based groups, NIP-57 zap event storage, and correct REQ behavior for `ids`, multi-author feeds, and `#e` thread/zap lookups — alongside all existing enterprise features (workflows, canvases, forums, agent harness, audit logging, full-text search).
## References
- Plans: `PLANS/SPROUT_PR4_STANDARD_SOCIAL_KINDS.md`, `PLANS/SPROUT_PR5_REQ_QUERY_PUSHDOWN.md`
- Prerequisite PRs: #245, #246, #249
- NIP specs: [NIP-16](https://github.com/nostr-protocol/nips/blob/master/16.md), [NIP-33](https://github.com/nostr-protocol/nips/blob/master/33.md), [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md), [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md), [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md)
Contributor guide
Assessment
This issue has not been assessed yet.