feat: add peer-to-peer LAN tunneling for remote terminal sessions - #8
Merged
Merged
Conversation
Enable discovery and management of terminal sessions across machines
on the same LAN using mDNS (bonjour-service) for peer discovery and
encrypted WebSocket connections (ws) with DH key exchange + AES-256-GCM.
Remote sessions use namespaced IDs (tunnel:{instanceId}:{sessionId})
that route transparently through the existing IPC and renderer flow.
Identity is derived from git email (SHA-256 hash) so only machines
belonging to the same user can discover and connect to each other.
New files: protocol, identity, crypto, discovery, server, client,
manager (main process); tunnel stores, TunnelHostItem (renderer).
Modified: session manager (emit bridging), ipc (tunnel routing),
index (lifecycle), preload (tunnel API), sidebar (Local/Remote tabs),
tab bar (context filtering), new session dialog (remote target),
App.svelte (tunnel event wiring).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The default import `import Bonjour from 'bonjour-service'` resolves to
the wrong export when externalizeDepsPlugin transpiles to CJS require.
The constructor lives at the named export `{ Bonjour }`, not `.default`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The referer.address field is the network-level source IP of the mDNS multicast packet, which can be a gateway/router address on bridged or multi-subnet networks. This caused ECONNREFUSED when connecting to peers since the client would try the router IP instead of the host IP. Now uses service.addresses (from A/AAAA DNS records) with preference for routable IPv4, skipping loopback and link-local. Also explicitly binds WebSocket server to 0.0.0.0 for consistent cross-platform behavior and adds diagnostic logging for address resolution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mDNS is unreliable on Windows — macOS has native Bonjour but Windows often fails to advertise services even with firewall rules. This meant Mac couldn't discover Windows machines on the LAN. Add a UDP broadcast beacon as a parallel discovery mechanism: - Each instance broadcasts a JSON beacon every 5s on UDP port 41832 - Receivers filter by identityHash match (same as mDNS) - Hosts are swept after 20s without a beacon - Both mDNS and beacon feed into the same registerHost() method Also adds reverse discovery: when a client connects to our WebSocket server, we register it as a known host using the TCP socket's remote address, providing a third discovery path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
255.255.255.255 limited broadcast is unreliable across machines — some OS kernels only send it out on one interface and some routers don't forward it. Now enumerates network interfaces and sends to each subnet's directed broadcast address (e.g. 192.168.10.255). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 100ms fixed delay before writing the command was too short for PowerShell which can take several seconds to initialize. Now uses a debounce approach: waits for a 300ms gap in shell output (indicating the prompt is ready) before sending the command, with a 5s fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PowerShell 5.1 (Windows default) doesn't support the || operator. Use $LASTEXITCODE check instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
tunnel:{instanceId}:{sessionId}) that route transparently through the existing IPC/renderer flow — no changes needed to Terminal componentgit config --global user.email(SHA-256 hash) — only machines with the same git email discover each otherArchitecture
10 new files in
src/main/tunnel/:protocol.ts— wire message types, session ID helpersidentity.ts— git email detection, stable instance UUIDcrypto.ts— DH key exchange (modp14), AES-256-GCM encrypt/decryptdiscovery.ts— mDNS publish/browse viabonjour-serviceserver.ts— WebSocket server with handshake, auth, session routing (port retry 9500-9510)client.ts— WebSocket client with request correlation, auto-reconnect (exponential backoff 1s-30s)manager.ts— orchestrator owning Discovery/Server/Clients, bridges SessionManager eventsRenderer (
src/renderer/):TunnelHostItem.svelte— host display with status dot, connect/disconnecttunnels.tsstore — sidebar context, host list, selected machinetunnelHelpers.ts— renderer-side session ID utilities8 modified files: session manager (emit bridging), ipc (tunnel routing + 8 new handlers), index (lifecycle), preload (tunnel API namespace), env.d.ts (types), Sidebar (Local/Remote tabs), TabBar (context filtering), NewSessionDialog (remote target), App.svelte (tunnel event wiring)
New dependencies:
bonjour-service,ws,@types/wsTest plan
ELECTRON_USER_DATA=/tmp/tm1 npm run devandELECTRON_USER_DATA=/tmp/tm2 npm run devtsc --noEmitpasses cleanelectron-vite buildsucceeds🤖 Generated with Claude Code