diff --git a/src/commands/start.ts b/src/commands/start.ts index 0c7f835..45d13c2 100644 --- a/src/commands/start.ts +++ b/src/commands/start.ts @@ -3,9 +3,15 @@ import { DomainConfig, IConfig, getCurrentConfig, saveCurrentConfig } from '../c import { decryptNsec } from '../config/keys.js'; import { fork } from 'child_process'; import { resolve } from 'path'; -import NDK, { NDKAppHandlerEvent, NDKKind, NDKPrivateKeySigner, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk'; +import { finalizeEvent, getPublicKey, nip05, nip19 } from 'nostr-tools'; +import { RelayPool } from '../daemon/lib/relay-pool.js'; +import { secretKeyBytes } from '../daemon/nip46/transport.js'; import { debug } from 'console'; +// NIP-89 application-handler event kind, and the handled NIP-46 client kind. +const APP_HANDLER_KIND = 31990; +const NOSTR_CONNECT_KIND_STR = '24133'; + interface IOpts { keys: string[]; verbose: boolean; @@ -16,29 +22,26 @@ interface IOpts { async function nip89announcement(configData: IConfig) { const domains = configData.domains as Record; if (!domains) return; + + const sk = secretKeyBytes(configData.admin.key); + const signerPubkey = getPublicKey(sk); + for (const [ domain, config ] of Object.entries(domains)) { - const hasNip89 = !!config.nip89; - if (!hasNip89) continue; + if (!config.nip89) continue; - const signer = new NDKPrivateKeySigner(configData.admin.key); - const signerUser = await signer.user(); - - const profile = config.nip89!.profile; - const relays = config.nip89!.relays; - const nip05 = `_@${domain}`; - - const ndk = new NDK({explicitRelayUrls: relays}); + const profile = config.nip89.profile; + const relays = config.nip89.relays; + const nip05addr = `_@${domain}`; // make sure the nip05 correctly points to this pubkey - const uservianip05 = await NDKUser.fromNip05(nip05, ndk); - if (!uservianip05 || uservianip05.pubkey !== signerUser.pubkey) { - console.log(`❌ ${nip05} does not point to this nsecbunker's key`); - if (uservianip05) { - console.log(`${nip05} points to ${uservianip05.pubkey} instead of ${signerUser.pubkey}`) + const resolved = await nip05.queryProfile(nip05addr).catch(() => null); + if (!resolved || resolved.pubkey !== signerPubkey) { + console.log(`❌ ${nip05addr} does not point to this nsecbunker's key`); + if (resolved) { + console.log(`${nip05addr} points to ${resolved.pubkey} instead of ${signerPubkey}`) } else { - console.log(`${nip05} needs to point to ${signerUser.pubkey}`) + console.log(`${nip05addr} needs to point to ${signerPubkey}`) } - continue } @@ -55,52 +58,47 @@ async function nip89announcement(configData: IConfig) { const hasWallet = !!config.wallet; const hasNostrdress = !!config.wallet?.lnbits?.nostdressUrl; - ndk.signer = signer; - ndk.connect(5000).then(async () => { - const event = new NDKAppHandlerEvent(ndk, { - tags: [ - [ "alt", "This is an nsecBunker announcement" ] - ] - } as NostrEvent); - - const operator = config.nip89!.operator; - if (operator) { - try { - const opUser = new NDKUser({npub: operator}); - event.tags.push(["p", opUser.pubkey]); - } catch {} - } - + // kind:31990 (NIP-89) is addressable by (kind, author, d-tag); this code + // always uses the default d-tag "24133", so publishing replaces the prior + // announcement — the same effect the old fetch-existing-d-tag dance had. + const tags: string[][] = [ + ["alt", "This is an nsecBunker announcement"], + ["d", NOSTR_CONNECT_KIND_STR], + ["k", NOSTR_CONNECT_KIND_STR], + ]; + const operator = config.nip89.operator; + if (operator) { try { - const user = await ndk.signer!.user(); - const existingEvent = await ndk.fetchEvent({ - authors: [user.pubkey], - kinds: [NDKKind.AppHandler], - "#k": [NDKKind.NostrConnect.toString()] - }); + tags.push(["p", nip19.decode(operator).data as string]); + } catch { /* ignore a bad operator npub */ } + } + if (hasWallet && hasNostrdress) { + tags.push(["f", "wallet"]); + tags.push(["f", "zaps"]); + } - if (existingEvent) { - debug(`🔍 Found existing NIP-89 announcement for ${domain}:`, existingEvent.encode()); - // update existing event - const dTag = existingEvent.tagValue("d"); - event.tags.push(["d", dTag!]) - } else { - debug(`🔍 No existing NIP-89 announcement for ${domain} found.`); - event.tags.push(["d", NDKKind.NostrConnect.toString()]); - } + profile.nip05 = nip05addr; + const event = finalizeEvent( + { + kind: APP_HANDLER_KIND, + created_at: Math.floor(Date.now() / 1000), + tags, + content: JSON.stringify(profile), + }, + sk, + ); - profile.nip05 = nip05; - event.content = JSON.stringify(profile); - event.tags.push(["k", NDKKind.NostrConnect.toString()]) - if (hasWallet && hasNostrdress) { - // add wallet and zaps feature tags - event.tags.push(["f", "wallet"]); - event.tags.push(["f", "zaps"]); - } - await event.publish(); - debug(`✅ Published NIP-89 announcement for ${domain}:`, event.encode()); - } catch(e: any) { console.log(`❌ Failed to publish NIP-89 announcement for ${domain}!`, e.message); } - }) + const pool = new RelayPool(relays, {}); + pool.start(); + await new Promise((r) => setTimeout(r, 3000)); + try { + await pool.publish(event); + debug(`✅ Published NIP-89 announcement for ${domain}`); + } catch (e: any) { + console.log(`❌ Failed to publish NIP-89 announcement for ${domain}!`, e.message); + } finally { + pool.stop(); + } } } diff --git a/src/config/index.ts b/src/config/index.ts index db819f6..392c196 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,10 +1,10 @@ import { readFileSync, writeFileSync } from 'fs'; -import { NDKPrivateKeySigner, NDKUserProfile } from '@nostr-dev-kit/ndk'; +import { generateSecretKey } from 'nostr-tools'; import { IAdminOpts } from '../daemon/admin'; import { version } from '../../package.json'; -const generatedKey = NDKPrivateKeySigner.generate(); +const generatedKeyHex = Buffer.from(generateSecretKey()).toString('hex'); export type LNBitsWalletConfig = { url: string, @@ -54,7 +54,7 @@ const defaultConfig: IConfig = { adminRelays: [ "wss://relay.nsecbunker.com" ], - key: generatedKey.privateKey!, + key: generatedKeyHex, notifyAdminsOnBoot: true, }, database: 'sqlite://nsecbunker.db', diff --git a/src/daemon/admin/commands/create_account.ts b/src/daemon/admin/commands/create_account.ts index 6a2d561..7a732cf 100644 --- a/src/daemon/admin/commands/create_account.ts +++ b/src/daemon/admin/commands/create_account.ts @@ -1,7 +1,8 @@ -import { Hexpubkey, NDKPrivateKeySigner, NDKUserProfile } from "@nostr-dev-kit/ndk"; import { AdminRpcRequest } from "../types.js"; import AdminInterface from ".."; -import { nip19 } from 'nostr-tools'; +import { nip19, generateSecretKey, getPublicKey } from 'nostr-tools'; +import type { SkeletonProfile } from "../../lib/profile.js"; +type Hexpubkey = string; import { setupSkeletonProfile } from "../../lib/profile"; import { IConfig, getCurrentConfig, saveCurrentConfig } from "../../../config"; import { readFileSync, writeFileSync } from "fs"; @@ -161,20 +162,20 @@ export async function createAccountReal( await validate(currentConfig, username, domain, email); const nip05 = `${username}@${domain}`; - const key = NDKPrivateKeySigner.generate(); - const profile: NDKUserProfile = { + const sk = generateSecretKey(); + const pubkey = getPublicKey(sk); + const npub = nip19.npubEncode(pubkey); + const profile: SkeletonProfile = { display_name: username, name: username, nip05, ...(domainConfig.defaultProfile || {}) }; - const generatedUser = await key.user(); - - debug(`Created user ${generatedUser.npub} for ${nip05}`); + debug(`Created user ${npub} for ${nip05}`); // Add NIP-05 - await addNip05(currentConfig, username, domain, generatedUser.pubkey); + await addNip05(currentConfig, username, domain, pubkey); debug(`Added NIP-05 for ${nip05}`); @@ -182,7 +183,7 @@ export async function createAccountReal( if (domainConfig.wallet) { generateWallet( domainConfig.wallet, - username, domain, generatedUser.npub + username, domain, npub ).then((lnaddress) => { debug(`wallet for ${nip05}`, {lnaddress}); if (lnaddress) profile.lud16 = lnaddress; @@ -190,23 +191,23 @@ export async function createAccountReal( debug(`error generating wallet for ${nip05}`, e); }).finally(() => { debug(`saving profile for ${nip05}`, profile); - setupSkeletonProfile(key, profile, email); + setupSkeletonProfile(sk, profile, email); }) } else { debug(`no wallet configuration for ${domain}`); // Create user profile - setupSkeletonProfile(key, profile, email); + setupSkeletonProfile(sk, profile, email); } const keyName = nip05; - const nsec = key.nsec; - currentConfig.keys[keyName] = { key: key.privateKey }; + const nsec = nip19.nsecEncode(sk); + currentConfig.keys[keyName] = { key: Buffer.from(sk).toString('hex') }; saveCurrentConfig(admin.configFile, currentConfig); await admin.loadNsec!(keyName, nsec); - await prisma.key.create({ data: { keyName, pubkey: generatedUser.pubkey } }); + await prisma.key.create({ data: { keyName, pubkey } }); // Immediately grant access to the creator key // This means that the client creating this account can immediately @@ -219,8 +220,8 @@ export async function createAccountReal( // request's kind so the response goes back on the same channel the // client subscribed for. Filed as part of aiolabs/nsecbunkerd#7 // diagnosis 2026-05-27. - const originalKind = req.event.kind!; - return admin.rpc.sendResponse(req.id, req.pubkey, generatedUser.pubkey, originalKind); + const originalKind = req.event.kind; + return admin.rpc.sendResponse(req.id, req.pubkey, pubkey, originalKind); } catch (e: any) { console.trace('error', e); const originalKind = req.event.kind!; diff --git a/src/daemon/admin/commands/create_new_key.ts b/src/daemon/admin/commands/create_new_key.ts index c9182ed..75c43f9 100644 --- a/src/daemon/admin/commands/create_new_key.ts +++ b/src/daemon/admin/commands/create_new_key.ts @@ -1,4 +1,4 @@ -import NDK, { NDKEvent, NDKPrivateKeySigner, type NostrEvent } from "@nostr-dev-kit/ndk"; +import { generateSecretKey, getPublicKey, nip19 } from "nostr-tools"; import { AdminRpcRequest } from "../types.js"; import AdminInterface from "../index.js"; import { NIP46_ADMIN_RESPONSE_KIND } from "../kinds.js"; @@ -6,6 +6,7 @@ import { saveEncrypted } from "../../../commands/add.js"; import { getCurrentConfig } from "../../../config/index.js"; import { decryptNsec } from "../../../config/keys.js"; import { setupSkeletonProfile } from "../../lib/profile.js"; +import { secretKeyBytes } from "../../nip46/transport.js"; export default async function createNewKey(admin: AdminInterface, req: AdminRpcRequest) { const [ keyName, passphrase, _nsec ] = req.params as [ string, string, string? ]; @@ -44,27 +45,26 @@ export default async function createNewKey(admin: AdminInterface, req: AdminRpcR `decrypt it; refusing to overwrite (${e.message})`, ); } - const existingUser = await new NDKPrivateKeySigner(existingNsec).user(); - const result = JSON.stringify({ npub: existingUser.npub }); + const existingNpub = nip19.npubEncode(getPublicKey(secretKeyBytes(existingNsec))); + const result = JSON.stringify({ npub: existingNpub }); return admin.rpc.sendResponse(req.id, req.pubkey, result, NIP46_ADMIN_RESPONSE_KIND); } - let key; + let sk: Uint8Array; if (_nsec) { - // NDK 3.x's `NDKPrivateKeySigner` accepts nsec1 or hex directly - // (see core/src/signers/private-key/index.ts `@ai-guardrail`). - key = new NDKPrivateKeySigner(_nsec); + // secretKeyBytes accepts nsec1 or hex directly. + sk = secretKeyBytes(_nsec); } else { - key = NDKPrivateKeySigner.generate(); + sk = generateSecretKey(); - setupSkeletonProfile(key); + setupSkeletonProfile(sk); console.log(`setting up skeleton profile for ${keyName}`); } - const user = await key.user(); - const nsec = key.nsec; + const npub = nip19.npubEncode(getPublicKey(sk)); + const nsec = nip19.nsecEncode(sk); await saveEncrypted( admin.configFile, @@ -76,7 +76,7 @@ export default async function createNewKey(admin: AdminInterface, req: AdminRpcR await admin.loadNsec(keyName, nsec); const result = JSON.stringify({ - npub: user.npub, + npub, }); return admin.rpc.sendResponse(req.id, req.pubkey, result, NIP46_ADMIN_RESPONSE_KIND); diff --git a/src/daemon/admin/index.ts b/src/daemon/admin/index.ts index 633b050..a54ad60 100644 --- a/src/daemon/admin/index.ts +++ b/src/daemon/admin/index.ts @@ -1,4 +1,3 @@ -import NDK, { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; import { getPublicKey, nip19 } from 'nostr-tools'; import createDebug from 'debug'; import { Key, KeyUser } from '../run'; @@ -122,19 +121,19 @@ class AdminInterface { /** * Boot-time DM to the admin npubs. One-shot, best-effort notification over - * public relays — not part of the reconnect-sensitive RPC path, so it still - * uses a throwaway NDK + the existing dmUser helper. (#42 leaves this on NDK.) + * public relays via a throwaway RelayPool (aiolabs/nsecbunkerd#44). */ private async notifyAdminsOfNewConnection(connectionString: string) { - const blastrNdk = new NDK({ - explicitRelayUrls: ['wss://blastr.f7z.xyz', 'wss://nostr.mutinywallet.com'], - signer: new NDKPrivateKeySigner(this.adminNsec), - }); - await blastrNdk.connect(2500); + const sk = secretKeyBytes(this.adminNsec); + const pool = new RelayPool(['wss://blastr.f7z.xyz', 'wss://nostr.mutinywallet.com'], {}); + pool.start(); + // Give the connections a moment to come up before publishing. + await new Promise((r) => setTimeout(r, 2500)); for (const npub of this.npubs || []) { - dmUser(blastrNdk, npub, `nsecBunker has started; use ${connectionString} to connect to it and unlock your key(s)`); + await dmUser(sk, npub, `nsecBunker has started; use ${connectionString} to connect to it and unlock your key(s)`, pool); } + pool.stop(); } /** diff --git a/src/daemon/authorize.ts b/src/daemon/authorize.ts index 2b17744..a47c493 100644 --- a/src/daemon/authorize.ts +++ b/src/daemon/authorize.ts @@ -1,4 +1,5 @@ -import { Hexpubkey, NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk"; +import type { Event as NDKEvent } from "nostr-tools"; +type Hexpubkey = string; import type { Backend } from "./backend"; import prisma from "../db"; import type { Request } from "@prisma/client"; @@ -59,8 +60,11 @@ async function createRecord( ) { let params: string | undefined; - if (typeof param === 'object' && param !== null && 'rawEvent' in param) { - params = JSON.stringify(param.rawEvent()); + // `param` for sign_event is the parsed event object the signer passed to the + // ACL (a plain event since #43, no longer an NDKEvent with .rawEvent()); + // for the encrypt/decrypt methods it's the payload string. + if (typeof param === 'object' && param !== null) { + params = JSON.stringify(param); } else if (param) { params = param.toString(); } diff --git a/src/daemon/lib/acl/index.ts b/src/daemon/lib/acl/index.ts index 62de873..de54f70 100644 --- a/src/daemon/lib/acl/index.ts +++ b/src/daemon/lib/acl/index.ts @@ -1,4 +1,5 @@ -import type { NostrEvent, NIP46Method } from '@nostr-dev-kit/ndk'; +import type { Event as NostrEvent } from 'nostr-tools'; +import type { NIP46Method } from '../../nip46/types.js'; import prisma from '../../../db.js'; import { liveWhere } from './lifecycle.js'; diff --git a/src/daemon/lib/profile.ts b/src/daemon/lib/profile.ts index f433452..c30b8ac 100644 --- a/src/daemon/lib/profile.ts +++ b/src/daemon/lib/profile.ts @@ -1,6 +1,7 @@ -import NDK, { NDKEvent, NDKPrivateKeySigner, NostrEvent, type NDKUserProfile } from "@nostr-dev-kit/ndk"; +import { finalizeEvent, getPublicKey } from "nostr-tools"; import * as CryptoJS from 'crypto-js'; import createDebug from "debug"; +import { RelayPool } from "./relay-pool.js"; const debug = createDebug("nsecbunker:profile"); @@ -12,14 +13,27 @@ const explicitRelayUrls = [ "wss://nostr.mutinywallet.com" ]; +/** The handful of profile (kind:0) fields the skeleton sets. */ +export interface SkeletonProfile { + display_name?: string; + about?: string; + website?: string; + image?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [k: string]: any; +} + /** - * Setup a skeleton profile for a new key since - * the experience of a completely empty profile - * is pretty bad when logging in with Coracle. + * Setup a skeleton profile for a new key since the experience of a completely + * empty profile is pretty bad when logging in with Coracle. * + * Ported off NDK onto nostr-tools + a throwaway RelayPool (aiolabs/nsecbunkerd#44). + * Fire-and-forget, like before: the caller doesn't await it. + * + * @param sk - the new key's secret bytes * @param email - if provided, will fetch the gravatar */ -export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: NDKUserProfile, email?: string) { +export async function setupSkeletonProfile(sk: Uint8Array, profile?: SkeletonProfile, email?: string) { const rand = Math.random().toString(36).substring(7); profile ??= {}; profile.display_name ??= 'New User via nsecBunker'; @@ -39,49 +53,54 @@ export async function setupSkeletonProfile(key: NDKPrivateKeySigner, profile?: N } } - const user = await key.user(); - const ndk = new NDK({ - explicitRelayUrls, - signer: key - }); + const pubkey = getPublicKey(sk); + const now = () => Math.floor(Date.now() / 1000); - await ndk.connect(2500); - user.ndk = ndk; + const pool = new RelayPool(explicitRelayUrls, {}); + pool.start(); + // Give the connections a moment to come up (NDK used connect(2500)). + await new Promise((r) => setTimeout(r, 2500)); - let event = new NDKEvent(ndk, { - kind: 0, - content: JSON.stringify(profile), - pubkey: user.pubkey, - } as NostrEvent); - await event.sign(key); - - const t = await event.publish(); - debug(`Published to ${t.size} relays`); - - event = new NDKEvent(ndk, { - kind: 3, - tags: [ - ['p', 'fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52'], - ['p', user.pubkey], - ], - pubkey: user.pubkey, - } as NostrEvent); - await event.sign(key); - debug(`follow list event`, event.rawEvent()); - await event.publish(); - - const relays = new NDKEvent(ndk, { - kind: 10002, - tags: [ - ['r', 'wss://purplepag.es'], - ['r', 'wss://relay.f7z.io'], - ['r', 'wss://relay.damus.io'], - ['r', 'wss://relayable.org'], - ['r', 'wss://relay.nostr.band'], - ['r', 'wss://relay.primal.net'], - ], - pubkey: user.pubkey, - } as NostrEvent); - await relays.sign(key); - await relays.publish(); -} \ No newline at end of file + try { + await pool.publish( + finalizeEvent({ kind: 0, created_at: now(), tags: [], content: JSON.stringify(profile) }, sk), + ); + await pool.publish( + finalizeEvent( + { + kind: 3, + created_at: now(), + tags: [ + ['p', 'fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52'], + ['p', pubkey], + ], + content: '', + }, + sk, + ), + ); + await pool.publish( + finalizeEvent( + { + kind: 10002, + created_at: now(), + tags: [ + ['r', 'wss://purplepag.es'], + ['r', 'wss://relay.f7z.io'], + ['r', 'wss://relay.damus.io'], + ['r', 'wss://relayable.org'], + ['r', 'wss://relay.nostr.band'], + ['r', 'wss://relay.primal.net'], + ], + content: '', + }, + sk, + ), + ); + debug('published skeleton profile (kind 0/3/10002)'); + } catch (e) { + debug('error publishing skeleton profile', e); + } finally { + pool.stop(); + } +} diff --git a/src/daemon/nip46/types.ts b/src/daemon/nip46/types.ts index 33e36c7..e2bf30b 100644 --- a/src/daemon/nip46/types.ts +++ b/src/daemon/nip46/types.ts @@ -2,7 +2,7 @@ * Local NIP-46 types (aiolabs/nsecbunkerd#42). * * These replace the identically-named types we used to import from - * `@nostr-dev-kit/ndk`, so the daemon's signing path no longer depends on NDK. + * NDK, so the daemon's signing path no longer depends on NDK. * Kept structurally identical to NDK's so the ACL callback * (`signingAuthorizationCallback`) and the admin layer don't have to change. */ diff --git a/src/daemon/run.ts b/src/daemon/run.ts index 3ba08a0..0d9c045 100644 --- a/src/daemon/run.ts +++ b/src/daemon/run.ts @@ -1,8 +1,8 @@ -import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk'; -import { nip19, utils as nostrUtils } from 'nostr-tools'; +import { nip19, getPublicKey, utils as nostrUtils } from 'nostr-tools'; import { Backend } from './backend/index.js'; import { applyToken } from './backend/token-store.js'; import { RelayPool } from './lib/relay-pool.js'; +import { secretKeyBytes } from './nip46/transport.js'; import type { Nip46PermitCallback, Nip46PermitCallbackParams } from './nip46/types.js'; import { checkIfPubkeyAllowed, recordSigning } from './lib/acl/index.js'; import AdminInterface from './admin/index.js'; @@ -38,10 +38,10 @@ function getKeys(config: DaemonConfig) { const keys: Key[] = []; for (const [name, nsec] of Object.entries(config.keys)) { - const user = await new NDKPrivateKeySigner(nsec).user(); + const npub = nip19.npubEncode(getPublicKey(secretKeyBytes(nsec))); const key = { name, - npub: user.npub, + npub, userCount: await prisma.keyUser.count({ where: { keyName: name } }), tokenCount: await prisma.token.count({ where: { keyName: name } }) }; diff --git a/src/utils/dm-user.ts b/src/utils/dm-user.ts index 0f922e4..db3dc06 100644 --- a/src/utils/dm-user.ts +++ b/src/utils/dm-user.ts @@ -1,23 +1,33 @@ -import NDK, { NDKUser, NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk"; +import { finalizeEvent, nip04, nip19 } from "nostr-tools"; +import type { RelayPool } from "../daemon/lib/relay-pool.js"; -export async function dmUser(ndk: NDK, recipient: NDKUser | string, content: string): Promise { - let targetUser; - - if (typeof recipient === 'string') { - targetUser = new NDKUser({ npub: recipient }); - } else if (recipient instanceof NDKUser) { - targetUser = recipient; - } - - const event = new NDKEvent(ndk, { kind: 4, content } as NostrEvent); - event.tag(targetUser); - await event.encrypt(targetUser); - await event.sign(); +/** + * Send a kind-4 NIP-04 DM to `recipient` (npub or hex) as `sk`, published via an + * already-started `pool`. Ported off NDK (aiolabs/nsecbunkerd#44) — used only for + * the one-shot boot notification to admins. + */ +export async function dmUser( + sk: Uint8Array, + recipient: string, + content: string, + pool: RelayPool, +): Promise { + const recipientHex = recipient.startsWith("npub1") + ? (nip19.decode(recipient).data as string) + : recipient; + const ciphertext = nip04.encrypt(sk, recipientHex, content); + const event = finalizeEvent( + { + kind: 4, + created_at: Math.floor(Date.now() / 1000), + tags: [["p", recipientHex]], + content: ciphertext, + }, + sk, + ); try { - await event.publish(); + await pool.publish(event); } catch (e) { console.log(e); } - - return event; }