Parallel remote execution tty - (Yet another parallel ssh/shell)
- Run commands across many hosts with colored, prefixed output.
- Keep an interactive prompt with a per-host shell session.
- Run async jobs in separate SSH sessions and track status.
- Load hosts from args, config groups, or a hosts file.
Requires Go 1.25.
go install github.com/ncode/pretty@latest
pretty host1 host2 host3
pretty -G prod
pretty -H /tmp/hosts.txt
pretty looks for a config file named .pretty in your home directory with a supported extension:
$HOME/.pretty.yaml, $HOME/.pretty.yml, $HOME/.pretty.json, or $HOME/.pretty.toml.
Use --config to point at an explicit path.
Optional keys:
username: SSH username override (falls back to SSH config, then current shell user).known_hosts: path to a known_hosts file for host key verification.groups.<name>: host groups as wrapper objects withhostsand optionaluser.prompt: interactive prompt string (UTF-8 supported).--promptoverrides config.
Example:
known_hosts: /Users/me/.ssh/known_hosts
prompt: "pretty> "
groups:
web:
user: deploy
hosts:
- web1.example.com
- web2.example.com:2222
Host key verification:
- If
known_hostsis set and loads successfully, it is used. - Otherwise
~/.ssh/known_hostsis used if it loads successfully. - If neither can be loaded, host keys are not verified.
- A loaded known_hosts file must contain each host key or connections will fail.
Notes:
- Group entries must use the wrapper schema with a
hostslist. - Auth uses your SSH agent (
SSH_AUTH_SOCK) and IdentityFile entries from SSH config. Load keys withssh-add.
Accepted formats:
host(defaults to port 22)host:portuser@hostuser@host:port[ipv6]:port(required to specify a port with IPv6)user@[ipv6]:port
Hosts files (-H) accept one entry per line in the same formats. Blank lines are ignored.
--config <path>: config file path.--prompt <string>: prompt to display in the interactive shell.-G,--hostGroup <name>: loadgroups.<name>from config.-H,--hostsFile <path>: read hosts from a file (one host per line).-h,--help: help for pretty.
Host selection behavior:
- At least one of positional hosts,
--hostGroup, or--hostsFileis required. - With no positional hosts,
--hostGrouploads only the group. - With more than one positional host,
--hostGroupappends the group. - With exactly one positional host,
--hostGroupis currently ignored. --hostsFilealways appends its hosts.
:help
:list
:status [id]
:async <command>
:scroll
:bye
exit
Notes:
:listshows connection status per host.:statusshows the last normal job plus the last two async jobs;:status <id>targets a single job.:asyncruns a command in a new SSH session per host and returns to the prompt immediately.:scrollenters scroll mode for the output viewport (output scrolling is disabled otherwise); pressescto return to the prompt.- Use Up/Down arrows to navigate command history (persisted in
history_file). Ctrl+Cforwards to remote sessions; press twice within 500ms to quit locally.Ctrl+Zforwards to remote sessions (suspend).
- Starts one persistent SSH shell session per host for interactive commands.
- Wraps each command with a sentinel to capture per-host exit codes.
- Runs async commands in fresh SSH sessions and updates job status as they finish.
- Prefixes output with
host:portand assigns a stable color per host. - Keeps the last 10,000 output lines in the UI buffer.
Use the local SSHD testbed to exercise pretty against three localhost targets.
Generate keys, password, and a ready-to-use config file:
export PRETTY_AUTHORIZED_KEY="$(ssh-add -L | grep 'my-key' | head -n1)"
./scripts/ssh-testbed-setup.sh
Start the testbed:
docker compose -f docker-compose.sshd.yml up -d --build
Re-run setup after the containers are running to populate .pretty-test/known_hosts:
./scripts/ssh-testbed-setup.sh
If you want to use the generated test key instead of an existing agent key:
ssh-add .pretty-test/id_ed25519
Example run:
pretty --config .pretty-test/pretty.yaml -G testbed
Then run whoami to confirm each host responds as pretty.
The generated password is stored at .pretty-test/password.txt for manual ssh testing if needed.
pretty is a tool to control interactive shells across multiple hosts from a single point.
After using polysh for a long time, it came with the motivation to try to write my own parallel shell in Go. In the end the tool worked so well and I decided to open source the code.
- SSH authentication uses the local agent and SSH config IdentityFile entries; there is no keyfile flag.