chat: migrate from NIP-04 to NIP-17 (NIP-44 + NIP-59 gift wrap) #40

Open
opened 2026-05-03 10:35:07 +00:00 by padreug · 0 comments
Owner

Context

PR #39 migrated the market module order DMs from NIP-04 (kind 4, AES-256-CBC) to NIP-17 (kind 1059 gift wrap, NIP-44 v2 + NIP-59). The companion nostrmarket extension refactor (aiolabs/nostrmarket#2) refused to handle kind 4 anymore, so the market had to upgrade.

The chat module (src/modules/chat/services/chat-service.ts) still uses NIP-04 for general user-to-user direct messages. NIP-04 is deprecated:

  • Metadata leaks (sender + recipient pubkeys, timestamps all public)
  • No deniability (signed events)
  • Weaker AES-256-CBC encryption
  • Out of step with modern Nostr clients (most have moved to NIP-17)

Scope

Migrate chat-service to NIP-17:

  1. Sending: replace nip04.encrypt() + kind: 4 finalizeEvent() (around chat-service.ts:364–367) with nip59.wrapEvent() producing kind 1059. The rumor uses kind: 14 per the NIP-17 spec.

  2. Receiving: replace the kind 4 subscription filters (chat-service.ts:528, 536, 634, 812, 819) with {kinds: [1059], '#p': [userPubkey]}. Replace nip04.decrypt() (chat-service.ts:575, 741) with nip59.unwrapEvent().

  3. Re-attach market routing: once chat is on kind 1059, the market module can hand off its order gift-wrap subscription back to chat-service via setMarketMessageHandler(handleOrderDM) — chat-service receives all 1059 events for the user and routes by content.

    useMarket.registerMarketMessageHandler() (composables/useMarket.ts:62–80) currently subscribes directly to kind 1059. There's a comment in that file noting this should revert once chat upgrades. Either:

    • Detect the chat service capability and prefer it over the direct subscription, or
    • Have chat-service register a routing function that checks if the unwrapped rumor's content has the nostrmarket type field (0/1/2) and forwards to the market handler.

References

Notes

  • Self-archive pattern: when sending, also wrap a copy to the sender's own pubkey so the sender's other devices can see their outgoing messages from relays. Backend uses this pattern (see services.py send_dm); see also nip59.wrapManyEvents() in nostr-tools.
  • Kind 10050 (DM relay list) discovery is part of full NIP-17; the backend doesn't publish it yet either. Out of scope for this issue but worth noting.
  • Consider whether to retain a transitional NIP-04 receive path so messages from older clients still arrive while peers upgrade. (No backwards compat for sending — modern clients all support NIP-17.)
## Context PR #39 migrated the **market module** order DMs from NIP-04 (kind 4, AES-256-CBC) to NIP-17 (kind 1059 gift wrap, NIP-44 v2 + NIP-59). The companion nostrmarket extension refactor ([aiolabs/nostrmarket#2](https://git.atitlan.io/aiolabs/nostrmarket/pulls/2)) refused to handle kind 4 anymore, so the market had to upgrade. The **chat module** (`src/modules/chat/services/chat-service.ts`) still uses NIP-04 for general user-to-user direct messages. NIP-04 is deprecated: - Metadata leaks (sender + recipient pubkeys, timestamps all public) - No deniability (signed events) - Weaker AES-256-CBC encryption - Out of step with modern Nostr clients (most have moved to NIP-17) ## Scope Migrate `chat-service` to NIP-17: 1. **Sending**: replace `nip04.encrypt()` + `kind: 4` `finalizeEvent()` (around chat-service.ts:364–367) with `nip59.wrapEvent()` producing kind 1059. The rumor uses `kind: 14` per the NIP-17 spec. 2. **Receiving**: replace the kind 4 subscription filters (chat-service.ts:528, 536, 634, 812, 819) with `{kinds: [1059], '#p': [userPubkey]}`. Replace `nip04.decrypt()` (chat-service.ts:575, 741) with `nip59.unwrapEvent()`. 3. **Re-attach market routing**: once chat is on kind 1059, the market module can hand off its order gift-wrap subscription back to chat-service via `setMarketMessageHandler(handleOrderDM)` — chat-service receives all 1059 events for the user and routes by content. `useMarket.registerMarketMessageHandler()` (composables/useMarket.ts:62–80) currently subscribes directly to kind 1059. There's a comment in that file noting this should revert once chat upgrades. Either: - Detect the chat service capability and prefer it over the direct subscription, or - Have chat-service register a routing function that checks if the unwrapped rumor's content has the nostrmarket `type` field (0/1/2) and forwards to the market handler. ## References - NIP-17 spec: https://github.com/nostr-protocol/nips/blob/master/17.md - NIP-44 spec: https://github.com/nostr-protocol/nips/blob/master/44.md - NIP-59 spec: https://github.com/nostr-protocol/nips/blob/master/59.md - `nostr-tools` 2.10.4 already exposes `nip59.wrapEvent()`, `nip59.unwrapEvent()`, `nip44.encrypt()`, `nip44.decrypt()` — no dep change needed. - Reference implementation in our codebase: PR #39 (`src/modules/market/services/nostrmarketService.ts` and `src/modules/market/composables/useMarket.ts`). ## Notes - Self-archive pattern: when sending, also wrap a copy to the sender's own pubkey so the sender's other devices can see their outgoing messages from relays. Backend uses this pattern (see `services.py` `send_dm`); see also `nip59.wrapManyEvents()` in nostr-tools. - Kind 10050 (DM relay list) discovery is part of full NIP-17; the backend doesn't publish it yet either. Out of scope for this issue but worth noting. - Consider whether to retain a transitional NIP-04 receive path so messages from older clients still arrive while peers upgrade. (No backwards compat for sending — modern clients all support NIP-17.)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/webapp#40
No description provided.