diff --git a/src/daemon/admin/index.ts b/src/daemon/admin/index.ts index 569e1c3..a54ad60 100644 --- a/src/daemon/admin/index.ts +++ b/src/daemon/admin/index.ts @@ -110,13 +110,9 @@ class AdminInterface { this.config().then((config) => { if (config.admin?.notifyAdminsOnBoot) { - // .catch so a boot-DM failure can't surface as an unhandled - // rejection (process-terminating under Node defaults). #48 / CS-3. - this.notifyAdminsOfNewConnection(connectionString).catch((e) => - console.log('notifyAdminsOfNewConnection failed:', e?.message ?? e), - ); + this.notifyAdminsOfNewConnection(connectionString); } - }).catch((e) => console.log('config() failed during admin init:', e?.message ?? e)); + }); } public async config(): Promise { @@ -131,26 +127,13 @@ class AdminInterface { const sk = secretKeyBytes(this.adminNsec); const pool = new RelayPool(['wss://blastr.f7z.xyz', 'wss://nostr.mutinywallet.com'], {}); pool.start(); - // Wait until at least one relay is actually connected (capped), rather - // than a fixed sleep — these external public relays can be slow to come - // up, and a too-short fixed wait made the DM publish fail on boot. Still - // best-effort: if none connect in time we fall through and dmUser logs - // the publish failure without affecting the daemon. (#48) - const deadline = Date.now() + 8000; - while (pool.connectedCount() === 0 && Date.now() < deadline) { - await new Promise((r) => setTimeout(r, 100)); - } + // Give the connections a moment to come up before publishing. + await new Promise((r) => setTimeout(r, 2500)); - // try/finally so a throw mid-loop can't leak the pool's reconnect loops + - // sockets for the process lifetime (#48 / review CS-3). dmUser itself is - // now fully guarded, but keep the finally as belt-and-suspenders. - try { - for (const npub of this.npubs || []) { - await dmUser(sk, npub, `nsecBunker has started; use ${connectionString} to connect to it and unlock your key(s)`, pool); - } - } finally { - pool.stop(); + for (const npub of this.npubs || []) { + 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/utils/dm-user.ts b/src/utils/dm-user.ts index 34c6add..db3dc06 100644 --- a/src/utils/dm-user.ts +++ b/src/utils/dm-user.ts @@ -12,26 +12,22 @@ export async function dmUser( content: string, pool: RelayPool, ): Promise { - // Guard the whole thing: nip19.decode throws on a malformed npub (passes the - // startsWith check but fails the bech32 checksum), and that previously threw - // *before* the publish try, escaping the caller. Best-effort — never throw. - // (review CS-3) + 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 { - 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, - ); await pool.publish(event); } catch (e) { - console.log('dmUser failed for', recipient, '-', (e as any)?.message ?? e); + console.log(e); } }