A Rust rewrite of the classic macOS Shuttle menu-bar app for launching SSH sessions, terminal commands, and URLs from a simple JSON config.
Download latest release · Website · Installation instructions · Config documentation
Note
This repository is experimental software provided as-is, ported to Rust exclusively using AI Agents. Use it at your own risk; we assume no liability for issues, data loss, security problems, or operational impact.
Shuttle lives in your macOS menu bar. Click the icon, choose a host or command, and Shuttle opens it in your preferred terminal.
This project is a fork and Rust rewrite of the original, now-unmaintained Shuttle project.
Use it for:
- SSH shortcuts for servers and environments
- Nested menus for teams, projects, or datacenters
- Commands that should open in Terminal.app, iTerm, Ghostty, cmux, or background
screen - URLs such as dashboards, SSH URLs, or local files
- Importing hosts from
~/.ssh/config
- Open the latest release.
- Download
Shuttle.zipfrom Assets. - Unzip it.
- Drag
Shuttle.appinto/Applications. - Open Shuttle from
/Applications.
Because current release builds may not be Apple-notarized yet, macOS can show a warning that the app is damaged or from an unidentified developer. If that happens, run:
xattr -dr com.apple.quarantine /Applications/Shuttle.app
open /Applications/Shuttle.appFuture notarized releases should not need this workaround, but notarizationm is not on the roadmap atm.
Requirements: macOS and the Rust toolchain.
./scripts/check-rust.sh
./scripts/build-rust-app.sh
cp -R target/release/Shuttle.app /Applications/
open /Applications/Shuttle.appOn first launch, Shuttle creates a config file at:
~/.config/shuttle/config.json
Open the menu-bar icon and choose Configuration to edit, import, or export your config.
{
"terminal": "Ghostty",
"open_in": "tab",
"show_ssh_config_hosts": true,
"hosts": [
{ "cmd": "ssh prod.example.com", "name": "Production" },
{ "cmd": "https://grafana.example.com", "name": "Grafana" },
{
"Staging": [
{ "cmd": "ssh web-1.staging", "name": "Web 1" },
{ "cmd": "ssh db-1.staging", "name": "Database" }
]
}
]
}Save the file, then reopen the Shuttle menu. The app reloads when the config changes.
| Key | Description | Values |
|---|---|---|
editor |
App used by the Configuration menu | "default", "nano", "vi", etc. |
launch_at_login |
Start Shuttle on login | true / false |
terminal |
Preferred terminal | "Terminal.app", "iTerm", "Ghostty", "cmux" |
open_in |
Default launch mode | "new", "tab", "current", "virtual" |
show_ssh_config_hosts |
Import SSH config hosts | true / false |
backend |
Explicit backend override | See Backends |
strategy |
Backend strategy hint | See Strategies |
| Key | Description |
|---|---|
cmd |
Command, URL, or SSH command to launch |
name |
Menu label |
inTerminal |
Per-host mode: "new", "tab", "current", "virtual" |
theme |
Terminal profile/theme |
title |
Terminal window/tab title |
backend |
Per-host backend override |
strategy |
Per-host strategy override |
Wrap hosts in a named object:
{
"hosts": [
{
"Production": [
{ "cmd": "ssh web1", "name": "Web 1" },
{ "cmd": "ssh web2", "name": "Web 2" }
]
}
]
}Prefix a menu item with [aaa] to control sort order. Add [---] to place a separator after it. These markers are hidden in the menu.
{ "cmd": "ssh prod", "name": "[aaa][---]Production" }When show_ssh_config_hosts is enabled, Shuttle reads:
~/.ssh/config/etc/ssh/ssh_config
Imported hosts appear as ssh <alias> menu items. You can customize names with comments:
Host prod
HostName prod.example.com
# shuttle.name Servers/ProductionHosts with wildcards, dot-prefixed names, or matching ignore filters are skipped.
To use a custom main config path:
echo '/path/to/config.json' > ~/.shuttle.pathRemove it to return to the default:
rm ~/.shuttle.pathAn optional alternate config can live at ~/.shuttle-alt.path, ~/.config/shuttle/alt.json, or legacy ~/.shuttle-alt.json. Alternate hosts are appended to the main menu.
Legacy ~/.shuttle.json is migrated automatically to ~/.config/shuttle/config.json when no new config exists yet.
The easiest way to choose a backend is the terminal key:
{ "terminal": "Terminal.app" }
{ "terminal": "iTerm" }
{ "terminal": "Ghostty" }
{ "terminal": "cmux" }For precise control, use backend globally or per host.
| Backend | Config value | Notes |
|---|---|---|
| Terminal.app | terminal-app |
Supports new windows, tabs, current window, and virtual mode via AppleScript |
| iTerm | iterm-stable, iterm-nightly |
Supports stable/nightly iTerm variants via AppleScript |
| Ghostty open | ghostty-open |
Opens new Ghostty windows without Automation permission |
| Ghostty AppleScript | ghostty-applescript |
Supports tabs/current windows; requires Ghostty 1.3+ and Automation permission |
| cmux CLI | cmux-cli |
Sends commands to cmux workspaces/focused surfaces |
| cmux socket | cmux-socket |
Uses cmux Unix socket JSON API |
| screen | screen |
Runs commands detached in the background |
| Strategy | Description |
|---|---|
default |
Backend-specific default |
workspace |
Target a named workspace, mainly for cmux |
socket |
Use socket API, mainly for cmux |
applescript |
Use AppleScript automation |
Backend precedence:
- Per-host
backend/strategy - Top-level
backend/strategy - Legacy
terminal,iTerm_version, andopen_in - Default: Terminal.app, tab mode
The menu contains:
- Configured hosts and imported SSH hosts
- Check for Updates… when update support is available
- Configuration for editing/importing/exporting config
- About Shuttle
- Quit
See docs/troubleshooting-rust-port.md for common issues:
- macOS quarantine / damaged app warnings
- Config parse errors
- Automation permissions
- Missing terminal apps
- cmux socket access
./scripts/check-rust.sh
./scripts/build-rust-app.sh
./scripts/package-release.shUseful docs:
