Open-source project
vaniacer/sshto avatar
vaniacer/sshto

sshto: an SSH menu built from your own config file

Small bash script to manage your ssh connections. It builds menu (via dialog) from your ~/.ssh/config. It can not only connect but also to run commands, copy files, tunnel ports.

654 stars66 forksShellMIT

At a glance

What is it?
sshto is a single bash script that reads your ~/.ssh/config, turns every Host entry into a dialog menu, and then lets you connect, run a command, copy files or tunnel a port from that menu. The command list is a bash array in the script that you edit yourself, which is both its strength and the reason it will never be as polished as an installed package.
Who is it for?
sshto fits a developer who already keeps a large ~/.ssh/config and wants a keyboard driven menu for running diagnostics, moving files and opening tunnels from that same file, and it does not fit a machine where dialog cannot be installed or where an audited, packaged tool is required. Before adopting it, read the cmdlist array in the script, since that array is where you will change everything, and check that the tool honours the Include directive in your config.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 59 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The menu is derived from the config you already have

sshto is a small bash script for managing SSH connections, and the core idea is that the host list is not maintained twice. The script builds a menu, using the dialog utility, from your ~/.ssh/config, and from that menu you can connect to a server, run a command on it, copy files, or tunnel ports.

That derivation is the whole design. A separate tool that stores its own host database asks you to keep a second copy of the same information, and the two copies drift. sshto reads the file that SSH itself reads, so a host you add with a normal editor appears in the menu, and the HostName, Port and User values are the ones already in effect when you type ssh.

The repository is correspondingly small. The top level holds a .github directory, a .gitignore, a LICENSE.md, the README, and the script itself, named sshto, with no other source files. It is a script you copy, not a program you install.

The licence is MIT, the last push was on 2026-08-26, and the repository is not archived. The release list shows v1.02 on 2026-08-26, v1.01 on 2026-06-19 and v1.0 on 2026-06-18, so the project took a month to go from nothing to a numbered release and has been maintaining a roughly two month cadence since.

The audience is a systems administrator or a developer with a dozen hosts in their ssh config who wants a keyboard driven way to reach them, run the usual diagnostic commands and open tunnels.

The command list is a bash array you edit yourself

The command menu in sshto is an array in the script, and the README documents its structure directly. The first column is the command, the second is the description, and a comment line labels the two columns.

bash
    #Command#    #Description#
    "${slct[@]}" #De/Select command
    "Username"   "Change ssh username to \Z1$GUEST\Z0"
    "ls -lah"    "List Files"
    "free -h"    "Show free memory"
    "Custom"     "Run custom command on \Z4$target\Z0"

The escape sequences are the display layer. The Z1 and Z4 sequences set text attributes for the description, so a description can highlight a variable such as the current user or the selected target, and the Z0 sequence resets it.

The README sets out the rules for extending it. Simple commands like ls -la can be added as they are. A list of commands or complicated logic should be added through a function instead. Empty values, written as two single quotes, act as delimiters that group the menu visually.

The stock entries show the scope. There are the diagnostics, ls -lah, free -h, df -ih and df -h, and then the operations: a Custom command and a Custom script run against the target, a Yes entry that answers the ssh prompt, an Info entry for full system information, Fix_id to update the host in known_hosts, Sshkey to push your public key to the target, Alias to copy aliases across, and Copy to move a file or directory to the target. The transfer section has Home and Dest to set the local and remote directories, Upload, Download and Mount and Unmount for a remote folder. The tunnelling section sets a local port and a remote port and starts a tunnel from the target's remote port to localhost's local port, and the last two entries show and edit the ssh config for the host.

Editing that array is how you make the tool yours, and it is also why the tool cannot grow features without your hands on it.

Host comments become descriptions and group separators

sshto reads more than the Host and HostName lines. An optional description can be attached to a host by putting it in a comment after the host name.

bash
Host server1 #Description, it could be more than one word
HostName 192.168.0.1
Port 22
User admin

The same mechanism produces groups. A line of the form a comment with a DUMMY host name and a group name acts as a menu separator.

bash
#Host DUMMY #Rybinsk#

If the placeholder name is inconvenient, for instance because you actually have a host called dummy, the script lets you change the template through the group_id=dummy variable.

The design constraint that makes this work is that all of these additions are comments. SSH ignores them, so the config file stays a valid ssh config and the ordinary ssh command still works exactly as before. The README is explicit that the additions will not break your ssh configs because they are considered as comments. That is the property that lets the tool be opinionated about presentation without taking anything away from the file it reads.

Two behaviours of the session are worth knowing. There is a CONNECT button that jumps straight to the selected server, and the ssh session is not a dead end: pressing CTRL and D, or running exit, brings you back to the sshto command section rather than dropping you to a shell prompt.

Include works, so a large config can be split up

sshto supports the Include directive of ssh configs, so you can split the configuration across multiple files and the menu still shows everything.

The example in the README is a config that includes three sources, a pair of named files and a glob over a conf.d directory.

bash
$ cat ~/.ssh/config
Include config_moscow
Include config_rybinsk
Include conf.d/config_*

That matters more than it sounds. A host list that spans a few personal servers and a dozen company machines is exactly the case where a single file becomes unmanageable, and the usual response is to split it. If the tool did not follow Include, splitting the config would silently drop hosts from the menu, and the tool would be useless for the people with the most hosts.

The listing that goes with it shows the resulting directory, with the config file itself, the two named files and the conf.d directory side by side.

The groups in the example config line up with that structure, with one separator for the servers in one city and another for the servers in another, which is the pattern the group comment syntax is designed for.

Preset variables and the dialog configuration are both editable

There are two configuration surfaces outside the script itself, and both are ordinary files in your home directory.

The first is ~/.sshtorc, which the README describes as the place to tweak all preset variables and functions. The example shows a single line appended to it.

bash
echo "REMOTE=9000  # Remote port for tunneling." >> ~/.sshtorc

The second is the dialog configuration, which dialog itself can create for you and which you then edit in a text editor.

bash
dialog --create-rc ~/.dialogrc
nano ~/.dialogrc

That is a real advantage of building the menu with dialog rather than with a home grown TUI, because the widget behaviour, the colours and the layout are already a supported configuration surface with an official command to generate a starting file.

The script also makes the sshto variables and functions available for adjustment, which is the difference between a script you use and a script you take over. Nothing about the tool prevents you from replacing the menu behaviour entirely, because the menu is a bash function like everything else in the file.

The tunnelling defaults are worth noting as an example of what the preset variables are for. The remote port and the local port used for a tunnel are variables, so a different port can be made the default rather than typed every time, and the menu entries for Local and Remote exist to change them per session.

Installation is a copy into /usr/bin

The installation section is short, and it is worth reading as a description of the project's ambitions as much as a procedure. You clone or download the project, change into its folder, and copy the sshto file into /usr/bin with sudo privileges. The uninstall is the matching removal.

bash
sudo rm /usr/bin/sshto

There is no package, no configuration file to place and no dependency resolution. The script is copied into a directory on your PATH, and it depends on dialog being installed, since the menu is built with it.

The README addresses the case where you cannot or do not want to install dialog by pointing at a separate project by the same author, bashui, described as a dialog-less version of sshto with its own demos. That is a reasonable answer to a real dependency objection, and it also tells you how small the ecosystem around this script is.

There is an asciinema recording of the tool in action, and a video guide by Tom Lawren about ssh config and sshto for anyone who wants the background on the config format itself. A Telegram channel is linked from the README as the place for announcements, and the author asks for support through PayPal.

What a single script does not give you

Three limits are visible, and they are consequences of the design rather than oversights.

The first is the dependency. The menu is built with dialog, so the script needs that utility present, and on a minimal or locked down machine that is not a given. The author's answer is a separate project for a dialog-less version, which is a workaround rather than a fix inside this repository.

The second is the installation model. Copying a file into /usr/bin with sudo is a deliberate simplicity, and it means there is no package manager to update the script, no dependency declarations, and no way to install it for one user without touching a system directory. The README's own upgrade story is not given, which is consistent with a project whose releases are three tags in a year.

The third is the test surface. The repository holds the script, the README and the licence, and no test directory, no lint configuration and no CI beyond the .github directory. For a tool that runs commands on your servers from a menu, that is worth weighing. The commands themselves are yours, since the array is editable, but the script's own behaviour is unverified by anything in the repository.

The alternative to sshto is not another menu tool so much as a different approach. A small shell function that reads your config with a fuzzy finder, or a handful of aliases, avoids the dependency and the install entirely, at the cost of losing the file transfer, the tunnel management and the session handling. What sshto adds over that is the breadth of the command set, and what it costs is the dependency on dialog and the fact that the tool is a file you copy rather than a program you install.

On maintenance, the picture is recent and small. The last push was on 2026-08-26, the same day as the v1.02 release, the repository is not archived, and the licence is MIT, so the script can be read, changed and redistributed with the notice kept.

Editorial conclusion

sshto fits a developer who already keeps a large ~/.ssh/config and wants a keyboard driven menu for running diagnostics, moving files and opening tunnels from that same file, and it does not fit a machine where dialog cannot be installed or where an audited, packaged tool is required. Before adopting it, read the cmdlist array in the script, since that array is where you will change everything, and check that the tool honours the Include directive in your config.

Frequently asked questions

How do I install sshto?

Clone or download the project, change into its folder, and run sudo cp sshto /usr/bin/ to copy the script into your PATH. To uninstall it, run sudo rm /usr/bin/sshto.

What does sshto need to work?

The menu is built with the dialog utility, so it has to be installed. The README points to a separate project, bashui by the same author, as a dialog-less version of the tool for anyone who cannot or does not want to install dialog.

How do I add my own commands to sshto?

Edit the cmdlist array in the script. The first column is the command and the second is the description, simple commands can be added as they are, more complicated logic should be added through a function, and empty values act as delimiters in the menu.

How are host descriptions and groups added to the ssh config?

A comment after the host name becomes the description, for example Host server1 #Description, and a comment line with a dummy host name becomes a group separator, for example #Host DUMMY #Rybinsk#. Both are comments, so ssh itself ignores them and the config keeps working as usual.

Can sshto work with a split ssh config?

Yes. The tool supports the Include directive, so a config that includes named files and a glob over a conf.d directory still produces a complete menu. The README gives an example with Include config_moscow, Include config_rybinsk and Include conf.d/config_*.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. vaniacer/sshto on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vaniacer-sshto.svg)](https://hysenlabs.com/projects/vaniacer-sshto)

Was this useful?