feat(tunnels): detect intermittent listener flapping#16
Merged
Conversation
Track a short window of listener-presence observations per SSH tunnel and surface a distinct 'flapping' status when an Alive -L/-D tunnel's local LISTEN socket comes and goes across scans. This catches a tunnel that degrades slowly (listener intermittently present) rather than only the binary 'no listener' (gone right now) case. - SshTunnel records one listener observation per confirmable scan into a capped VecDeque; is_flapping() reports a window holding both present and absent samples. History clears on respawn. - Recording lives in App::refresh (only past the startup grace window and while auto-refresh is active) so stale/premature samples never forge a phantom flap. Shared LISTENER_GRACE + entry_has_listener helper between the recorder and the tunnels view. - New tunnel_health_flapping i18n string (en/ru/zh), rendered in light yellow, distinct from 'no listener' (yellow) which keeps priority. https://claude.ai/code/session_01BHfdoVE66Dag2SEFtzNSR3
Owner
Author
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: beb85c8a41
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
diff_entries retains a vanished LISTEN socket in session.entries as EntryStatus::Gone for GONE_RETENTION (5s) before removal. entry_has_listener matched it regardless of status, so a single-refresh listener drop recorded 'true' for the missing socket — the flapping window never saw the absence and the new signal stayed hidden. It also kept the binary 'no listener' check green for up to 5s after the socket actually died. Filter out Gone so short drops are observable. Reported by Codex review. https://claude.ai/code/session_01BHfdoVE66Dag2SEFtzNSR3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a listener flapping health signal for
ssh -L/-Dtunnels in the Tunnels view. Today the view has a binary listener check — greenalivevs yellowno listener(aLISTENsocket owned by the tunnel'ssshPID is gone right now). That only catches a full bind failure. This PR catches the "degrades slowly rather than breaks all at once" case: a listener that is intermittently present across scans (leaked descriptors, stuck connections, a flakyssh -Dupstream), surfacing it as a distinctflappingstatus.This is observation-only —
prtmonitors ports, it never proxies traffic, so this reuses data already scanned and opens no new connections. The auto-reconnect loop andlast_statusare untouched.How
SshTunnelkeeps a cappedVecDeque<bool>of recent listener-presence observations.is_flapping()is true when the window has enough samples (≥4 of 6) and holds both a present and an absent sample. History clears onrespawnso a restarted tunnel starts clean.App::refresh, only forAlivetunnels past the startup grace window (and only while auto-refresh is active). This mirrors the existingLISTENER_GRACEguard so a freshly (re)started tunnel the scan hasn't observed yet never forges a phantom flap.LISTENER_GRACEandentry_has_listenermoved toapp.rsand reused by both the recorder and theviews::tunnelsrenderer, so the grace window and the PID-match invariant live in one place.no listener(yellow, gone now — keeps priority) ›flapping(light yellow, present now but recently dropped) ›alive(green).tunnel_health_flappingstring for en (flapping) / ru (нестабилен) / zh (监听抖动).Tests
Six new unit tests in
forward.rscover the flapping predicate and window capping (stable-present, all-absent, alternating, insufficient samples, empty, eviction). Logic is extracted into pure free functions so it's testable without spawning a realsshchild. Full suite green;clippyandfmtclean.https://claude.ai/code/session_01BHfdoVE66Dag2SEFtzNSR3
Generated by Claude Code