Skip to content

feat: better VPN detection#354

Open
alexey-yarmosh wants to merge 7 commits intomasterfrom
vpn-detection
Open

feat: better VPN detection#354
alexey-yarmosh wants to merge 7 commits intomasterfrom
vpn-detection

Conversation

@alexey-yarmosh
Copy link
Copy Markdown
Member

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4314a76b-86d2-4835-86b9-80e286b5a0a6

📥 Commits

Reviewing files that changed from the base of the PR and between bf4bf09 and 5c29d32.

📒 Files selected for processing (1)
  • test/unit/status-manager/disconnect-test.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/unit/status-manager/disconnect-test.test.ts

Walkthrough

The PR refactors status management by replacing the monolithic src/lib/status-manager.ts with a modular src/status-manager/ directory. It adds three submodules—DisconnectTest, PingTest, and IcmpTcpTest—and a new StatusManager that composes them. The probe startup logging was moved into connect(). api-error-handler now reports disconnects via the new disconnect-test. Configuration gains status.icmpTcpTargets. Tests were added for the new modules and the old lib test removed; .claude/CLAUDE.md were added to .gitignore.

Possibly related PRs

  • feat: add TCP ping #305: Adds TCP-ping support and ping parsing used by the new IcmpTcpTest latency-differential measurements.
  • feat: improve alt IP reporting flow #320: Adjusts status-manager surface and probe startup integration; overlaps with moving initialization/imports to src/status-manager/.
  • feat: improve QA tests #350: Changes disconnect reporting and status-manager structure, related to replacing prior disconnect reporting with the new TTL-based disconnect tracker.

Suggested reviewers

  • MartinKolarik
🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements a significant architectural shift toward ICMP-TCP latency-based VPN detection through new modules (icmp-tcp-test, ping-test, disconnect-test, status-manager refactor), but the implementation diverges from the linked issue's requirements. Issue #766 requires traceroute-derived hop latency analysis and per-hop IP lookups; this PR implements ICMP-TCP ping comparison instead. Either clarify if this approach supersedes the issue or align implementation with the stated requirements.
Out of Scope Changes check ⚠️ Warning Several changes appear unrelated to VPN detection: AWS S3 dualstack targets in config, Claude file ignores, and refactored StatusManager structure go beyond implementing the hop-latency and per-hop IP query logic described in #766. Clarify the purpose of S3 dualstack targets for ICMP-TCP testing and explain why the full StatusManager refactor (including disconnect-test and ping-test reorganization) is necessary for the VPN detection feature.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: better VPN detection' directly relates to the main objective of implementing improved VPN detection through ICMP-TCP latency comparison instead of external API queries.
Description check ✅ Passed The description references issue #766, which is the linked issue containing the VPN detection requirements. This establishes a connection to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vpn-detection

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/status-manager/disconnect-test.ts (1)

19-25: Repeated updateStatus(true) calls after threshold is reached.

Once MAX_DISCONNECTS_COUNT (3) is reached, every subsequent reportDisconnect() call will trigger updateStatus('too-many-disconnects', true) again since size will remain >= 3. If the StatusManager.updateStatus deduplicates these calls, this is fine. Otherwise, consider tracking whether the threshold has already been reported to avoid redundant updates.

♻️ Optional: Track threshold state to avoid redundant calls
 export class DisconnectTest {
+	private thresholdReached = false;
 	private readonly disconnects = new TTLCache<string, number>({
 		ttl: DISCONNECTS_TTL,
 		dispose: () => {
 			if (this.disconnects.size === 0) {
+				this.thresholdReached = false;
 				this.updateStatus('too-many-disconnects', false);
 			}
 		},
 	});

 	constructor (private readonly updateStatus: (status: 'too-many-disconnects', value: boolean) => void) {}

 	public reportDisconnect () {
 		this.disconnects.set(randomUUID(), Date.now());

-		if (this.disconnects.size >= MAX_DISCONNECTS_COUNT) {
+		if (this.disconnects.size >= MAX_DISCONNECTS_COUNT && !this.thresholdReached) {
+			this.thresholdReached = true;
 			this.updateStatus('too-many-disconnects', true);
 		}
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/status-manager/disconnect-test.ts` around lines 19 - 25, reportDisconnect
currently calls updateStatus('too-many-disconnects', true) on every call once
disconnects.size >= MAX_DISCONNECTS_COUNT; to avoid redundant updates add a
guard that only triggers when the threshold is first crossed (e.g. only when
disconnects.size === MAX_DISCONNECTS_COUNT) or maintain a boolean like
tooManyDisconnectsReported on the StatusManager class; set it true when you call
updateStatus in reportDisconnect and reset it to false wherever disconnects
entries are pruned (or when size falls below MAX_DISCONNECTS_COUNT) so
subsequent crossings will re-report correctly; update references:
reportDisconnect, disconnects, updateStatus, MAX_DISCONNECTS_COUNT.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/probe.ts`:
- Around line 74-76: The startup log in connect() (logger.info referencing
VERSION, process.env['NODE_ENV'], and probeUuid) runs per throng worker; move
that logger.info call out of the connect() function so it runs once at module
startup, or add a guard inside connect() to only log when workerId is the
primary worker (e.g., when workerId === 0 or workerId is undefined) so the
message emits only once during initialization.

In `@src/status-manager/icmp-tcp-test.ts`:
- Around line 26-29: The readiness path currently blocks on the socket
'api:connect:isProxy' handler (where you set this.isProxy and call
evaluateAndUpdate()), causing evaluateAndUpdate() to never set
"icmp-tcp-test-failed" if that event is delayed; update the logic in the
socket.on('api:connect:isProxy', ...) area and the analogous handler around the
other occurrence so that isProxy has a safe default (e.g., false/unknown) and/or
add a short timeout that assigns the fallback and calls evaluateAndUpdate() if
the event hasn't arrived; ensure evaluateAndUpdate(), the isProxy property, and
any StatusManager.getStatus() callers use that fallback so readiness won't be
gated indefinitely and adjust tests that assumed the event never triggers.
- Around line 91-129: The current VPN decision (isVpnDetected/isVpn) treats IPv4
and IPv6 separately and lets a single over-100 RTT-diff block a probe; update
logic to aggregate signals across protocols and require a minimum of two
“points” before declaring VPN: combine diffs from diffsIPv4 and diffsIPv6 into
one numeric array inside isVpnDetected (instead of calling isVpn twice), include
traceroute/public-hop signals (the traceroute/public-hop result(s) you added
elsewhere as extra point(s)) when computing the score, and change isVpn so that
an over-100 diff contributes a single point but does not trigger VPN unless
total points >= 2 (and similarly require two over-60s across the combined data
or one over-60 plus a traceroute/public-hop point). Keep measureDiff as-is but
ensure its nulls are treated as non-points when aggregating.

In `@test/unit/status-manager/disconnect-test.test.ts`:
- Around line 47-50: The test's assertions for updateStatus are wrong: change
the expected call count from 4 to 2 and adjust the inspected argument index from
args[3] to args[1]; specifically update the assertions around
sandbox.clock.tickAsync(...) to expect updateStatus.callCount === 2 and
expect(updateStatus.args[1]).to.deep.equal(['too-many-disconnects', false]);
this targets the updateStatus spy used in the disconnect test so the assertion
matches the implementation where a single true and a single false call are made
after TTL expiry.

---

Nitpick comments:
In `@src/status-manager/disconnect-test.ts`:
- Around line 19-25: reportDisconnect currently calls
updateStatus('too-many-disconnects', true) on every call once disconnects.size
>= MAX_DISCONNECTS_COUNT; to avoid redundant updates add a guard that only
triggers when the threshold is first crossed (e.g. only when disconnects.size
=== MAX_DISCONNECTS_COUNT) or maintain a boolean like tooManyDisconnectsReported
on the StatusManager class; set it true when you call updateStatus in
reportDisconnect and reset it to false wherever disconnects entries are pruned
(or when size falls below MAX_DISCONNECTS_COUNT) so subsequent crossings will
re-report correctly; update references: reportDisconnect, disconnects,
updateStatus, MAX_DISCONNECTS_COUNT.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a2d4b8e2-6813-4873-9e4e-f08ecc832587

📥 Commits

Reviewing files that changed from the base of the PR and between 11799fa and bf4bf09.

📒 Files selected for processing (15)
  • .gitignore
  • config/default.cjs
  • src/helper/api-error-handler.ts
  • src/lib/status-manager.ts
  • src/probe.ts
  • src/status-manager/disconnect-test.ts
  • src/status-manager/icmp-tcp-test.ts
  • src/status-manager/ping-test.ts
  • src/status-manager/status-manager.ts
  • test/unit/lib/status-manager.test.ts
  • test/unit/probe.test.ts
  • test/unit/status-manager/disconnect-test.test.ts
  • test/unit/status-manager/icmp-tcp-test.test.ts
  • test/unit/status-manager/ping-test.test.ts
  • test/unit/status-manager/status-manager.test.ts
💤 Files with no reviewable changes (2)
  • src/lib/status-manager.ts
  • test/unit/lib/status-manager.test.ts

Comment thread src/probe.ts
Comment thread src/status-manager/icmp-tcp-test.ts
Comment thread src/status-manager/icmp-tcp-test.ts
Comment thread test/unit/status-manager/disconnect-test.test.ts Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better VPN detection

1 participant