fix(desktop): own native WebSocket lifecycle#2076
Conversation
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
wesbillman
left a comment
There was a problem hiding this comment.
No code-level blocker found in the native WebSocket lifecycle implementation. I checked cancellation/gating, bounded queues and shutdown, stale-connection cleanup, frontend IPC compatibility, ACL registration, reload teardown, and the added Rust/JS/E2E coverage.
I’m holding approval because this head (3633663fe) is behind current main and required CI is not green: Desktop E2E Integration is failing in the newer onboarding membership-denial/import flow, while several smoke shards are still running. The failure appears unrelated to this WebSocket diff, so please sync current main and rerun CI; I can approve once the updated head is green.
wesbillman
left a comment
There was a problem hiding this comment.
Implementation review is clean. Approving so this can merge after the branch is synced as needed and required CI is green.
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1mprnacetjua2xx3p5eddmhxyk6wv929ymm5py8kd2xfxurxahspqqlgyta <d8473ee32b973aa31a21a65adddcc4b69cc2a8a4dee8121ecd51926e0cddbc02@sprout-oss.stage.blox.sqprod.co>
Summary
tauri-plugin-websocketwith a small Buzz-ownedtokio-tungstenitelifecycle adapterdisconnect/disconnect_allcommands and tear down native sockets before Cmd+R reloadWhy
The upstream plugin keeps every writer behind one global mutex, awaits network I/O while holding it, has no force-disconnect command, and can retain native sockets across webview reload. A half-open transport can therefore survive JS reset/reload and block unrelated connections.
This is lifecycle hardening, not a claim that it was the proven root cause of Mat's report: Mat was on 0.4.16 before #2060, and the observed trigger aligns with a relay pod bounce.
Validation
just ci(Hermit Rust 1.95): passdisconnect_all, and added the inlined-plugin ACL/default permissionwebsocket:defaultallows connect/send/disconnect/disconnect_allNot run