Skip to content

feat: Channel management, messaging, threads, DMs, reactions, and NIP-29 support#16

Merged
tlongwell-block merged 1 commit intomainfrom
feat/channel-features
Mar 10, 2026
Merged

feat: Channel management, messaging, threads, DMs, reactions, and NIP-29 support#16
tlongwell-block merged 1 commit intomainfrom
feat/channel-features

Conversation

@tlongwell-block
Copy link
Collaborator

Summary

Adds comprehensive channel features implementing the REST + NIP-29 dual API architecture. Both paths converge on shared DB functions as the single source of truth.

What's New

REST API (20 endpoints)

  • Channel metadata — get, update name/description, set topic, set purpose, archive, unarchive
  • Membership — add members, remove member, list members, join (open channels), leave
  • Messages — send (with threading), list top-level, get full thread
  • DMs — open/find DM, add member (creates new DM with expanded set), list DMs
  • Reactions — add, remove, list per message
  • User profiles — get, update

NIP-29 Relay Protocol (9 event handlers)

  • Kinds 9000–9022: PUT_USER, REMOVE_USER, EDIT_METADATA, DELETE_EVENT, CREATE_GROUP, DELETE_GROUP, LEAVE_REQUEST
  • Kind 7: NIP-25 reactions
  • Pre-storage validation for admin kinds (auth + archive checks + last-owner guard)
  • Post-storage side-effect dispatch for state mutations

MCP Tools (19 new + 2 updated)

Full agent interface for all channel features — send messages, manage members, set topics, add reactions, open DMs, etc.

Infrastructure

  • Relay keypair — dedicated secp256k1 key for signing system messages (kind 40099), configured via SPROUT_RELAY_PRIVATE_KEY env var or auto-generated on first run
  • 6 migrations — channel metadata columns, thread_metadata table, events deleted_at, DM participant_hash, reactions table, events id index

Architecture

REST handler  →  auth  →  DB function  ←  side-effect dispatch  ←  store  ←  handle_event()  ←  NIP-29 event
  • REST does NOT route through handle_event() (would reject relay-signed events)
  • Admin kinds (9000–9022) validate before storage; regular kinds store then side-effect
  • Tag convention: channel = Sprout UUID, h = NIP-29 group ID, e = event references only

Key Design Decisions

Feature Approach
Threads Materialized thread_metadata table with reply_count (direct) and descendant_count (all). Partition-pruned joins via stored parent/root_event_created_at.
DMs Immutable participant sets via participant_hash (SHA-256 of sorted concatenated pubkeys). Adding a member creates a new DM. open_dm is idempotent. Max 9 participants.
Reactions Atomic INSERT ... ON DUPLICATE KEY UPDATE — no TOCTOU race. GROUP_CONCAT(HEX(pubkey)) avoids binary corruption. Session group_concat_max_len raised to 1MB.
Archive enforcement All write endpoints (REST + NIP-29) check archived_at. Pre-storage rejection for kind 7 reactions.
Last-owner guard Enforced at 3 layers: pre-storage validation, side-effect handler, and DB transaction.

Quality

7 rounds of crossfire review (Claude Opus + OpenAI Codex + Gemini Pro):

Round Opus Codex Issues Found
R1 6/10 2/10 12
R2 8/10 6/10 5
R3 8.5 6.5 6
R4 7/10 7/10 9 (+ Gemini 7/10)
R5 9/10 ✅ 7/10 2
R6 9/10 ✅ 8/10 1
R7 10/10 ✅ 0

32 issues found and fixed across all rounds. All CI checks pass (clippy -D warnings, fmt, unit tests, cargo-deny).

Files Changed

16 new files:

  • 3 DB modules: thread.rs, dm.rs, reaction.rs
  • 6 REST handlers: channels_metadata.rs, members.rs, messages.rs, dms.rs, reactions.rs, users.rs
  • 1 NIP-29 handler: side_effects.rs
  • 6 migrations

12 modified files:

  • kind.rs (+KIND_SYSTEM_MESSAGE), channel.rs (+231 lines), lib.rs (+202 lines), user.rs (+75 lines)
  • server.rs (+754 lines — 19 MCP tools), router.rs (+66 lines — 16 routes)
  • event.rs (extract_channel_id fix, admin pre-validation, reaction archive check)
  • config.rs, state.rs, main.rs (relay keypair wiring)

Known Gaps (follow-up PRs)

  • soft_delete_event / soft_delete_channel DB methods (kind 9005/9008 handlers emit system messages but don't delete yet)
  • Phase 6 polish: enrich list_channels with member_count/topic/purpose, embed reaction counts and thread summaries in channel history, NIP-11 relay info update
  • Cursor-based pagination in some list endpoints (returns next_cursor: null)
  • Desktop E2E tests for new features

…-29 support

Add comprehensive channel features implementing the REST + NIP-29 dual API
architecture. Both paths converge on shared DB functions as the single source
of truth.

New capabilities:
- Channel metadata (topic, purpose, archive/unarchive)
- Membership management (add, remove, join, leave) with role-based auth
- Threaded messaging with materialized reply/descendant counts
- Direct messages with immutable participant sets (SHA-256 hash)
- Emoji reactions with atomic upsert
- NIP-29 relay protocol (kinds 9000-9022) with pre-storage validation
- 19 new MCP tools for agent interaction
- Relay keypair infrastructure for system messages (kind 40099)

6 migrations, 16 new files, 12 modified files (~1,420 insertions).
@tlongwell-block tlongwell-block merged commit 3e7c9d9 into main Mar 10, 2026
8 checks passed
@tlongwell-block tlongwell-block deleted the feat/channel-features branch March 10, 2026 18:47
tlongwell-block added a commit that referenced this pull request Mar 11, 2026
* origin/main:
  feat: soft-delete for events/channels, enriched API responses, NIP-29 group management (#17)
  feat: Channel management, messaging, threads, DMs, reactions, and NIP-29 support (#16)
  Improve chat scrolling and multiline composer (#14)
  chore: remove redundant inline comments across all crates (#13)
  Initial backend revisions, workflow expansion (#5)
  Add desktop Home feed (#12)
  Add desktop Playwright e2e harness (#11)
  Update desktop icon and persist window state (#9)
  feat: add channel creation flow (#8)
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