Migrate private messaging from NIP-04 to NIP-17 #1

Closed
opened 2026-01-01 20:43:33 +00:00 by padreug · 1 comment
Owner

Problem

NIP-04 (the original Nostr direct message scheme) leaks significant metadata:

  • Recipient's public key is visible in event tags
  • Makes it possible to infer who is communicating with whom
  • Uses outdated encryption (AES-256-CBC with non-hashed ECDH key)
  • No forward secrecy

Solution

Migrate to NIP-17 which combines:

  • NIP-44 encryption (ChaCha20 with HKDF-derived conversation key)
  • NIP-59 gift-wrapping to hide the encrypted message inside another event

Privacy benefits of NIP-17:

  • No metadata leak - Participant identities are hidden
  • Hidden timestamps - Each message's real date/time is concealed
  • Hidden event kinds - Event types are obscured
  • Hidden tags - Other event tags are not visible publicly

Implementation Approach

Backwards compatible with deprecation warning:

  • Support receiving both NIP-04 (old) and NIP-17 (new) messages
  • Send new messages using NIP-17
  • Display a warning when receiving NIP-04 messages indicating the sender is using an outdated/less private protocol

Both codebases use private messaging and should be updated to ensure interoperability.

References

## Problem NIP-04 (the original Nostr direct message scheme) leaks significant metadata: - Recipient's public key is visible in event tags - Makes it possible to infer who is communicating with whom - Uses outdated encryption (AES-256-CBC with non-hashed ECDH key) - No forward secrecy ## Solution Migrate to **NIP-17** which combines: - **NIP-44** encryption (ChaCha20 with HKDF-derived conversation key) - **NIP-59** gift-wrapping to hide the encrypted message inside another event ### Privacy benefits of NIP-17: - **No metadata leak** - Participant identities are hidden - **Hidden timestamps** - Each message's real date/time is concealed - **Hidden event kinds** - Event types are obscured - **Hidden tags** - Other event tags are not visible publicly ## Implementation Approach **Backwards compatible with deprecation warning:** - Support receiving both NIP-04 (old) and NIP-17 (new) messages - Send new messages using NIP-17 - Display a warning when receiving NIP-04 messages indicating the sender is using an outdated/less private protocol ## Related - aiolabs/webapp#10 - Same migration needed in the webapp Both codebases use private messaging and should be updated to ensure interoperability. ## References - [NIP-17 specification](https://www.e2encrypted.com/nostr/nips/17/) - [NIP-44 PR](https://github.com/nostr-protocol/nips/pull/574) - [NIP-59 improved direct messages](https://github.com/nostr-protocol/nips/pull/351) - [Nostr NIPs repository](https://github.com/nostr-protocol/nips)
Author
Owner

Done in PR #2 (merged), shipping in v1.1.0-aio.2.

Highlights:

  • Full NIP-17 messaging: kind 1059 gift wraps with NIP-44 v2 encryption + NIP-59 wrapping
  • New nostr/nip44.py + nostr/nip59.py (44 unit tests, validated against the NIP-44 spec test vector)
  • Subscription filter migrated from kind 4 → kind 1059
  • _handle_nip04_message replaced with _handle_gift_wrap; merchant↔customer DMs all flow through wrap_message / unwrap_message
  • Eliminates the merchant-as-customer routing bug previously patched by commit d3229cd (NIP-59's layered envelope is sender/recipient unambiguous by design)
  • Bonus: account-keypair-based auto-provisioning, default stall publishing, key-rotation migration feature, subscription health monitor

Companion changes:

  • aiolabs/webapp#39 — customer client uses nip59.wrapEvent() for orders
  • aiolabs/lnbits#11_create_default_merchant now delegates to nostrmarket.services.provision_merchant()

End-to-end flow verified locally on regtest (full purchase: order → invoice → payment → status update, all gift-wrapped).

No NIP-04 fallback retained — clean break, per the discussion in the PR.

Done in PR #2 (merged), shipping in `v1.1.0-aio.2`. Highlights: - Full NIP-17 messaging: kind 1059 gift wraps with NIP-44 v2 encryption + NIP-59 wrapping - New `nostr/nip44.py` + `nostr/nip59.py` (44 unit tests, validated against the NIP-44 spec test vector) - Subscription filter migrated from kind 4 → kind 1059 - `_handle_nip04_message` replaced with `_handle_gift_wrap`; merchant↔customer DMs all flow through `wrap_message` / `unwrap_message` - Eliminates the merchant-as-customer routing bug previously patched by commit `d3229cd` (NIP-59's layered envelope is sender/recipient unambiguous by design) - Bonus: account-keypair-based auto-provisioning, default stall publishing, key-rotation migration feature, subscription health monitor Companion changes: - aiolabs/webapp#39 — customer client uses `nip59.wrapEvent()` for orders - aiolabs/lnbits#11 — `_create_default_merchant` now delegates to `nostrmarket.services.provision_merchant()` End-to-end flow verified locally on regtest (full purchase: order → invoice → payment → status update, all gift-wrapped). No NIP-04 fallback retained — clean break, per the discussion in the PR.
Sign in to join this conversation.
No labels
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/nostrmarket#1
No description provided.