aboutsummaryrefslogtreecommitdiffstats
path: root/src/nostr-login.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-30 13:01:39 -0700
committerAdam Soltys <[email protected]>2026-08-30 13:01:39 -0700
commit80179a2e3bdc94a83a5969d5957af9eb2f051e50 (patch)
tree805444602fa0acb45af85312809e4e36a21727aa /src/nostr-login.js
parentd7d5484b76459c068135ebd7fb33ef19adf33bc0 (diff)
downloadcoinosv3-80179a2e3bdc94a83a5969d5957af9eb2f051e50.tar.gz
coinosv3-80179a2e3bdc94a83a5969d5957af9eb2f051e50.zip
The Amethyst signer stops dying every time a stranger's wrap fails to decrypt: only connection-shaped failures (with a real per-op deadline now — a backgrounded signer app used to just hang the call) drop the live bunker, the reconnect surfaces offer only the door the account actually came through (a recorded 'via', so an Amethyst-linked npub no longer stares at Google and passkey buttons), the settings card quietly reattaches from the stored session instead of waiting for a tap, and the DM retry drain eases to three full-strength passes per wrap
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
Diffstat (limited to 'src/nostr-login.js')
-rw-r--r--src/nostr-login.js17
1 files changed, 12 insertions, 5 deletions
diff --git a/src/nostr-login.js b/src/nostr-login.js
index 514c36b..4cc1ddf 100644
--- a/src/nostr-login.js
+++ b/src/nostr-login.js
@@ -145,17 +145,24 @@ export async function resumeBunker(session, { onAuth, timeoutMs = 8000 } = {}) {
}
function bunkerAdapter(signer, pubkey, local) {
+ // A dead transport (relay socket gone, signer app killed in the background)
+ // makes a NIP-46 call hang forever, not fail — so every operation carries
+ // its own deadline. The distinct message lets callers tell "the signer
+ // never answered" (connection is dead, reconnect) from "the signer said
+ // no" (an answered error — the connection is fine).
+ const answered = (p, ms = 20_000) => Promise.race([p,
+ new Promise((_, rej) => setTimeout(() => rej(new Error('signer did not answer')), ms))]);
return {
kind: 'bunker', pubkey, label: 'remote signer',
// Everything needed to rebuild this connection later. `local` is the
// client key the remote signer authorised — not the user's identity key,
// which never leaves the signer.
session: local ? { local: hex.encode(local), bp: signer.bp } : null,
- signEvent: (e) => signer.signEvent(e),
- encryptSelf: (txt) => signer.nip44Encrypt(pubkey, txt),
- decryptSelf: (ct) => signer.nip44Decrypt(pubkey, ct),
- encryptTo: (peer, txt) => signer.nip44Encrypt(peer, txt),
- decryptFrom: (peer, ct) => signer.nip44Decrypt(peer, ct),
+ signEvent: (e) => answered(signer.signEvent(e)),
+ encryptSelf: (txt) => answered(signer.nip44Encrypt(pubkey, txt)),
+ decryptSelf: (ct) => answered(signer.nip44Decrypt(pubkey, ct)),
+ encryptTo: (peer, txt) => answered(signer.nip44Encrypt(peer, txt)),
+ decryptFrom: (peer, ct) => answered(signer.nip44Decrypt(peer, ct)),
close: () => { try { signer.close(); } catch {} },
};
}