Skip to content

Windows desktop 3.2.3: incy://connect / open / toggle deep links never start the VPN (only disconnect works) #1

Description

@Viroopadas

Environment

  • INCY desktop 3.2.3, Windows portable build (incy-desktop-3.2.3.jar, Compose Desktop wrapper over Xray-core 26.6.1 via incycore.dll)
  • Windows 11 Pro 22621
  • Single-instance IPC delivery of deep links works (each command is logged as Processing deep link from IPC: ... in config/cache/incy-app.log)

Summary

The developer docs (ru/dev-docs/deep-links.md) document incy://connect / incy://open / incy://disconnect / incy://close / incy://toggle with no platform caveats. On the Windows desktop build, only the disconnect half actually works:

  • incy://disconnect (and close) tears the tunnel down reliably within seconds — works as documented.
  • incy://connect (and open) is received and dispatched, but never starts the VPN. The only visible effect is (at most) a "Connecting…" toast.
  • incy://toggle can therefore only ever disconnect: its connect branch is the same dead path.

Clicking the Connect button in the GUI works normally, so this is specific to the deep-link path.

Steps to reproduce

  1. Start INCY and connect via the GUI (tunnel up, TUN adapter present).
  2. Run: Start-Process incy.exe -ArgumentList 'incy://disconnect' → tunnel goes down within seconds. OK.
  3. Wait for the tunnel to be fully down, then run: Start-Process incy.exe -ArgumentList 'incy://connect' (also tried incy://toggle, and incy://open followed by incy://connect, with generous waits between steps).
  4. Observe: incy-app.log shows Processing deep link from IPC: incy://connect — but no connection attempt follows. No VpnVM/engine start lines appear in the log (a manual GUI click at the same moment produces the full connect() called → … → Connected! chain in ~4 s). The tunnel never comes up.

Reproduced consistently across multiple runs.

Analysis (from inspecting the 3.2.3 desktop jar bytecode with javap)

The deep-link dispatcher (DeepLinkHandler) recognizes all five commands, but the two branches are asymmetric:

  • handleDisconnect directly calls the VPN controller's teardown method — that's why disconnect works (and why it bypasses VpnVM, matching the missing teardown log lines).
  • handleConnect only emits DeepLinkResult.VpnAction("Connecting..."), whose sole consumer is the ToastManager. It never calls the engine-start path (VpnVM.connect() → controller start) that the GUI button uses. There are zero references from the deep-link chain to the connect path.

So on the desktop build, the connect branch of the deep-link surface is effectively a toast-only stub. (There is also an isConnected guard that silently no-ops connect when the app still believes it is connected, but the primary issue is the missing engine call.)

Expected

incy://connect / incy://open start the VPN (same path as the GUI Connect button), and incy://toggle toggles in both directions — as the deep-links documentation implies.

Actual

Only disconnect/close affect the tunnel; connect/open show at most a toast; toggle can only disconnect.

Impact

Deep links are the natural automation/watchdog surface (e.g. recover a hung tunnel by cycling the VPN from a script). With connect missing, programmatic recovery is impossible without GUI automation or a full app restart, while disconnect being scriptable makes the asymmetry surprising.

If desktop connect-by-deep-link is intentionally unsupported, a note in deep-links.md would also resolve this — but implementing the connect branch would be far more useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions