tsshd is a UDP-based SSH server built for unreliable networks. It supports seamless roaming across networks and IP changes, and works well on high-latency links such as cellular connections and unstable Wi-Fi.
tsshd aims to be fully compatible with OpenSSH while providing additional capabilities:
- Survives sleep, wake, and temporary network loss.
- Roams seamlessly across networks and IP changes.
- Supports UDP port forwarding (Local and Remote).
tsshd was inspired by mosh, and the tsshd works like mosh-server, while the tssh --udp works like mosh.
| Feature | mosh ( mosh-server ) | tssh ( tsshd ) |
|---|---|---|
| Low Latency | ?? | ✅ KCP |
| Keep Alive | ✅ | ✅ |
| Client Roaming | ✅ | ✅ |
| Local Echo & Line Editing | ✅ | Not Planned |
| Multi Platform / Windows | mosh#293 | ✅ |
| SSH X11 Forwarding | mosh#41 | ✅ |
| SSH Agent Forwarding | mosh#120 | ✅ |
| SSH Port Forwarding | mosh#337 | ✅ |
| Output Scrollback | mosh#122 | ✅ |
| OSC52 Sequence | mosh#637 | ✅ |
| ProxyJump | mosh#970 | ✅ |
| tmux -CC Integration | mosh#1078 | ✅ |
tssh and tsshd works exactly like ssh, there are no plans to support local echo and line editing, and will not have the mosh issues: mosh#1041, mosh#1281, mosh#1295, etc.
-
Install tssh on the client ( your local machine ).
-
Install tsshd on the server ( the remote host ).
-
Use
tssh --udp xxxto log in. The usage is the same as standard SSH.- Latency-sensitive users can specify the
--kcpoption. - Alternatively, configure the following in
~/.ssh/configto omit the--udpor--kcpoption:Host xxx #!! UdpMode ( Yes | QUIC | KCP )
- Latency-sensitive users can specify the
-
The
tsshplays the role ofsshon the client side, while thetsshdacts assshdon the server side. -
The
tsshfirst logs in to the server normally as an ssh client, and then starts a newtsshdprocess on the server, where each session has its owntsshdprocess. -
The
tsshdprocess listens on a random UDP port in the range 61001–61999 (configurable viaTsshdPort), and sends the port number and session secret keys back to thetsshprocess through the SSH channel. The SSH connection is then closed, andtsshcommunicates withtsshdover UDP.
-
Install with apt on Ubuntu
sudo apt install tsshdsudo apt update && sudo apt install software-properties-common sudo add-apt-repository ppa:trzsz/ppa && sudo apt update sudo apt install tsshd
-
Install with apt on Debian
sudo apt install tsshdsudo apt install curl gpg curl -s 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x7074ce75da7cc691c1ae1a7c7e51d1ad956055ca' \ | gpg --dearmor -o /usr/share/keyrings/trzsz.gpg echo 'deb [signed-by=/usr/share/keyrings/trzsz.gpg] https://ppa.launchpadcontent.net/trzsz/ppa/ubuntu jammy main' \ | sudo tee /etc/apt/sources.list.d/trzsz.list sudo apt update sudo apt install tsshd
-
Install with yum on Linux
sudo yum install tsshd-
Install with gemfury repository.
echo '[trzsz] name=Trzsz Repo baseurl=https://yum.fury.io/trzsz/ enabled=1 gpgcheck=0' | sudo tee /etc/yum.repos.d/trzsz.repo sudo yum install tsshd
-
Install with wlnmp repository. It's not necessary to configure the epel repository for tsshd.
curl -fsSL "https://sh.wlnmp.com/wlnmp.sh" | bash sudo yum install tsshd
-
-
Install with yay on ArchLinux
yay -S tsshdyay -Syu yay -S tsshd
-
Install with Homebrew on MacOS
brew install tsshdbrew install tsshd
-
Install with scoop on Windows
scoop install tsshdscoop bucket add extras scoop install tsshd
Need to allow
C:\Users\<user>\scoop\apps\tsshd\<version>\tsshd.exethrough the firewall for it to work properly. -
Install with Go ( Requires go 1.25 or later )
go install github.com/trzsz/tsshd/cmd/tsshd@latest# latest release go install github.com/trzsz/tsshd/cmd/tsshd@latest # latest development version (main branch) go install github.com/trzsz/tsshd/cmd/tsshd@main
The binaries are usually located in ~/go/bin/ ( C:\Users\your_name\go\bin\ on Windows ).
-
Build from source ( Requires go 1.25 or later )
sudo make installgit clone --depth 1 https://github.com/trzsz/tsshd.git cd tsshd make sudo make install -
Download from the GitHub Releases and install locally
download and install locallysudo apt install /tmp/tsshd_*.deb sudo dpkg -i /tmp/tsshd_*.deb sudo dnf install /tmp/tsshd_*.rpm sudo yum install /tmp/tsshd_*.rpm sudo rpm -i /tmp/tsshd_*.rpm tar zxvf tsshd_*.tar.gz && sudo cp tsshd_*/tsshd /usr/bin/
The following clients or terminals support the tsshd server:
-
trzsz-ssh ( tssh ) – An SSH client designed as a drop-in replacement for the OpenSSH client.
-
rootshell - A free, Metal-accelerated terminal emulator for iPhone, iPad, Vision Pro, and Mac.
┌───────────────────────┐ ┌───────────────────────┐
│ │ │ │
│ tssh (process) │ │ tsshd (process) │
│ │ │ │
│ ┌───────────────────┐ │ │ ┌───────────────────┐ │
│ │ │ │ │ │ │ │
│ │ KCP/QUIC Client │ │ │ │ KCP/QUIC Server │ │
│ │ │ │ │ │ │ │
│ └───────┬───▲───────┘ │ │ └───────┬───▲───────┘ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ ┌───────▼───┴───────┐ │ │ ┌───────▼───┴───────┐ │
│ │ ├─┼────────────────┼─► │ │
│ │ Client Proxy │ │ │ │ Server Proxy │ │
│ │ ◄─┼────────────────┼─┤ │ │
│ └───────────────────┘ │ │ └───────────────────┘ │
└───────────────────────┘ └───────────────────────┘
-
The client
KCP/QUIC ClientandClient Proxyare on the same machine and in the same process, and the connection between them will not be interrupted. -
The server
KCP/QUIC ServerandServer Proxyare on the same machine and in the same process, and the connection between them will not be interrupted. -
If the client doesn't receive a heartbeat from the server for a period of time, it might be due to network changes causing the original connection to be interrupted. In this case, the
Client Proxywill re-establish a connection to theServer Proxy, and communicate through the new connection after successful authentication. From the perspective of theKCP/QUIC Clientand theKCP/QUIC Server, the connection is never interrupted.
-
Client ProxyandKCP/QUIC Clientrun in the same process, andServer ProxyandKCP/QUIC Serverrun in the same process on the server. The proxy implements thenet.PacketConninterface, so packets are exchanged directly in memory rather than through the local network stack. This prevents other local processes from intercepting or injecting packets. -
Server Proxyaccepts packets from only one authenticated client address at a time. If the client reconnects from a new IP or port (for example after a network change), the newClient Proxymust authenticate again. Once authenticated, the new address replaces the previous one and packets from the old address are ignored. -
When a
Client Proxyconnects or reconnects, it sends an authentication packet encrypted with AES-256-GCM. The encryption key is a session-specific key generated by the server and delivered to the client through the SSH channel during login. -
The
Server Proxyverifies the client ID and ensures that the authentication sequence number is strictly monotonically increasing across all previously observed authentication packets to prevent replay attacks. Upon successful verification, the server marks the client address as authenticated and responds with an encrypted authentication acknowledgment. -
Communication between the client and server uses encrypted transports provided by kcp-go or quic-go. QUIC uses TLS 1.3 as the underlying security protocol to ensure confidentiality and integrity of transmitted data, and supports key updates throughout the connection lifecycle. For KCP, a custom rekey mechanism is implemented to periodically rotate encryption keys with forward secrecy, ensuring that all traffic remains encrypted end-to-end.
-
By default, tsshd reuses OpenSSH configuration (default
/etc/ssh/sshd_configon Unix-like systems), ensuring behavior is consistent with OpenSSH. -
If
$XDG_CONFIG_HOME/tsshd/sshd_configexists (default~/.config/tsshd/sshd_config), tsshd prefers it over the OpenSSH config, even if it is empty.
Host xxx
#!! UdpMode Yes
#!! TsshdPort 61001-61999
#!! TsshdPath ~/go/bin/tsshd
#!! UdpAliveTimeout 86400
#!! UdpHeartbeatTimeout 3
#!! UdpReconnectTimeout 15
#!! ShowNotificationOnTop yes
#!! ShowFullNotifications yes
#!! UdpProxyMode UDP
#!! UdpMTU 1400
-
UdpMode:No(the default: tssh works in TCP mode),Yes(default protocol:QUIC),QUIC(QUIC protocol: faster speed),KCP(KCP protocol: lower latency). -
TsshdPort: Specifies the port range that tsshd listens on, default is [61001, 61999]. You can specify multiple discrete ports (e.g.,6022,7022) or multiple discrete ranges (e.g.,8010-8020,9020-9030,10080); tsshd will randomly choose an available port. You can also specify the port on the command line using--tsshd-port. -
TsshdPath: Specifies the path to the tsshd binary on the server, lookup in $PATH if not configured. You can also specify the path on the command line using--tsshd-path. -
UdpAliveTimeout: If the disconnection lasts longer thanUdpAliveTimeoutin seconds, tssh and tsshd will both exit, and no longer support reconnection. The default is 86400 seconds. -
UdpHeartbeatTimeout: If the disconnection lasts longer thanUdpHeartbeatTimeoutin seconds, tssh will try to reconnect to the server by a new path. The default is 3 seconds. -
UdpReconnectTimeout: If the disconnection lasts longer thanUdpReconnectTimeoutin seconds, tssh will display a notification indicating that the connection has been lost. The default is 15 seconds. -
ShowNotificationOnTop: Whether the connection loss notification is displayed on the top. The default is yes, which may overwrite some of the previous output. Set it toNoto display notifications on the current line of the cursor. -
ShowFullNotifications: Whether to display the full notifications or a brief notification. The default is yes, which may output several lines to the screen. Set it toNowill output only one line. -
UdpProxyMode: The default transport protocol isUDP. IfUDPtraffic is blocked by firewalls in your network environment, you can set it toTCPto work around the restriction, though this may introduce additional latency. -
UdpMTU: Sets the maximum transmission unit (MTU) for UDP packets. Default is 1400.
When using tssh as the client, UDP port forwarding is supported.
-
Command-line
-L/-Roptions are extended with audp/prefix (the/can also be replaced with:,_, or-):-L udp/[bind_address:]port:host:hostport -L udp:[bind_address:]port:/remote_socket -L udp_/local_socket:host:hostport -L udp-/local_socket:/remote_socket -R udp/[bind_address:]port:host:hostport -R udp:[bind_address:]port:/local_socket -R udp_/remote_socket:host:hostport -R udp-/remote_socket:/local_socket -
Configuration is similar to
LocalForwardandRemoteForward, with an addedUDPprefix (case-insensitive):UdpLocalForward [bind_address:]port host:hostport UdpLocalForward [bind_address:]port /remote_socket UdpLocalForward /local_socket host:hostport UdpLocalForward /local_socket /remote_socket UdpRemoteForward [bind_address:]port host:hostport UdpRemoteForward [bind_address:]port /local_socket UdpRemoteForward /remote_socket host:hostport UdpRemoteForward /remote_socket /local_socket -
ForwardUdpTimeout: Sets the idle timeout for UDP forwarding sessions; the corresponding forwarding session will be cleared automatically if no data is sent or received within this period to free resources. Default is 5 minutes.
Feel free to email the author lonnywong@qq.com, or create an issue. Welcome to join the QQ group: 318578930.
❤️ Sponsor trzsz ❤️, buy the author a drink 🍺 ? Thank you for your support!