Skip to content
/ pretty Public

Parallel remote execution tty - (Yet another parallel ssh/shell)

License

Notifications You must be signed in to change notification settings

ncode/pretty

Repository files navigation

Go Report Card License codecov

pretty

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.

Installation

Requires Go 1.25.

go install github.com/ncode/pretty@latest

Quick start

pretty host1 host2 host3
pretty -G prod
pretty -H /tmp/hosts.txt

Configuration

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 with hosts and optional user.
  • prompt: interactive prompt string (UTF-8 supported). --prompt overrides 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_hosts is set and loads successfully, it is used.
  • Otherwise ~/.ssh/known_hosts is 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 hosts list.
  • Auth uses your SSH agent (SSH_AUTH_SOCK) and IdentityFile entries from SSH config. Load keys with ssh-add.

Host specs

Accepted formats:

  • host (defaults to port 22)
  • host:port
  • user@host
  • user@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.

Flags

  • --config <path>: config file path.
  • --prompt <string>: prompt to display in the interactive shell.
  • -G, --hostGroup <name>: load groups.<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 --hostsFile is required.
  • With no positional hosts, --hostGroup loads only the group.
  • With more than one positional host, --hostGroup appends the group.
  • With exactly one positional host, --hostGroup is currently ignored.
  • --hostsFile always appends its hosts.

Interactive commands

:help
:list
:status [id]
:async <command>
:scroll
:bye
exit

Notes:

  • :list shows connection status per host.
  • :status shows the last normal job plus the last two async jobs; :status <id> targets a single job.
  • :async runs a command in a new SSH session per host and returns to the prompt immediately.
  • :scroll enters scroll mode for the output viewport (output scrolling is disabled otherwise); press esc to return to the prompt.
  • Use Up/Down arrows to navigate command history (persisted in history_file).
  • Ctrl+C forwards to remote sessions; press twice within 500ms to quit locally.
  • Ctrl+Z forwards to remote sessions (suspend).

How it works

  • 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:port and assigns a stable color per host.
  • Keeps the last 10,000 output lines in the UI buffer.

Local SSHD testbed

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.

Why pretty?

pretty is a tool to control interactive shells across multiple hosts from a single point.

Motivation

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.

Limitations

  • SSH authentication uses the local agent and SSH config IdentityFile entries; there is no keyfile flag.

About

Parallel remote execution tty - (Yet another parallel ssh/shell)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published