diff options
| author | Adam Soltys <[email protected]> | 2026-09-08 11:15:02 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-08 11:15:02 -0700 |
| commit | 7b444d6f0750b1ff316204dd5ec4425201c480e9 (patch) | |
| tree | fb6b4a7393b22402d49b91fe98ee89304a2977b6 /src/features | |
| parent | 779284629dd30df04b5e594b9d729546e08bbe71 (diff) | |
| download | coinosv3-7b444d6f0750b1ff316204dd5ec4425201c480e9.tar.gz coinosv3-7b444d6f0750b1ff316204dd5ec4425201c480e9.zip | |
DMs: one nostr identity per wallet (stop the phantom seed-key inbox).
A wallet has two nostr keys — the seed-derived key and, when signed in, the login identity (extension/bunker/passkey/Google). Sending already used one identity, but RECEIVING/advertising used both: startDMs subscribed kind-1059 gift wraps on '#p' for both keys and fetched both keys' inbox relays, and ensureDmRelayList always published a kind-10050 inbox for the seed key even under a login. Result: a login wallet advertised the seed key as a SECOND DM inbox, so the welcomer greeted both keys (duplicate welcome) and a person's DMs split across two identities — one showed up as if from "another user".
Collapse to a single DM identity: new dmSubKeys() returns just the login pubkey when signed in, else the seed key, and startDMs subscribes/fetches only that; ensureDmRelayList no longer advertises the seed key when a login identity is active. isMe() and dmDecryptors() stay tolerant of both keys so already-received messages still thread and align. Not a privacy fix — nothing ever leaked to a third party (both keys were the user's own); this removes the confusing second self-identity going forward. (Existing cached DMs to the seed key on a given device don't retro-clean; a chat re-sync/forget clears them.)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGzU12QrAA4G6BYGzGVdXz
Diffstat (limited to 'src/features')
| -rw-r--r-- | src/features/messages.js | 22 |
1 files changed, 20 insertions, 2 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 4455711..6e4c781 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -212,6 +212,19 @@ export function messagesFeature(ctx) { return pks; }; const isMe = (pk) => myPubkeys().includes(pk); + // The ONE nostr identity this wallet uses for DMs: the login identity when + // signed in (extension/bunker/passkey/Google), else the seed-derived key. A + // wallet has two keys (seed + login), but only this one should receive and + // advertise DMs — advertising/polling the seed key as a SECOND inbox is what + // produced duplicate welcome DMs and split a person's messages across two + // identities. isMe/dmDecryptors stay tolerant of both so already-received + // messages still thread and align; we just stop treating the seed key as a + // live second inbox. (One key here, but kept as a list for the '#p' filters.) + const dmSubKeys = () => { + const id = hook('nostrLoginIdentity'); + const pk = (id && id.pubkey) || (wallet.nostr && wallet.nostr.pk) || null; + return pk ? [pk] : []; + }; // "No identity" while soft-locked means the keys left with the lock — say // that, not "signer disconnected" (which reads as a nostr-login problem). const noIdToast = () => { @@ -1446,7 +1459,9 @@ export function messagesFeature(ctx) { function startDMs() { if (dmStarted) return; - const pks = myPubkeys(); + // Poll/advertise only the single DM identity (see dmSubKeys) — not the seed + // key as a phantom second inbox. + const pks = dmSubKeys(); if (!pks.length) return; dmStarted = true; // warm threads from the local cache. A self-thread of our own messages is @@ -1490,9 +1505,12 @@ export function messagesFeature(ctx) { // Publish a kind 10050 DM-relay list for the wallet key if none exists, so // other NIP-17 clients can find our inbox. Never touch a login npub's list - // — the user's other clients own that. + // — the user's other clients own that. And when a login identity IS active, + // don't advertise the seed key at all: it's internal, not a second inbox + // (advertising it is what let DMs land on a phantom identity). async function ensureDmRelayList() { if (!wallet.nostr || !wallet.nostr.sk) return; + if (hook('nostrLoginIdentity')) return; const pk = wallet.nostr.pk; const existing = await queryOn(DM_RELAYS, { kinds: [10050], authors: [pk] }, 2500); if (existing.length) return; |
