SSH Tunnel Manager
Tauri 2 · Rust · React 19v1.0.2

An interface
for ssh -L

Create local port forwards to your Linux servers, watch them come up, close them. No flags to memorise, no guessing why it failed, and no orphaned processes left behind when you close the window.

Windows 10/11 · macOS 12+ · MIT License

Topologyssh -L
  1. Local · your tool
    127.0.0.1:4096
    Listening · probed every 150 ms
  2. ssh -N -L
  3. Local · ssh client
    Authenticated, encrypted
    process must be named ssh
  4. TCP over SSH
  5. Server · dev
    localhost:5432
    resolved from the server
ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:4096:localhost:5432 dev

The window

List, status and actions, one screen

The table carries only what you actually look at: ID, host, local and remote addresses, PID, creation time, status. Running rows can be closed; stopped rows can be started, edited or deleted. The app interface is currently Chinese-only, so the screenshot shows the real UI as it is.

The SSH Tunnel Manager main window: a table of five tunnels, all stoppedThe SSH Tunnel Manager main window: a table of five tunnels, all stopped
Main window · five records

01 — Flags you keep forgetting

Four fields for -L, plus -N and ExitOnForwardFailure, every single time. Here there are only four inputs: host, remote address, local address, bind address.

02 — Proof it really works

A successful login is not a working forward. The app reports success only once the local port is actually listening — and when it fails, it says why.

03 — A clean shutdown

Closing the window only hides it to the tray. Records stay, PIDs go to zero, and the next launch reuses the same config.

Behaviour

How it decides

Every line below is what the code does, not a marketing claim. Details in How it works.

Startup success
The local bind:port is really listening (probed every 150 ms). ssh only binds -L after authentication, so a slow server is never reported as ready early.
Startup failure
The process exits and stderr is classified immediately — port in use, auth failure, connection refused, unresolvable host — or 18 seconds pass with no listener.
Closing
Graceful terminate, wait up to 1.8 s, then force. The record is kept and the PID reset, so the same config can be started again at any time.
Passwords
Handed to ssh through the child process environment only. Never written to tunnels.json, never logged. A wrong password is caught by a pre-check, not after a fake success.
Process check
Before terminating anything the process name must be ssh, so a reused PID cannot get someone else’s process killed.

Under the hood

What it actually runs

There is no embedded SSH library — it calls the system ssh. That means ~/.ssh/config, keys, ssh-agent and known_hosts all keep working exactly as before.

Terminal · one linezsh / pwsh
ssh -N \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -o ConnectTimeout=15 \
  -o StrictHostKeyChecking=accept-new \
  -o BatchMode=yes \
  -L 127.0.0.1:4096:localhost:5432 \
  dev

Split across lines for readability; the arguments are passed as a single array. With password auth, BatchMode=yes becomes NumberOfPasswordPrompts=1 and the password is injected through SSH_ASKPASS.

Download

Installers are published on GitHub Releases

All releases

Windows 10 / 11 · x64

…_1.0.2_x64-setup.exe (NSIS) · …_1.0.2_x64_en-US.msi (WiX)

Download →

macOS 12+ · Apple Silicon

…_1.0.2_aarch64.dmg

Download →

The installers are not code-signed: Windows may show a SmartScreen prompt, and macOS needs right-click → Open the first time. To build from source see Development & release.

Start

First time here? Five steps to a working tunnel.

Quick start