Compare commits

..

No commits in common. "9f2f592f20586f3863d283d598857bd73dd89b0b" and "17540272496ed37e43580889e90786346e6cc082" have entirely different histories.

2 changed files with 21 additions and 42 deletions

View file

@ -110,13 +110,9 @@ class AdminInterface {
this.config().then((config) => { this.config().then((config) => {
if (config.admin?.notifyAdminsOnBoot) { if (config.admin?.notifyAdminsOnBoot) {
// .catch so a boot-DM failure can't surface as an unhandled this.notifyAdminsOfNewConnection(connectionString);
// rejection (process-terminating under Node defaults). #48 / CS-3.
this.notifyAdminsOfNewConnection(connectionString).catch((e) =>
console.log('notifyAdminsOfNewConnection failed:', e?.message ?? e),
);
} }
}).catch((e) => console.log('config() failed during admin init:', e?.message ?? e)); });
} }
public async config(): Promise<IConfig> { public async config(): Promise<IConfig> {
@ -131,26 +127,13 @@ class AdminInterface {
const sk = secretKeyBytes(this.adminNsec); const sk = secretKeyBytes(this.adminNsec);
const pool = new RelayPool(['wss://blastr.f7z.xyz', 'wss://nostr.mutinywallet.com'], {}); const pool = new RelayPool(['wss://blastr.f7z.xyz', 'wss://nostr.mutinywallet.com'], {});
pool.start(); pool.start();
// Wait until at least one relay is actually connected (capped), rather // Give the connections a moment to come up before publishing.
// than a fixed sleep — these external public relays can be slow to come await new Promise((r) => setTimeout(r, 2500));
// 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));
}
// try/finally so a throw mid-loop can't leak the pool's reconnect loops + for (const npub of this.npubs || []) {
// sockets for the process lifetime (#48 / review CS-3). dmUser itself is await dmUser(sk, npub, `nsecBunker has started; use ${connectionString} to connect to it and unlock your key(s)`, pool);
// 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();
} }
pool.stop();
} }
/** /**

View file

@ -12,26 +12,22 @@ export async function dmUser(
content: string, content: string,
pool: RelayPool, pool: RelayPool,
): Promise<void> { ): Promise<void> {
// Guard the whole thing: nip19.decode throws on a malformed npub (passes the const recipientHex = recipient.startsWith("npub1")
// startsWith check but fails the bech32 checksum), and that previously threw ? (nip19.decode(recipient).data as string)
// *before* the publish try, escaping the caller. Best-effort — never throw. : recipient;
// (review CS-3) 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 { 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); await pool.publish(event);
} catch (e) { } catch (e) {
console.log('dmUser failed for', recipient, '-', (e as any)?.message ?? e); console.log(e);
} }
} }