feat(nostr): switch to gift-wrapped events (#6677)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
tionis
2026-01-12 04:23:52 +01:00
committed by GitHub
parent b02d6792de
commit 0981fee9b2
3 changed files with 18 additions and 24 deletions

2
package-lock.json generated
View File

@@ -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",

View File

@@ -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",

View File

@@ -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