You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reliability improvements for back-to-back commands and long-lived
tunnels. Verified 35/40 (87.5%) operations succeed under aggressive
rapid-fire stress between two matching-version daemons; rc3 hung
indefinitely against the same pattern.
Three layers of recovery:
* relayPinned map. Authoritative-signal sources (registry relay_only,
dial direct-phase exhaustion, ICMP-unreachable threshold, beacon-
sourced auth key_exchange) pin the relay flag.
clearRelayOnDirectLocked is now disabled for pinned peers — cannot
auto-flip back to direct on stray non-beacon-sourced packets that
the prior heuristic mis-detected (NAT-port-rewrite of beacon replies,
beacon-mesh forwards). The previous flap caused session-key rotation
cascades and the "ping works for a minute, dial timeout for minutes"
symptom.
* peerCrypto.decryptFailCount + decryptFailDropThreshold=5. After 5
consecutive AEAD authentication failures from a peer, drop crypto
entirely and trigger a fresh key_exchange. Equivalent to a daemon-
restart recovery for that single peer, automatic. Reset to 0 on any
successful decrypt. Public DropCrypto(nodeID) method exposes this
for the dial-timeout-exhausted path and operator scripts.
* DialMaxRetries 6 → 7. Relay phase gets 4 retries (was 3), giving
cold-start handshakes enough room without exceeding --timeout. Total
budget ~8 s with DialInitialRTO=250ms / DialMaxRTO=8s.
pilotctl ping per-attempt floor 4s → 10s — covers daemon startup-storm
warm-up window where 7+ trusted peers concurrently establish crypto.
Considered and reverted in development:
* Inbound auth-key-exchange rate-limit (deadlocked peer-side rebuild
attempts when their decryptFailDropThreshold dropped crypto for us)
* Recv-replay-window reset on stale auth duplicate (broke high-counter
in-flight packets after reset)
* Trusted-peer tunnel pre-warm at startup (ECDH variant caused rekey
storm; ensureTunnel-only variant caused NAT-punch wave)
* Dial-timeout-exhausted crypto drop (fired on cold-start delays and
made things worse)
Known residual: peers running older daemon versions still trigger
occasional rekey-storm dial timeouts; updating those peers to rc4
resolves it. Cold-start: first 1-3 dials immediately after daemon
restart can fail while crypto establishes; subsequent dials succeed.
Copy file name to clipboardExpand all lines: pkg/daemon/daemon.go
+4-4Lines changed: 4 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -140,10 +140,10 @@ const (
140
140
141
141
// Dial and retransmission constants.
142
142
const (
143
-
DialDirectRetries=3// direct connection attempts before relay
144
-
DialMaxRetries=6// total attempts (direct + relay)
145
-
DialInitialRTO=250*time.Millisecond// initial SYN retransmission timeout. Lowered from 1s — modern relay RTT is <200ms; waiting a full second before assuming loss makes cold dials feel like a stall. Three direct retries with exponential backoff (250→500→1000) still cover up to 1.75s of jitter before flipping to relay; that's plenty for an unhealthy direct path while letting the common case (peer is reachable, single retry needed) feel snappy.
146
-
DialMaxRTO=8*time.Second// max backoff for SYN retransmission
143
+
DialDirectRetries=3// direct connection attempts before relay
144
+
DialMaxRetries=7// total attempts (direct + relay). 3 direct + 4 relay. With DialInitialRTO=250ms exponential-backoff capped at DialMaxRTO=8s, the relay phase is ~7.75s — covers cold-start handshake (key_exchange + flushPending + SYN/SYN-ACK round trip) for typical peers while keeping bad dials from blocking longer than the user's --timeout. The probe-and-adapt machinery (see srttHistory below) will let us shorten this for peers we've successfully dialed before.
145
+
DialInitialRTO=250*time.Millisecond// initial SYN retransmission timeout. Lowered from 1s — modern relay RTT is <200ms; waiting a full second before assuming loss makes cold dials feel like a stall. Three direct retries with exponential backoff (250→500→1000) still cover up to 1.75s of jitter before flipping to relay; that's plenty for an unhealthy direct path while letting the common case (peer is reachable, single retry needed) feel snappy.
146
+
DialMaxRTO=8*time.Second// max backoff for SYN retransmission
147
147
DialCheckInterval=10*time.Millisecond// poll interval for state changes during dial
0 commit comments