aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-30 13:08:05 -0700
committerAdam Soltys <[email protected]>2026-08-30 13:08:05 -0700
commit431907c9959e6d414fe13e05f76d1a1a45f7828e (patch)
tree4193810d2c22848da9ea670d9002f8d07878d233
parente16e7ed5ec1a762c58b052f76ad84c452f0080b8 (diff)
downloadcoinosv3-431907c9959e6d414fe13e05f76d1a1a45f7828e.tar.gz
coinosv3-431907c9959e6d414fe13e05f76d1a1a45f7828e.zip
The DM drain wakes the signer itself: boot's single resume attempt losing to cold-start churn left the phone holding every wrap with nothing ever asking the bunker again — now a drain that finds its decryptor set incomplete calls resume (single-flight, rate-limited) before deciding anything, and the bunker resume deadline stretches to 15s
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr
-rw-r--r--src/features/messages.js13
-rw-r--r--src/nostr-login.js2
2 files changed, 11 insertions, 4 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 4003e6c..637c2d1 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -1155,10 +1155,17 @@ export function messagesFeature(ctx) {
}
async function drainPendingWraps() {
- if (draining || !pendingWraps.size || !dmDecryptors().length) return;
+ if (draining || !pendingWraps.size) return;
draining = true;
- const complete = decryptorsComplete();
try {
+ // Wraps sealed to the login npub can't open without its signer. Wake it
+ // up ourselves — the boot-time resume is a single attempt, and if it
+ // loses to cold-start churn nothing else asks until the user happens to
+ // touch a screen that calls identity(). Resume single-flights and
+ // rate-limits itself, so asking here is cheap.
+ if (!decryptorsComplete()) await Promise.resolve(hook('nostrLoginResume')).catch(() => null);
+ if (!dmDecryptors().length) return;
+ const complete = decryptorsComplete();
for (const [id, p] of [...pendingWraps]) {
// A remote signer can die mid-pass; counting the remaining wraps as
// full-strength failures would evict messages that were never really
@@ -1169,7 +1176,7 @@ export function messagesFeature(ctx) {
}
} finally { draining = false; }
// still holding wraps: the signer may connect later — keep a slow retry
- if (pendingWraps.size) scheduleDrain(complete ? 30_000 : 60_000);
+ if (pendingWraps.size) scheduleDrain(decryptorsComplete() ? 30_000 : 60_000);
}
function openDirectBundle(json) {
diff --git a/src/nostr-login.js b/src/nostr-login.js
index 4cc1ddf..86c6b79 100644
--- a/src/nostr-login.js
+++ b/src/nostr-login.js
@@ -126,7 +126,7 @@ export async function bunkerSigner(uri, { onAuth } = {}) {
// permission to this client key, not to this page load, so rebuilding from
// the same client key and pointer picks the session back up — usually without
// prompting the user again.
-export async function resumeBunker(session, { onAuth, timeoutMs = 8000 } = {}) {
+export async function resumeBunker(session, { onAuth, timeoutMs = 15000 } = {}) {
if (!session || !session.local || !session.bp || !session.bp.pubkey) return null;
const { BunkerSigner } = await loadNip46();
const local = hex.decode(session.local);