diff options
| author | Adam Soltys <[email protected]> | 2026-10-10 08:35:36 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-10 08:35:36 -0700 |
| commit | 2338345063b0f1c816469401264b0cd0d7b7636b (patch) | |
| tree | a5a35e4bd7a78e5803e68cd49c44692389691961 | |
| parent | 42c0ef9c6391285d76825bdf0d29371cd6ebf500 (diff) | |
| download | coinosv3-2338345063b0f1c816469401264b0cd0d7b7636b.tar.gz coinosv3-2338345063b0f1c816469401264b0cd0d7b7636b.zip | |
Messages: the Amber notice no longer waits for Amber to wake. "Messages sent to you can't be opened here · Connect Amber" showed only once the Amber signer had resumed, and the DM drain is what resumes it — on a phone whose every wrap the old code had already logged as opened, nothing is pending, so nothing might ever ask. nostrLoginIdentity now says how the account came in (`via`), and an Amber account counts as one whether or not its signer is live yet (the notice, and the flag that re-opens written-off wraps once a decrypting signer takes over). tools/amber-dm-upgrade-test.js runs that case with ALL_LOGGED=1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3
| -rw-r--r-- | src/features/messages.js | 8 | ||||
| -rw-r--r-- | src/features/nostrlogin.js | 6 | ||||
| -rw-r--r-- | tools/amber-dm-upgrade-test.js | 13 |
3 files changed, 19 insertions, 8 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 28e20f1..be5f785 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -2950,7 +2950,13 @@ export function messagesFeature(ctx) { const login = hook('nostrLoginIdentity'); return !login || !!(login.signer && !login.signer.interactive && login.signer.decryptFrom); } - const interactiveLogin = () => { const l = hook('nostrLoginIdentity'); return !!(l && l.signer && l.signer.interactive); }; + // an Amber-by-intent account, whether or not its signer has woken yet: + // a wallet whose every wrap was already logged never wakes it, and then + // never said why its messages were missing + const interactiveLogin = () => { + const l = hook('nostrLoginIdentity'); + return !!l && (l.signer ? !!l.signer.interactive : l.via === 'amber'); + }; // Wraps logged while an interactive signer stood in for the login npub // may be messages no key here ever tried. Once a signer that can decrypt // takes over (Amber reconnected over NIP-46), the heal pass re-opens diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js index 2023931..4f1f0de 100644 --- a/src/features/nostrlogin.js +++ b/src/features/nostrlogin.js @@ -763,10 +763,12 @@ export function nostrLoginFeature(ctx) { // A just-connected signer counts immediately — the persisted link lands // only after the wallet opens, and the header avatar shouldn't show the // wallet key's face in the gap. - if (liveSigner()) return { pubkey: live.pubkey, npub: npubOf(live.pubkey), signer: live }; + // `via`: how the account came in, known before its signer wakes (an + // Amber account is one whether or not Amber has been asked anything yet) const st = load(); + if (liveSigner()) return { pubkey: live.pubkey, npub: npubOf(live.pubkey), signer: live, via: st.pubkey === live.pubkey ? reconnectViaOf(st) : viaOf(live) }; if (!st.pubkey) return null; - return { pubkey: st.pubkey, npub: npubOf(st.pubkey), signer: live }; + return { pubkey: st.pubkey, npub: npubOf(st.pubkey), signer: live, via: reconnectViaOf(st) }; }, // A page reload loses the signer. A stored passkey key or an installed // extension reattaches without prompting, which is what lets the payment diff --git a/tools/amber-dm-upgrade-test.js b/tools/amber-dm-upgrade-test.js index 4e34a94..9a5070a 100644 --- a/tools/amber-dm-upgrade-test.js +++ b/tools/amber-dm-upgrade-test.js @@ -6,7 +6,7 @@ // the same account over NIP-46 (nostrconnect), after which every wrap the // relays hold is opened again — including ones the old code wrote off. // The remote signer is played by this script over a shimmed relay. -// Run: bun tools/amber-dm-upgrade-test.js +// Run: bun tools/amber-dm-upgrade-test.js (and ALL_LOGGED=1 bun tools/amber-dm-upgrade-test.js) import puppeteer from './puppeteer.js'; import { cacheKeyFor } from '../src/wallet.js'; import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure'; @@ -20,6 +20,9 @@ const SK = generateSecretKey(); const PK = getPublicKey(SK); // the Amber accoun const PEER_SK = generateSecretKey(); const PEER = getPublicKey(PEER_SK); const OLD = wrapEvent(PEER_SK, { publicKey: PK }, 'an old message the wrap log wrote off'); const NEW = wrapEvent(PEER_SK, { publicKey: PK }, 'a message from before the upgrade'); +// ALL_LOGGED=1: every wrap already written off by the old code, so nothing +// is pending and the Amber signer is never woken (isolabellart's phone) +const ALL = process.env.ALL_LOGGED === '1'; const html = await buildHtml({ minify: true, pwa: false }); const server = Bun.serve({ port: 5311, fetch: (req) => { const p = new URL(req.url).pathname; if (p === '/verify-worker.js' || p === '/nip46.js' || p.startsWith('/punks')) return new Response(Bun.file('dist' + p)); return new Response(html, { headers: { 'content-type': 'text/html' } }); } }); @@ -106,12 +109,12 @@ try { const base = cacheKeyFor(mn + '\n'); // an Amber login, and a wrap log in which the old code had already // written OLD off as opened - await page.evaluate(([k, pk, oldId]) => { + await page.evaluate(([k, pk, oldId, newId]) => { localStorage.setItem(k + ':nostrlogin', JSON.stringify({ pubkey: pk, session: { amber: pk }, via: 'amber' })); const m = JSON.parse(localStorage.getItem(k + ':messages') || '{}'); - m.wraps = [oldId.slice(0, 16)]; m.dmHeal = 2; + m.wraps = [oldId.slice(0, 16)].concat(newId ? [newId.slice(0, 16)] : []); m.dmHeal = 2; localStorage.setItem(k + ':messages', JSON.stringify(m)); - }, [base, PK, OLD.id]); + }, [base, PK, OLD.id, ALL ? NEW.id : null]); await page.reload({ waitUntil: 'domcontentloaded' }); await waitText('receive', 20000); await page.evaluate(() => document.querySelector('.app-nav-button[aria-label="Messages"]')?.click()); @@ -121,7 +124,7 @@ try { await sleep(4000); check('no decrypt intent went to Amber for an arriving wrap', !(await page.evaluate(() => window.__amberUrls.some((u) => /nip44_decrypt/.test(u))))); check('neither message shows', !(await text()).includes('a message from before the upgrade') && !(await text()).includes('wrote off')); - check('the arriving wrap is not logged as opened', await page.evaluate(([k, id]) => !(JSON.parse(localStorage.getItem(k + ':messages') || '{}').wraps || []).includes(id.slice(0, 16)), [base, NEW.id])); + if (!ALL) check('the arriving wrap is not logged as opened', await page.evaluate(([k, id]) => !(JSON.parse(localStorage.getItem(k + ':messages') || '{}').wraps || []).includes(id.slice(0, 16)), [base, NEW.id])); console.log('\n[Connect Amber: the same account over NIP-46]'); await click('connect amber'); await sleep(300); |
