Skip to main content

SSH Channels Hub

English | 中文

Declarative SSH tunnels with auto-reconnect. Define your port forwards once in TOML, start one service, and they all come up — reconnecting automatically when the link drops.

Cross-platform (Linux, macOS, Windows). Written in Rust on top of russh.

Why

Reach for this when ssh -L 3306:127.0.0.1:3306 db.example.com has grown into "I have five of those, my laptop sleeps, my Wi-Fi flakes, and I want them all back when I open the lid."

  • Declarative: tunnels live in config.toml, not in shell history or terminal panes.
  • No host config duplication: host info (HostName / User / Port / IdentityFile) is read straight from ~/.ssh/config — you reference aliases.
  • ProxyJump aware: chain through bastions defined in ~/.ssh/config — alias-only references, publickey auth, and strict known_hosts checks for targets and jumps. See docs/configuration.md §3.4.
  • Auto-reconnect: compatible tunnels share one SSH session; a dropped route reconnects with jittered backoff without disturbing other routes.
  • Both directions in one schema: local-to-remote (ssh -L) and remote-to-local (ssh -R).
  • Foreground or daemon: start attaches to the terminal, start -D detaches; stop / restart / status talk to the running process via IPC.

Quickstart

1. Run or install

Run directly with uvx (recommended, no installation required):

uvx ssh-channels-hub --help

Or install it with pip inside an activated virtual environment:

pip install ssh-channels-hub
ssh-channels-hub --help

The wheel installs the same ssh-channels-hub binary on Linux x86_64, macOS arm64, and Windows x86_64; it does not run through Python.

For development, clone and build the source:

git clone https://github.com/maoXyzt/ssh-channels-hub.git
cd ssh-channels-hub
cargo build --release           # binary at target/release/ssh-channels-hub (or .exe on Windows)

2. Have the host in ~/.ssh/config

Host my-db
  HostName db.example.com
  User myuser
  IdentityFile ~/.ssh/id_rsa

3. Write config.toml in the current directory:

[[channels]]
name      = "db"
hostname  = "my-db"             # alias from ~/.ssh/config
direction = "local->remote"     # ssh -L
local     = "3306"              # listen on 127.0.0.1:3306
remote    = "3306"              # server connects to 127.0.0.1:3306

4. Run

uvx ssh-channels-hub start      # no installation
# or, after pip install:
ssh-channels-hub start          # Ctrl+C to stop
# or, after cargo build:
./target/release/ssh-channels-hub start       # Linux/macOS
.\target\release\ssh-channels-hub.exe start  # Windows PowerShell

Now mysql -h 127.0.0.1 -P 3306 goes through the tunnel.

Tip: ssh-channels-hub generate -o config.toml scaffolds one commented-out [[channels]] block per alias in your SSH config — uncomment and fill in ports. Or cp config.example.toml config.toml for an annotated template.

Configuration

config.toml is looked up in this order (first existing wins):

Platform Path
Current directory (always tried first) ./config.toml
Linux / macOS ~/.config/ssh-channels-hub/config.toml
Windows %APPDATA%\ssh-channels-hub\config.toml

--config /path/to/file overrides the lookup.

Channel schema

[[channels]]
name      = "string"                            # required, unique identifier
hostname  = "ssh-config-alias"                  # required; resolves via ~/.ssh/config
direction = "local->remote" | "remote->local"   # required
local     = "port" | "host:port"                # required, this machine's side
remote    = "port" | "host:port"                # required, the SSH server's side

local and remote always name the address on their respective side regardless of direction. Direction decides who listens:

  • local->remote (≈ ssh -L): this machine listens on local; the server dials remote for each connection.
  • remote->local (≈ ssh -R): the server binds remote; incoming traffic is bridged to local on this side.

Endpoints accept:

  • "3306"127.0.0.1:3306 (bare port, host defaults to loopback)
  • "127.0.0.1:3306" → explicit form
  • "0.0.0.0:8080" → bind on every interface
  • "[::1]:3306" → IPv6

Credentials

~/.ssh/config can't hold passwords or key passphrases. When SSH config alone can't authenticate the host, add an [auth.<alias>] block keyed by the SSH config alias:

[auth.my-db]
password   = "..."          # for password-auth hosts (no IdentityFile in SSH config)
# or
passphrase = "..."          # for encrypted IdentityFile

password overrides any IdentityFile. Hosts that authenticate cleanly via SSH config alone don't need an [auth.*] block at all.

Reconnection (global)

[reconnection]
max_retries             = 0     # 0 = unlimited
initial_delay_secs      = 1
max_delay_secs          = 30
use_exponential_backoff = true

Each retry delay includes jitter. After a finite retry cycle is exhausted, automatic recovery continues with a second exponential backoff capped at 60 seconds; a successful session resets both counters. SSH handshakes are serialized to avoid reconnect storms.

More examples

Listen on every interface so other LAN machines can use the tunnel (mind your firewall):

[[channels]]
name      = "shared-db"
hostname  = "db-server"
direction = "local->remote"
local     = "0.0.0.0:3306"
remote    = "3306"

Expose a local service to the SSH server (ssh -R):

[[channels]]
name      = "expose-local-web"
hostname  = "jumpbox"
direction = "remote->local"
remote    = "8022"              # server binds 127.0.0.1:8022
local     = "80"                # incoming traffic bridges to 127.0.0.1:80 here

(For the server to bind 0.0.0.0:8022, set remote = "0.0.0.0:8022" and enable GatewayPorts in the server's sshd_config.)

Full field reference: docs/configuration.md.

Commands

Command What it does
start Run in the foreground (Ctrl+C to stop).
start -D / --daemon Spawn a detached background process.
stop Tell the running process to exit gracefully (via IPC).
restart Stop the running service, then re-start as daemon.
status Show service state, per-channel health (Connected / Reconnecting / Failed / Stopped), PID, and endpoints. Add --watch / -w to refresh every --interval / -n seconds (default 2).
test Probe each configured local->remote listener to confirm the tunnel is alive. remote->local channels are skipped — verify those server-side.
validate Resolve every channel against ~/.ssh/config and report any problems.
generate -o config.toml Scaffold a config.toml from existing SSH config aliases.
hosts Scan SSH config aliases and show whether each host is supported. Use --format json for script-friendly output.

All commands accept --config /path/to/config.toml to point at a non-default file, and --debug for verbose logging.

Troubleshooting

  • Channel '...' references host alias '...', but no Host ... block exists — typo in hostname, or the alias is missing from ~/.ssh/config.
  • Address(es) already in use — something else is bound to your local address. Change the port or stop the other process. Find the culprit with lsof -i :PORT (Linux/macOS) or netstat -ano | findstr :PORT (Windows).
  • Bind ports < 1024 — needs root (Linux/macOS) or Administrator (Windows).
  • Connection failsssh <alias> manually first to isolate SSH config / network / key permission issues.
  • Encrypted key not unlocking — set [auth.<alias>] passphrase = "...".
  • Full debug outputssh-channels-hub start --debug logs each channel's SSH handshake, channel open, and reconnection attempts.

Further reading

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ssh_channels_hub-0.4.5-py3-none-win_amd64.whl (2.5 MB view details)

Uploaded Python 3Windows x86-64

ssh_channels_hub-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

ssh_channels_hub-0.4.5-py3-none-macosx_11_0_arm64.whl (2.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file ssh_channels_hub-0.4.5-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for ssh_channels_hub-0.4.5-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 74c189bae1dc804b3275711c23610ea06934209009c2a918efb72dae8d486ab2
MD5 cff458c9b6e55687866adcc2df1687df
BLAKE2b-256 b0664d3dc5c2c3c846c16cadcdfc937978d05285b18cc2cee81a6d9b5dc4e0a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for ssh_channels_hub-0.4.5-py3-none-win_amd64.whl:

Publisher: build.yml on maoXyzt/ssh-channels-hub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ssh_channels_hub-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ssh_channels_hub-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 272b7585b3e194a6ba309be854a13d01299744ae68883cca8adf7de4e046703f
MD5 ba4cc7962cf7e0c70522b172219cddb7
BLAKE2b-256 f0f89bd34b038abe5a78a002c96cd40e1ce5e7cf7aedeba3b9bda4760729dd57

See more details on using hashes here.

Provenance

The following attestation bundles were made for ssh_channels_hub-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: build.yml on maoXyzt/ssh-channels-hub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ssh_channels_hub-0.4.5-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for ssh_channels_hub-0.4.5-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c12a75d73e8e051ae4caa64ca0161623fbbaf317577fd3f3efef9ec491ae3a4d
MD5 5604fc2469b183fe813549d81ffb1482
BLAKE2b-256 e1e75d39e28dfa417f8ecd1c62c4551aef7b238d96d24e1adaac95f78735c417

See more details on using hashes here.

Provenance

The following attestation bundles were made for ssh_channels_hub-0.4.5-py3-none-macosx_11_0_arm64.whl:

Publisher: build.yml on maoXyzt/ssh-channels-hub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page