Skip to content

feat: ssh tunnel + browser auto-open for OpenClaw web dashboard#2452

Merged
louisgv merged 3 commits intoOpenRouterTeam:mainfrom
AhmedTMM:feat/ssh-tunnel-browser-open-v2
Mar 10, 2026
Merged

feat: ssh tunnel + browser auto-open for OpenClaw web dashboard#2452
louisgv merged 3 commits intoOpenRouterTeam:mainfrom
AhmedTMM:feat/ssh-tunnel-browser-open-v2

Conversation

@AhmedTMM
Copy link
Copy Markdown
Collaborator

Summary

  • SSH-tunnels OpenClaw's web dashboard (port 18791) from remote VM to localhost and auto-opens the browser
  • Adds TunnelConfig to AgentConfig interface and startSshTunnel to ssh.ts
  • Captures gateway token in closure so the same token is used for remote config and browser URL
  • Implements getConnectionInfo() on all SSH-based clouds (AWS, DO, GCP, Hetzner)
  • Local cloud opens browser directly; Sprite gracefully skipped (no standard SSH)
  • Adds USER.md bootstrap file guiding users to the web dashboard for QR code scanning

Closes #2449
Supersedes #2418 (rebased cleanly onto current main)

Test plan

  • bunx @biomejs/biome check src/ — 0 errors (114 files)
  • bun test — 1497 tests pass
  • Manual: spawn run openclaw digitalocean — verify tunnel opens and browser navigates to dashboard

🤖 Generated with Claude Code

OpenClaw runs a web dashboard on port 18791 of the remote VM. This
change SSH-tunnels that port to localhost and auto-opens the browser,
giving users a web UI with zero CLI knowledge needed.

- Add TunnelConfig to AgentConfig interface (agents.ts)
- Add startSshTunnel function with port-finding logic (ssh.ts)
- Capture gateway token in closure so the same token is used for both
  the remote config and the browser URL (agent-setup.ts)
- Wire tunnel into orchestration pipeline between preLaunch and
  interactiveSession (orchestrate.ts)
- Add getConnectionInfo to CloudOrchestrator interface and implement
  in all SSH-based clouds (DO, Hetzner, AWS, GCP)
- Local: opens browser directly at localhost:18791
- Sprite: gracefully skipped (no standard SSH)
- Add USER.md bootstrap to guide OpenClaw users to web dashboard

Closes OpenRouterTeam#2449
Supersedes OpenRouterTeam#2418

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@louisgv louisgv left a comment

Choose a reason for hiding this comment

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

Security Review

Verdict: APPROVED
Commit: d6279b1

Security Analysis

SSH Tunnel Implementation (packages/cli/src/shared/ssh.ts):

  • ✅ Uses proper argument array for Bun.spawn() - no shell interpolation risk
  • ✅ Port range validation: remotePort to remotePort + 10 prevents port exhaustion
  • ✅ Hardcoded tunnel destination 127.0.0.1:${remotePort} - cannot be manipulated
  • ✅ Uses existing SSH_BASE_OPTS security flags (StrictHostKeyChecking, etc.)
  • ✅ Process cleanup via killWithTimeout() prevents zombie processes
  • ✅ Graceful error handling with stderr capture

Browser URL Generation (packages/cli/src/shared/agent-setup.ts:666):

  • ✅ Token generation: crypto.randomUUID().replace(/-/g, "") - secure random 128-bit
  • ✅ Token is properly escaped via jsonEscape() (JSON.stringify) in config files
  • ✅ URL construction: http://localhost:${localPort}/?token=${dashboardToken} - no user-controlled components
  • localPort is validated integer from port scan
  • dashboardToken is generated via crypto, not user-supplied

openBrowser Function (packages/cli/src/shared/ui.ts:130):

  • ✅ Uses argument array for spawn: Bun.spawnSync([cmd, ...args]) - no shell injection
  • ✅ URL passed as array element, not interpolated into command string
  • ✅ Falls back through platform-specific commands (open, xdg-open, termux-open-url)

Integration Points (packages/cli/src/shared/orchestrate.ts):

  • ✅ Tunnel only started for SSH clouds with getConnectionInfo()
  • ✅ Local cloud opens browser directly (no tunnel needed)
  • ✅ Tunnel cleanup on exit: tunnelHandle.stop() prevents port leaks
  • ✅ Try-catch with user-friendly fallback message

USER.md Documentation (packages/cli/src/shared/agent-setup.ts:391-406):

  • ✅ Static markdown text, no injection risk
  • ✅ Guides users to web dashboard for QR code scanning

Tests

  • bash -n: N/A (no shell scripts modified)
  • bun test: 1497 pass, 0 fail (all tests green)
  • curl|bash: N/A (TypeScript changes only)
  • ✅ macOS compat: N/A (TypeScript changes only)

Code Quality

  • ✅ Version bumped: 0.15.380.15.39 (required for CLI changes)
  • ✅ Type safety: All new code uses proper TypeScript types
  • ✅ No as assertions introduced
  • ✅ Follows ESM-only pattern (no require/CJS)

Summary

This PR implements SSH tunneling and browser auto-open for OpenClaw's web dashboard. All security-critical operations use safe argument arrays instead of shell interpolation. The token is cryptographically random and properly escaped in all contexts. Port allocation is bounded and cleanup is guaranteed.

No security issues found.


-- security/pr-reviewer

Copy link
Copy Markdown
Member

@la14-1 la14-1 left a comment

Choose a reason for hiding this comment

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

Reviewed: all CI checks pass (1497 tests, biome, shellcheck, macOS compat), security already approved. Implementation is clean:

  • IIFE closure ensures consistent token between remote config and browser URL
  • SSH tunnel uses argument array (no shell injection)
  • Port range bounded (remotePort..remotePort+10) with graceful fallback
  • Tunnel cleanup on session exit
  • getConnectionInfo() correctly implemented on all SSH clouds

Approving for merge.

-- refactor/issue-reviewer

@louisgv louisgv merged commit c77ca10 into OpenRouterTeam:main Mar 10, 2026
5 checks passed
@AhmedTMM AhmedTMM deleted the feat/ssh-tunnel-browser-open-v2 branch April 7, 2026 00:40
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.

feat: Re-implement SSH tunnel + browser auto-open for OpenClaw web dashboard

3 participants