From 618a84ab96f1865f6112d696077390ba184dd9b3 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 3 Sep 2026 22:55:03 -0700 Subject: Nostr signature verification moves off the main thread: the pool's synchronous verifyEvent (a schnorr check per event, ~560ms on a relay backlog — the profiled carousel-stutter cause) is switched off, and every event is now gated through a Web Worker before any caller sees it. Nothing unverified is ever trusted — the worker runs the same nostr-tools verifyEvent, just on another thread, with a synchronous fallback if the worker is unavailable (standalone file://) or stalls. Bundled to dist/verify-worker.js, precached in the SW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr --- build.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'build.js') diff --git a/build.js b/build.js index e2c0217..e48c4f7 100644 --- a/build.js +++ b/build.js @@ -42,7 +42,7 @@ const SW = `const CACHE = 'cold-{{VERSION}}'; // module are available offline too. app-{{VERSION}}.js is the main bundle — // content-addressed, so a new deploy precaches a new name and the old cache // is dropped wholesale. -const SHELL = ['./', 'manifest.webmanifest', 'icon-192.png', 'icon-512.png', 'jsqr.js', 'nip46.js', 'app-{{VERSION}}.js'{{EXTRA_SHELL}}]; +const SHELL = ['./', 'manifest.webmanifest', 'icon-192.png', 'icon-512.png', 'jsqr.js', 'nip46.js', 'verify-worker.js', 'app-{{VERSION}}.js'{{EXTRA_SHELL}}]; self.addEventListener('install', (e) => { self.skipWaiting(); @@ -181,6 +181,22 @@ export async function buildNip46({ minify = true } = {}) { return iife(await result.outputs[0].text()); } +// Off-main-thread nostr event verification worker (classic Worker, its own +// bundle of nostr-tools verify + noble crypto). NOT wrapped in an IIFE — +// worker code runs at top level and needs its self.onmessage handler live. +export async function buildVerifyWorker({ minify = true } = {}) { + const result = await Bun.build({ + entrypoints: ['./src/verify-worker.js'], + target: 'browser', + minify, + }); + if (!result.success) { + for (const log of result.logs) console.error(log); + throw new Error('verify-worker bundle failed'); + } + return await result.outputs[0].text(); +} + // The notify-only push handler for builds without the ark+nwc features (the // full builds get the bundled auto-answering handler from src/sw-nwc.js). const SW_WAKE_ONLY = `self.addEventListener('push', (e) => { @@ -421,6 +437,7 @@ if (import.meta.main) { // Lazy-loaded QR decoder — kept out of index.html, fetched only when a // browser without BarcodeDetector opens the scanner. + await Bun.write('dist/verify-worker.js', await buildVerifyWorker()); await Bun.write('dist/jsqr.js', await buildJsQr()); // Lazy-loaded NIP-46 remote-signer client — fetched on first bunker login. await Bun.write('dist/nip46.js', await buildNip46()); -- cgit v1.2.3