| … | … | @@ -39,7 +39,22 @@ export async function createPairInvite ({ onSubkey, swarm = null, timeout = TIME |
| 39 | 39 | const data = b4a.from(JSON.stringify(delegation)) |
| 40 | 40 | request.confirm({ key: pairingKey.publicKey, encryptionKey: null, additional: { data, signature: crypto.sign(data, inviteKeyPair.secretKey) } }) |
| 41 | 41 | resolve({ subKey: hello.subKey, delegation }) |
| 42 | | } catch (e) { reject(e) } |
| 42 | } catch (e) { |
| 43 | // ⛔ TELL THE CANDIDATE. Rejecting only settles OUR promise; the joiner is left holding an open |
| 44 | // tunnel with nothing coming back, so it waits out its full timeout (5 min) and reports |
| 45 | // nothing about why. That is exactly how a BROWSER invite pasted into a NODE presents: the |
| 46 | // node sends a 64-hex keet device key, delegate() demands 66-hex secp256k1 and throws here, |
| 47 | // and the node just… hangs. A refusal is information the other side needs. |
| 48 | // |
| 49 | // Confirm with an { error } payload — the same shape mintAdoptInvite already returns when a |
| 50 | // human declines — then reject locally. Best-effort: if the channel is already gone there is |
| 51 | // nothing to say and the timeout remains the backstop. |
| 52 | try { |
| 53 | const data = b4a.from(JSON.stringify({ error: (e && e.message) || String(e) })) |
| 54 | request.confirm({ key: pairingKey.publicKey, encryptionKey: null, additional: { data, signature: crypto.sign(data, inviteKeyPair.secretKey) } }) |
| 55 | } catch { /* channel gone — the candidate falls back to its timeout */ } |
| 56 | reject(e) |
| 57 | } |
| 43 | 58 | } |
| 44 | 59 | }) |
| 45 | 60 | }) |