Skip to content

feat(desktop): add authenticated remote port forwarding - #7922

Closed
burnmandont wants to merge 26 commits into
pingdotgg:mainfrom
burnmandont:pr/desktop-port-forwarding
Closed

burnmandont wants to merge 26 commits into
pingdotgg:mainfrom
burnmandont:pr/desktop-port-forwarding

Conversation

@burnmandont

@burnmandont burnmandont commented Aug 22, 2026 •

Copy link
Copy Markdown

Depends on #7921. Until that PR lands, GitHub includes its three routing commits in this comparison; the port-forwarding feature itself is the seven commits after a14598386.

What Changed

  • Add an optional port-forwarding capability and typed HTTP/WebSocket contracts.
  • Add a server-side, authenticated binary WebSocket-to-TCP bridge with short-lived authorization tickets.
  • Add Electron loopback listeners, IPC/preload integration, connection limits, flow control, and lifecycle cleanup.
  • Add desktop controls in the chat header and Settings, plus authorization refresh through the existing session.
  • Follow the environment's selected connection route and reset active streams safely when that route changes.
  • Support IPv4 and IPv6 localhost targets on the remote environment.

Why

Remote development often exposes an environment-local HTTP server, database, or debugger that the user needs on their desktop. This provides that path without exposing an unauthenticated listener or introducing another external tunnel dependency.

This is intentionally one complete cross-surface feature PR: contracts, server transport, client authorization, Electron lifecycle, and desktop UI must agree for the feature to be usable and capability-gated during version skew.

Security and Lifecycle

  • Local listeners bind only to 127.0.0.1.
  • Remote destinations are restricted to localhost, trying 127.0.0.1 and ::1.
  • Authorization tickets are single-use, destination-bound, parent-session-bound, and expire after 30 seconds.
  • The endpoint requires terminal:operate; active bridges follow parent-session revocation and expiration.
  • Connection, frame-size, initial-credit, and idle-time limits bound resource use.
  • Forward definitions are runtime-only: closing/restarting desktop removes them, with no persistence or autostart.
  • A server restart closes active streams. Route changes keep the local listener but close streams so local clients reconnect through the selected route.

UI Changes

Adds a port-forward control to the desktop chat header and management under Settings → Connections. The feature is desktop-only; mobile receives only compatible shared contracts and no forwarding UI.

Screenshots will be added before this draft is marked ready for review.

Verification

  • 112 focused tests passed across desktop IPC/manager, server bridge/tickets, client authorization/connection/session, and web controls/platform.
  • Typechecks passed for contracts, client-runtime, server, desktop, and web packages.
  • git diff --check passes against the routing branch.
  • No database migration is required.

Checklist

  • This PR is focused on one end-to-end port-forwarding feature
  • I explained what changed, why, and the security/lifecycle boundaries
  • I included screenshots for the desktop UI changes
  • No animation or motion behavior changed

Prepared with GPT-5.6-Sol via the Codex harness in T3 Code.

Note

Add desktop remote port forwarding, multi-route environments, and SSH runtime reuse

  • Adds desktop TCP port forwarding over authenticated WebSockets. The server issues single-use tickets via /api/auth/tcp-forward-ticket, and the desktop app bridges local TCP sockets to remote targets. UI controls added to settings and chat header.
  • Introduces multi-route connection environments. EnvironmentRegistry now tracks and publishes multiple available routes (e.g., Relay, SSH) per environment. Users can switch active routes from the settings UI.
  • Improves SSH remote runtime management. Remote launch and pairing scripts now use an inter-process lock, discover and reuse existing managed runtimes, and the client caches SSH bearer tokens, only re-pairing if the token is rejected.
  • Adds proactive DPoP credential refresh in the supervisor, waking up before expiry to reauthorize.
  • Risk: Catalog persistence changes add a routes array to ConnectionCatalogDocument; legacy documents fall back to treating targets as routes. SSH script changes (buildRemoteLaunchScript, buildRemotePairingScript) alter remote shell behavior and reuse logic.

Macroscope summarized 986953d.


Note

High Risk
Touches authentication (single-use tickets, session revocation, DPoP expiry), a new WebSocket-to-TCP bridge, and connection-catalog persistence. Mis-handling tickets, destinations, or route selection could expose remote loopback services or drop live sessions.

Overview
Adds desktop-only TCP port forwarding: a local 127.0.0.1 listener is bridged to a remote loopback port over a dedicated authenticated WebSocket. The server advertises tcpPortForwarding, issues destination-bound single-use tickets (/api/auth/tcp-forward-ticket, terminal:operate), and dials 127.0.0.1 then ::1. Electron owns listeners, credit-based framing, connection limits, and cleanup; the renderer authorizes each accepted socket through the current prepared connection.

UI: cable control in the chat header and a Connections settings panel to start/stop runtime-only forwards. Route changes keep the listener but close in-flight streams so reconnects use the newly selected path.

Also persists multiple routes per environment (selected targets plus routes). Users can switch T3 Connect vs SSH (and similar) from settings after a prepare-and-verify step; failed switches leave the live session unchanged. SSH prepare can reuse a cached bearer instead of always pairing.

Reviewed by Cursor Bugbot for commit 986953d. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e082fb1-c425-4244-8035-3ba4872d92a4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 22, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new Effect services (DesktopPortForwardManager, TcpForwardTicketStore, TcpForwardBridge) and their call sites against the service conventions. Service shape, make/layer exports, namespace imports, and dependency acquisition all look right; the findings below are about tagged-error catching and error modeling in the new code.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/server/src/auth/http.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardTicketStore.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts
Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread packages/ssh/src/tunnel.ts
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts
Comment thread packages/ssh/src/tunnel.ts Outdated
Comment thread packages/ssh/src/tunnel.ts Outdated
Comment thread packages/client-runtime/src/platform/storageDocument.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the changed web UI surfaces (DesktopPortForwardControl, DesktopPortForwardsSettings, the ui/select prop addition, and the ChatHeader/ConnectionsSettings call sites). Five findings, mostly around the shared overlay layer, the Button icon-color indirection, and duplicated treatment across the two new port-forward surfaces.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx Outdated
Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx Outdated
Comment thread apps/web/src/components/desktop/useDesktopPortForwards.ts Outdated
Comment thread packages/ssh/src/tunnel.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new port-forwarding services against the Effect service conventions. The findings from the previous run (catchTags usage, TcpForwardTargetConnectError structural fields, TcpForwardTicketInvalidError reason/cause, optional cause on DesktopPortForwardError) are all resolved. Two remaining items on error modeling and predicates are noted inline.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/web/src/components/desktop/desktopPortForwardAuthorization.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardTicketStore.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

UI consistency review of the new port-forwarding surfaces. Two concrete issues in the changed web UI; everything else (primitive usage, size="xs" route select in ConnectionsSettings, shared useDesktopPortForwards/portForwardConnectionSummary extraction, --control-icon-color indirection on the header trigger) now follows the existing contracts.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One convention finding on the new port-forward renderer helpers. Everything else in the new Effect service code (DesktopPortForwardManager, TcpForwardTicketStore, TcpForwardBridge) follows the canonical Context.Service / make / layer layout, structured Schema.TaggedErrorClass failures, and Effect.catchTags recovery.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/web/src/components/desktop/desktopPortForwardAuthorization.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two consistency findings in the new desktop port-forwarding UI (both in apps/web/src/components/settings/DesktopPortForwardsSettings.tsx, one of which also applies to the popover twin). Everything else — Button/Select/Input/Popover/Tooltip composition, the --control-icon-color indirection on the header trigger, the shared useDesktopPortForwards hook, and the new route Select in ConnectionsSettings — matches the existing primitives and call-site conventions.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated
Comment thread apps/web/src/components/settings/ConnectionsSettings.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new port-forwarding UI (DesktopPortForwardControl, DesktopPortForwardsSettings, the shared useDesktopPortForwards/presentation helpers, and the connection-method Select in ConnectionsSettings). The earlier findings on badge/icon-color indirection, hardcoded addresses, label spacing, arrow spacing, and the duplicated hook logic are resolved. One remaining consistency gap: the port inputs accept out-of-range values with no visible feedback, unlike the existing port field on the same settings page.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new Effect services in this slice (TcpForwardTicketStore, TcpForwardBridge, DesktopPortForwardManager, the desktop IPC methods, and the client-runtime route/authorization changes). Service definition order, make/layer exports, namespace imports, Effect.catchTags usage, and the schema predicates in desktopPortForwardAuthorization.ts all match the conventions. Two error-context findings below.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated
Comment thread apps/server/src/portForward/TcpForwardBridge.ts

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new Effect services and their call sites (TcpForwardTicketStore, TcpForwardBridge, DesktopPortForwardManager, registry/route-transition changes). Two remaining error-modeling items noted inline; imports, service/make/layer shape, catchTags usage, and predicate exports look consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/portForward/TcpForwardBridge.ts Outdated
Comment thread apps/desktop/src/portForward/DesktopPortForwardManager.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One themed-chrome finding in the chat header trigger. Prior findings on these files (settings status alignment, missing port aria-invalid feedback) still apply but are not re-posted.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One layout finding in the new port-forwarding settings form; details inline. Previously reported findings on these files that are still open (status alignment in SettingsRow, the duplicated create/stop/subscription logic shared with DesktopPortForwardControl, and the repeated count-badge treatment) are not re-posted here.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the changed web UI surfaces (DesktopPortForwardControl, DesktopPortForwardsSettings, the new route Select in ConnectionsSettings, and the ChatHeader insertion) against the shared primitives and this page's existing form conventions.

Resolved since the last run: the themed-toolbar hook (data-toolbar-control) is now present on the header trigger, the badge count is exposed through the trigger's accessible name, the stray z-70 select positioner override is gone, and the settings validation messages no longer sit inside sm:items-end grid cells.

Two remaining findings are noted inline: the popover's port validation messages are not associated with their inputs (so they are never announced), and the new field labels diverge from the field-label treatment used by every other form on this settings page.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/DesktopPortForwardsSettings.tsx Outdated
Comment thread apps/web/src/components/desktop/DesktopPortForwardControl.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the changed web UI surfaces (DesktopPortForwardControl, DesktopPortForwardsSettings, the new route selector in ConnectionsSettings). The label treatment, port-field validation wiring, badge treatment, and primitive usage now match the surrounding conventions. One remaining pending-state inconsistency noted inline.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/settings/ConnectionsSettings.tsx Outdated
@burnmandont
burnmandont marked this pull request as ready for review August 22, 2026 21:45
Comment thread apps/server/src/portForward/TcpForwardBridge.ts
@macroscopeapp

macroscopeapp Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Skipped

Macroscope did not run approvability analysis for this PR. Macroscope could not determine whether this PR modifies its approvability configuration, so the PR was not approved automatically. A PR that may change the rules that govern approval is never approved automatically.

@burnmandont
burnmandont force-pushed the pr/desktop-port-forwarding branch from 99c51ee to 78b7ec9 Compare August 22, 2026 22:15
@burnmandont
burnmandont force-pushed the pr/desktop-port-forwarding branch from 78b7ec9 to 986953d Compare August 22, 2026 22:16

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 986953d. Configure here.

),
),
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Session expiry sleep overflows timer

High Severity

expired sleeps until the parent session expiresAt from verifyWebSocketToken, which uses the session row TTL (30 days). That delay exceeds the 32-bit setTimeout limit (~24.8 days), so the sleep can fire immediately, win the race against runBridge, and tear down every new forward right after connect.

Fix in Cursor Fix in Web

Triggered by learned rule: Clamp computed setTimeout delays to 2^31-1 to prevent overflow

Reviewed by Cursor Bugbot for commit 986953d. Configure here.

@t3dotgg

t3dotgg commented Aug 23, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

Closing this PR after an automated pass over open pull requests. Adds a remote port-forwarding architecture across 70 files and depends on #7921.

@t3dotgg t3dotgg closed this Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants