2.9 KiB
Group Messaging
What ships today (NIP-17 group DMs)
SkateSpots group chat uses the NIP-17 fan-out primitive — the same encryption path as 1:1 DMs, just addressed to more than one person:
- The sender builds one kind-14 rumor with a
ptag per recipient, then encrypts that same rumor to each recipient separately inside a kind-13 seal, wrapped in a kind-1059 gift wrap addressed only to them. - Anyone receiving a rumor with more than one
ptag is in a group. - A group is identified by
getGroupId(participants)— the sorted, joined participant pubkeys — so every client derives the same id.
Implementation:
| Piece | File |
|---|---|
| Send (1:1 or group) | useDirectMessages.ts → useSendDirectMessage (createRumor) |
| List groups (inbox) | useDirectMessages.ts → useGroupConversations |
| Group thread | useDirectMessages.ts → useGroupConversation(participants) |
| Thread UI | pages/GroupConversationPage.tsx → /messages/group/:participants |
| Create a group | components/NewMessageDialog.tsx (multi-select → Start group) |
Honest limitations
This primitive is simple and interoperable, but it is not a modern group protocol:
- No forward secrecy / post-compromise security. One leaked message key can expose that message (and, with a leaked identity key, the whole history).
- No membership ratchet or roster. "Membership" is just whoever is in the
ptags; there is no cryptographic join/leave, and no automatic rekey when someone is removed. - Fan-out cost is O(n) per message (one sealed copy per recipient).
- Metadata (timing, group size) is visible to relays.
Good enough for casual crew/community chats. Not appropriate for high-stakes or large public rooms.
Upgrade paths (when the guarantees matter)
From Encrypted Group Chat on Nostr: Marmot, Concord, and Cordn Compared (https://nostrdev.com/blog/nostr-group-chat-protocols/):
| Protocol | Crypto | Use when |
|---|---|---|
| Marmot | MLS (RFC 9420), no privileged server | Small, high-stakes groups needing forward secrecy + post-compromise security; the most mature spec and a flagship Rust implementation (MDK) |
| Concord | Shared key + NIP-59 giftwrap, signed roster | Large, public, Discord-style communities (channels/roles/moderation). Trades forward secrecy for scale |
| Cordn | MLS via a per-group coordinator (ContextVM) | MLS guarantees where running/finding a small self-hostable coordinator is acceptable; best public-metadata story |
Migration would be additive: keep the NIP-17 threads for casual DMs and adopt one of the above for rooms that need ratcheting, rosters and moderation. Revisit when the target protocol's spec has stabilised.
Decision
Ship NIP-17 group DMs now (zero new protocols, reuses the DM stack); adopt Marmot if/when teams need Signal-grade guarantees, and Concord if the goal becomes large public communities. See the article above for the trade-offs.