diff --git a/changelog.md b/changelog.md index 10db25a..4ad7e97 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2026-03-20 - 4.13.2 - fix(remoteingress-core) +preserve reconnected edge entries during disconnect cleanup + +- Guard edge removal so disconnect handlers only delete entries whose cancel token is already cancelled +- Prevents stale TCP and QUIC disconnect paths from removing a newer connection after an edge reconnects + ## 2026-03-19 - 4.13.1 - fix(remoteingress-core) default edge transport mode to QUIC with fallback diff --git a/rust/crates/remoteingress-core/src/hub.rs b/rust/crates/remoteingress-core/src/hub.rs index d2b5afa..558f146 100644 --- a/rust/crates/remoteingress-core/src/hub.rs +++ b/rust/crates/remoteingress-core/src/hub.rs @@ -1073,7 +1073,11 @@ async fn handle_edge_connection( ).await; { let mut edges = connected.lock().await; - edges.remove(&edge_id); + // Only remove if the entry is still ours (not replaced by a reconnection). + // A replaced entry has a fresh non-cancelled token from the new handler. + if edges.get(&edge_id).map_or(false, |e| e.cancel_token.is_cancelled()) { + edges.remove(&edge_id); + } } let _ = event_tx.try_send(HubEvent::EdgeDisconnected { edge_id: edge_id.clone(), @@ -1534,7 +1538,11 @@ async fn handle_edge_connection_quic( { let mut edges = connected.lock().await; - edges.remove(&edge_id); + // Only remove if the entry is still ours (not replaced by a reconnection). + // A replaced entry has a fresh non-cancelled token from the new handler. + if edges.get(&edge_id).map_or(false, |e| e.cancel_token.is_cancelled()) { + edges.remove(&edge_id); + } } let _ = event_tx.try_send(HubEvent::EdgeDisconnected { edge_id, diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index d6b9591..86b7590 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/remoteingress', - version: '4.13.1', + version: '4.13.2', description: 'Edge ingress tunnel for DcRouter - tunnels TCP and UDP traffic from the network edge to SmartProxy over TLS or QUIC, preserving client IP via PROXY protocol.' }