Skip to content

Nightly: remote daemon, Tailscale setup, and PWA client#212

Draft
parsakhaz wants to merge 106 commits into
mainfrom
nightly
Draft

Nightly: remote daemon, Tailscale setup, and PWA client#212
parsakhaz wants to merge 106 commits into
mainfrom
nightly

Conversation

@parsakhaz
Copy link
Copy Markdown
Member

@parsakhaz parsakhaz commented May 17, 2026

Nightly integration: remote daemon, Tailscale setup, and PWA client

This PR is the integrated nightly validation branch for Pane's daemon/client split and self-hosted remote runtime work. It now covers the full desktop remote-host flow plus the first install-free browser/PWA client surface.

Latest Product Scope

  • Adds one-command remote setup from Settings with Open in Pane Terminal and Run Setup for interactive Tailscale auth and install-if-needed flows.
  • Makes Tailscale setup more defensive: automatic listen-port selection, elevated tailscale serve recovery where needed, cached Serve access reuse, and clearer retry guidance while Serve provisions.
  • Adds host-side controls and status: live/offline state, connected client labels, create/copy connection code, revoke paired clients, and stop remote host.
  • Adds remote-client lifecycle hardening: reconnect with exponential backoff, per-client terminal visibility tracking, local UI resync when switching back from remote, and normalized remote session timestamps.
  • Adds a Remote Desktop sidebar entry for remote profiles so GUI/Electron/browser testing has an obvious escape hatch.
  • Adds the first Remote Pane PWA/browser client: saved profiles, connection-code import, remote session list, terminal tabs, terminal creation presets, remote desktop link, SSE status, and mobile-first terminal controls.
  • Adds mobile terminal QoL: paste/send managed input, Stop/Esc/Tab/Enter/Up/Down controls, Reset scrollback parity with desktop Ctrl/Cmd+K, and host-provided terminal shortcuts/custom commands.
  • Adds Tailscale-specific failure guidance when a browser/PWA client cannot connect, so users know the client device must have Tailscale running and signed into the same tailnet.

Website/PWA Notes

The PWA is intentionally static and remote-token based, so it can be hosted as a future runpane.com/remote surface without a Pane desktop install. It still requires a valid pane-remote:// code and network reachability to the host, usually Tailscale on the same tailnet. No Pane backend needs to proxy user terminals.

Validation

  • pnpm --filter frontend typecheck
  • pnpm --filter main typecheck
  • pnpm --filter main exec vitest run src/daemon/pwaStaticAssets.test.ts src/daemon/remotePwaBrowserRuntime.test.ts src/daemon/httpApiServer.test.ts src/ipc/daemonRegistryBindings.test.ts src/core/importBoundary.test.ts
  • git diff --check and git diff --cached --check
  • Mobile Playwright smoke against http://localhost:5757/remote.html with a mocked remote daemon: verified no mobile horizontal overflow, terminal-only tabs, shortcut insertion, send-to-terminal, Reset scrollback invoke, and tab visual depth.
  • Commit hook ran root pnpm typecheck and root pnpm lint; lint passed with the existing warning-only baseline.

Manual Validation Notes

  • WSL remote host to macOS desktop client works through Tailscale Serve.
  • Windows remote host to macOS desktop client works once Tailscale Serve has finished provisioning.
  • Browser/PWA client now gives explicit Tailscale guidance on connection failure and is designed to be hosted publicly as a static client once the release is ready.

parsakhaz added 30 commits May 14, 2026 11:20
Use windows-2022 for main-process matrix checks to avoid the broken windows-latest VS 18 image.
Add authenticated HTTP invoke and SSE event transport above the daemon registry.
Keep daemon runtime event fanout shared across local and remote transports.
Copy link
Copy Markdown
Member Author

Update pushed to nightly: 0206bc2 (Add Tailscale IP fallback for remote profiles).

What changed:

  • Remote setup now includes the host's tailscale ip -4 in generated pane-remote:// codes when available.
  • Remote client requests keep the original .ts.net hostname for Host/SNI, but fall back to the stored Tailscale IP when normal OS DNS fails.
  • If a .ts.net profile has no stored IP, the client also tries Tailscale MagicDNS directly via 100.100.100.100 before surfacing the connection error.
  • Importing the same connection code/profile now dedupes by baseUrl + token + transport instead of creating duplicate saved profiles.

Validation:

  • pnpm --filter main exec vitest run src/ipc/remoteDaemon.test.ts src/daemon/setupRemoteHost.test.ts src/daemon/client/remotePaneClient.test.ts passed.
  • pnpm typecheck passed.
  • pnpm lint passed with the existing warning-only baseline.
  • pnpm --filter main exec vitest run passed on rerun: 27 files, 173 tests.
  • Commit hook also reran root pnpm typecheck and pnpm lint successfully.

Copy link
Copy Markdown
Member Author

Follow-up pushed in 7d53b48 after WSL/Mac testing.

What the test showed:

  • The Mac DNS/IP fallback path was no longer the primary blocker.
  • The WSL host had an existing Electron listener on 127.0.0.1:42137, and /health connected but never responded.
  • The current Pane process then failed to start its remote HTTP/SSE transport with EADDRINUSE, so the client timed out waiting for the remote daemon ready event.

Fix:

  • Generated interactive remote-host setup commands now include --auto-listen-port.
  • The setup CLI probes loopback from the requested port and selects the next available port before writing remote config or configuring Tailscale Serve.
  • This prevents Tailscale Serve from forwarding into a stale/wrong listener when the default dev port is already occupied.

Validation:

  • pnpm --filter main exec vitest run src/daemon/setupRemoteHost.test.ts src/daemon/setupRemoteHostCli.test.ts src/ipc/remoteDaemon.test.ts
  • pnpm typecheck
  • pnpm lint (passes with existing warning-only baseline)

Copy link
Copy Markdown
Member Author

Remote setup validation update for 762a46c:

  • Root cause found from the real WSL ~/.pane state: the terminal setup path had --auto-listen-port, but the direct Settings IPC setup path still called setupRemoteHost without auto port selection. That let Settings rewrite the host config back to 127.0.0.1:42137, where an existing Electron listener was already bound and /health timed out.
  • Fix: remote-daemon:setup-host now passes autoSelectListenPort: true; the IPC regression test now asserts that behavior.
  • Validated against the actual dev data dir on WSL, not just unit tests:
    • ~/.pane/config.json now has remote host listenPort: 42138.
    • ss shows the old stale listener on 127.0.0.1:42137 and a healthy Pane remote listener on 127.0.0.1:42138.
    • curl http://127.0.0.1:42138/health returns {"ok":true,"status":"ready","transport":"http+sse"}.
    • tailscale serve status now proxies https://parsa-sl7.taila5e94c.ts.net/ to http://127.0.0.1:42138.
    • curl -k --resolve parsa-sl7.taila5e94c.ts.net:443:100.127.116.52 https://parsa-sl7.taila5e94c.ts.net/health returns the same ready response, validating the Tailscale HTTPS route with the direct Tailscale IP fallback.
    • Raw Pane logs show the last EADDRINUSE at 20:11:00Z, before the corrected setup selected 42138; no newer EADDRINUSE appeared during validation.
  • Checks:
    • pnpm --filter main exec vitest run src/ipc/remoteDaemon.test.ts src/daemon/setupRemoteHost.test.ts src/daemon/setupRemoteHostCli.test.ts
    • pnpm typecheck
    • pnpm lint passes with the existing warning-only baseline.
    • Commit hook reran root typecheck and lint before committing.

Copy link
Copy Markdown
Member Author

End-to-end WSL -> macOS validation update:

  • After clearing stale WSL Electron processes and aligning Tailscale Serve to the live daemon port, macOS Pane successfully imported the new pane-remote://... profile and connected to PARSA-SL7 Pane daemon.
  • Remote terminal commands in the macOS client executed on the WSL host (PARSA-SL7), confirming the remote runtime path is working through Tailscale Serve + HTTP/SSE.
  • Settings showed the imported profile as connected.

Persistence caveat from this validation run:

  • The connection profile/token/config persist in ~/.pane/config.json and the imported macOS profile persists in the desktop config.
  • This specific setup was run with --no-install-service, so the remote daemon process itself is not installed as a boot service. After a WSL restart, the user must start Pane in WSL or run the printed manual daemon command again before the saved macOS profile can reconnect.
  • Tailscale auth and Serve config should persist with Tailscale, assuming the Tailscale daemon starts in the distro, but the Pane remote daemon listener still needs a running Pane/headless daemon process.
  • Follow-up hardening: prevent duplicate setup invocations from racing and rewriting the host config/Tailscale Serve target to a different port.

@parsakhaz
Copy link
Copy Markdown
Member Author

@claude review

@parsakhaz
Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ 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".

@parsakhaz
Copy link
Copy Markdown
Member Author

Update pushed in 224f548 (refactor: audit remote lifecycle transitions).

What changed:

  • Centralized remote client runtime transitions so import/connect/delete/local switch share controller action + config persistence + renderer resync rules.
  • Kept failed import-connect defensive: saves/dedupes the profile, stays on current runtime, and does not resync stale remote state.
  • Extracted renderer remote resync into a named helper that refreshes config, sessions, panels, clears stale active session/panels, and emits project refresh events.
  • Shared Settings/Sidebar remote status + connected-client copy via remoteRuntimePresentation.
  • Added docs/remote-daemon-lifecycle.md with the lifecycle matrix.
  • Added regression coverage for failed import-connect, active/inactive profile delete, local switch, host stop persistence, and stale renderer sessions after resync.

Validation run locally:

  • pnpm --filter main exec vitest run src/ipc/remoteDaemon.test.ts
  • pnpm --filter main exec vitest run src/daemon/client/remotePaneClient.test.ts src/daemon/remoteTransportController.test.ts
  • pnpm test -- tests/smoke.spec.ts --grep "Remote daemon resync"
  • pnpm typecheck
  • pnpm lint (passes, existing warnings remain)
  • pnpm run build:main && pnpm run build:frontend

Note: local Node is v20.19.3, so pnpm prints the existing engine warning for >=22.14.0, but the checks completed successfully.

@parsakhaz
Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ 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".

Adds the browser/PWA remote client, static daemon serving, mobile terminal controls, remote setup connection-code reuse, and host/client affordance APIs.

Includes Tailscale-aware connection guidance and regression coverage for PWA serving, CORS, daemon routing, and remote terminal controls.
@parsakhaz parsakhaz changed the title Nightly integration: daemon/client split stack Nightly: remote daemon, Tailscale setup, and PWA client May 19, 2026
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.

1 participant