From 0981fee9b2eced3b9098dcc32e70c2666ba5bb13 Mon Sep 17 00:00:00 2001 From: tionis Date: Mon, 12 Jan 2026 04:23:52 +0100 Subject: [PATCH] feat(nostr): switch to gift-wrapped events (#6677) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- package-lock.json | 2 +- package.json | 2 +- server/notification-providers/nostr.js | 38 +++++++++++--------------- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 02c0071fa..568f5ba30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "node-fetch-cache": "^5.1.0", "node-radius-utils": "~1.2.0", "nodemailer": "~7.0.12", - "nostr-tools": "^2.10.4", + "nostr-tools": "^2.17.0", "notp": "~2.0.3", "openid-client": "^5.4.2", "password-hash": "~1.2.2", diff --git a/package.json b/package.json index 496c185c1..1b6a10bfc 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "node-fetch-cache": "^5.1.0", "node-radius-utils": "~1.2.0", "nodemailer": "~7.0.12", - "nostr-tools": "^2.10.4", + "nostr-tools": "^2.17.0", "notp": "~2.0.3", "openid-client": "^5.4.2", "password-hash": "~1.2.2", diff --git a/server/notification-providers/nostr.js b/server/notification-providers/nostr.js index 0161332f3..1dc1e0897 100644 --- a/server/notification-providers/nostr.js +++ b/server/notification-providers/nostr.js @@ -1,17 +1,8 @@ const NotificationProvider = require("./notification-provider"); -const { finalizeEvent, Relay, kinds, nip04, nip19 } = require("nostr-tools"); +const { finalizeEvent, Relay, nip19, nip59 } = require("nostr-tools"); -// polyfills for node versions -const semver = require("semver"); -const nodeVersion = process.version; -if (semver.lt(nodeVersion, "20.0.0")) { - // polyfills for node 18 - global.crypto = require("crypto"); - global.WebSocket = require("isomorphic-ws"); -} else { - // polyfills for node 20 - global.WebSocket = require("isomorphic-ws"); -} +// polyfill WebSocket for nostr-tools +global.WebSocket = require("isomorphic-ws"); class Nostr extends NotificationProvider { name = "nostr"; @@ -20,24 +11,27 @@ class Nostr extends NotificationProvider { * @inheritdoc */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { - // All DMs should have same timestamp - const createdAt = Math.floor(Date.now() / 1000); - const senderPrivateKey = await this.getPrivateKey(notification.sender); const recipientsPublicKeys = await this.getPublicKeys(notification.recipients); - // Create NIP-04 encrypted direct message event for each recipient + // Create NIP-59 gift-wrapped events for each recipient + // This uses NIP-17 kind 14 (private direct message) wrapped with NIP-59 + // to prevent metadata leakage (sender/recipient public keys are hidden) + const createdAt = Math.floor(Date.now() / 1000); const events = []; for (const recipientPublicKey of recipientsPublicKeys) { - const ciphertext = await nip04.encrypt(senderPrivateKey, recipientPublicKey, msg); - let event = { - kind: kinds.EncryptedDirectMessage, + const event = { + kind: 14, // NIP-17 private direct message created_at: createdAt, tags: [["p", recipientPublicKey]], - content: ciphertext, + content: msg, }; - const signedEvent = finalizeEvent(event, senderPrivateKey); - events.push(signedEvent); + try { + const wrappedEvent = nip59.wrapEvent(event, senderPrivateKey, recipientPublicKey); + events.push(wrappedEvent); + } catch (error) { + throw new Error(`Failed to create gift-wrapped event for recipient: ${error.message}`); + } } // Publish events to each relay