aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-10 08:35:36 -0700
committerAdam Soltys <[email protected]>2026-10-10 08:35:36 -0700
commit2338345063b0f1c816469401264b0cd0d7b7636b (patch)
treea5a35e4bd7a78e5803e68cd49c44692389691961
parent42c0ef9c6391285d76825bdf0d29371cd6ebf500 (diff)
downloadcoinosv3-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.js8
-rw-r--r--src/features/nostrlogin.js6
-rw-r--r--tools/amber-dm-upgrade-test.js13
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);