⭐ If ShuttleX saves you time, please star the repo — it takes a second and helps others find it. Thank you!
💬 Help shape it — vote on what to build next in the feature poll.
A modern SSH launcher for the macOS menu bar — inspired by the original SSHMenu and by Shuttle, rebuilt with SwiftUI (@Observable) on a custom NSPanel. Pure arm64 binary for Apple Silicon, not a universal app.
📖 Full documentation is in the Wiki · version history in the Changelog.
- Lives entirely in the menu bar (no Dock icon); a modern dropdown panel with a search field, hover effects, and collapsible groups (collapsed by default, click to expand; matches expand automatically while searching)
- Switchable server source (Settings → Server source):
~/.ssh/config— hosts are read directly (includingIncludedirectives; wildcard hosts like*are ignored)- JSON file at
~/.config/shuttlex/servers.json(created with sample entries the first time you switch to it). The path is configurable in Settings, and the last 3 versions are kept as backups next to the file (servers.backup-…json) on every change — manual or imported - Remote URL — a read-only server list loaded from an
https://URL: a shared "single source of truth" for a team. Inventory only (groups, names, host, port; commands are ignored for safety), cached locally for offline use. Each person keeps their own login user and favorites locally
- Add, edit, duplicate, delete and reorder servers in-app (JSON source) — manage your connection list from a GUI in Settings → "Add / edit servers…" (drag to reorder within a group), no hand-editing of JSON required
- Run a command on a server: give an entry a remote command (e.g.
htop,tail -f …) and ShuttleX runs it over SSH with a TTY; or use a raw custom command for jump hosts/tunnels - Favorites: pin your most-used servers (hover the star in the menu, or toggle it in the editor) to a collapsible ★ Favorites section at the top of the dropdown
- Optional tags (off by default; enable in Settings → General): add comma-separated tags per server, shown as small badges in the menu and matched by search. When off, tags don't appear anywhere
- Choose your terminal app: Terminal, iTerm2, Ghostty, Warp, Alacritty, kitty, WezTerm — only apps that are actually installed are offered (also switchable right in the dropdown footer)
- Choose how it opens (dropdown footer or Settings): new window, new tab, or split pane — depending on what the terminal app supports:
- iTerm2: window, tab, split right, split down
- Terminal.app: window, tab (tab needs the Accessibility permission once, see Notes)
- Ghostty, Warp, Alacritty, kitty, WezTerm: new windows only (can't be steered otherwise from outside; unsupported modes fall back to "new window" automatically)
- When the terminal isn't running yet, a new window is always opened — tab/split only apply once a window exists
- Global hotkey (configurable in Settings → General, off by default): press it from anywhere to open ShuttleX as a centered, Spotlight-style search window — type to filter, ↑/↓ to pick, Enter to connect, Esc to dismiss. Clicking the menu-bar icon still shows the familiar dropdown anchored under it
- Keyboard-friendly: the search field is focused on open; type to filter, ↑/↓ to pick a result (the list scrolls to follow), and Enter to connect — the search clears afterwards
- Copy the SSH command: right-click a server to copy its
ssh …command to the clipboard — to share in chat, paste into another terminal, or drop into a ticket - Default SSH user (JSON / remote sources): a global default login user for entries that don't set their own, overridable per server
- Optional: launch at login (Settings → General)
- Optional update check (off by default): enable it in Settings to get a hint in the menu when a newer release is on GitHub — it checks the public Releases API at most once a day (no account, no tracking) and links to the download; it never auto-installs
brew install --cask dasduo/tap/shuttlexUpdates then come via brew upgrade --cask shuttlex.
Grab the latest from the Releases page:
ShuttleX-<version>-arm64.dmg— open it and drag ShuttleX onto the Applications shortcut.ShuttleX-<version>-arm64.zip— unzip and move the app to/Applications.
However you install it, approve the app once on first launch — it's ad-hoc signed, not notarized, so Gatekeeper blocks it the first time:
xattr -dr com.apple.quarantine /Applications/ShuttleX.app(or System Settings → Privacy & Security → "Open Anyway"). Step-by-step install and troubleshooting: the Installation wiki page.
Releases are produced automatically: push a tag (git tag vX.Y.Z && git push origin vX.Y.Z) and GitHub Actions builds and publishes the .dmg and .zip, and bumps the Homebrew cask. Maintainers: see RELEASING.md.
./build.sh
open build/ShuttleX.appRequirements: an Apple Silicon Mac, Xcode (or the Command Line Tools) with Swift 5.9+, macOS 14+.
For "launch at login" to work reliably, copy the app to /Applications after building:
cp -R build/ShuttleX.app /Applications/Run the test suite (CSV/XLSX parsing, JSON merge, backup rotation, shell-quoting / import safety) with:
swift testShuttleX can generate the JSON directly from a spreadsheet — handy when you manage many servers. Settings → Table import → "Import table …".
Expected columns (order doesn't matter; the header row is detected automatically, and German/English names are recognized):
| User | Server DNS | Server IP | Cluster | Stage |
|---|---|---|---|---|
| deploy | web01.prod.example.com | 10.0.1.11 | web | Prod |
- Format: CSV, TSV, or Excel (
.xlsx). The,and;delimiters are auto-detected. Google Sheets: just export as CSV or Excel (File → Download). - IP or DNS: before importing you choose whether the connection target is the DNS name or the IP address (if the chosen value is missing, it falls back to the other).
- Name: the "Server DNS" (or "Name") column is used verbatim as the display name in the menu — it doesn't have to be a real DNS. You can pick the IP as the connection target independently.
- Grouping: one group is created per combination as
Stage · Cluster(e.g. "Prod · web") — keeping the menu tidy. - Mode: Merge updates entries with the same name and adds new ones (manually maintained servers are preserved); Replace overwrites the JSON file completely.
- Safety: rows whose server fields contain unsafe characters (spaces or shell symbols) are skipped, and all connection targets are shell-quoted when the
sshcommand is built — so importing an untrusted spreadsheet can't inject shell commands.
A sample file lives at examples/servers-sample.csv.
~/.config/shuttlex/servers.json:
{
"groups": [
{
"name": "Production",
"hosts": [
{ "name": "Web server", "user": "root", "host": "web1.example.com" },
{ "name": "Database", "user": "admin", "host": "db.example.com", "port": 2222 },
{ "name": "Database · htop", "user": "admin", "host": "db.example.com", "remoteCommand": "htop" },
{ "name": "Via jump host", "command": "ssh -J jump.example.com root@10.0.0.5" }
]
}
],
"hosts": [
{ "name": "Ungrouped", "host": "example.org" }
]
}host/user/portare assembled intossh user@host -p portremoteCommandruns a command on the server, built on top ofhost/user/port, with a TTY (so interactive tools likehtopwork):ssh -t user@host <remoteCommand>commandallows arbitrary custom commands run verbatim (jump hosts, tunnels, mosh, …) and overrideshost/user/port- top-level
hostsend up in a group called "Servers"
Because the JSON path is configurable (Settings → Server source → Choose…), you can point it at a folder that a cloud service syncs — iCloud Drive, Dropbox, OneDrive — to share one server list across your Macs. ShuttleX re-reads the file every time you open the menu, so changes from another machine show up on the next open.
This is a lightweight setup, not built-in cloud sync, so keep a few things in mind:
- Edit on one Mac at a time. ShuttleX doesn't merge concurrent edits; if two Macs change the file at once, your cloud service may create a "conflicted copy" that ShuttleX won't reconcile.
- The version backups sync too. The
servers.backup-…jsonfiles are written next to your JSON, so they'll appear in the synced folder as well. - With iCloud Drive, keep the file downloaded (avoid "Optimize Mac Storage" evicting it). If iCloud replaces it with a not-yet-downloaded placeholder, ShuttleX sees the path as missing and starts a fresh sample file instead.
Besides the app bundle, ./build.sh produces ShuttleX-<version>-arm64.zip, and ./make-dmg.sh packages a drag-to-install ShuttleX-<version>-arm64.dmg. Copy either to the target Mac and install the app to /Applications.
Because the app is only ad-hoc signed (not notarized), Gatekeeper blocks the first launch when the file arrives via the internet/AirDrop. There are two ways to approve it:
- Via Terminal (simplest): remove the quarantine attribute, after which the app starts normally:
xattr -dr com.apple.quarantine /Applications/ShuttleX.app
- Via System Settings: launch the app once (dismiss the warning), then click "Open Anyway" under System Settings → Privacy & Security. (Since macOS 15, right-click → Open is no longer enough.)
If the app arrives on a FAT/exFAT-formatted USB stick, no quarantine attribute is set and it starts right away.
Distributing without this hurdle would require a Developer ID signature + notarization (Apple Developer account, $99/year). With an account: codesign --sign "Developer ID Application: …" --options runtime followed by xcrun notarytool submit.
- For Terminal.app and iTerm2, macOS asks once on first connect for permission ("ShuttleX wants to control Terminal") — this is required for AppleScript and must be allowed.
- The "new tab" mode in Terminal.app works via a simulated Cmd+T keystroke (Terminal.app offers no AppleScript API for it). For this, ShuttleX must be allowed under System Settings → Privacy & Security → Accessibility. iTerm2 doesn't need this — there tabs and splits go straight through the AppleScript API.
- Splits open in the currently active iTerm2 window; with no window open, a new one is created instead (same behavior for tabs).
- Ghostty, Alacritty, kitty, and WezTerm are launched via command-line arguments; Warp via a launch configuration (
warp://launch/…). - The app is ad-hoc signed (local build). Distributing to other Macs would require a Developer ID signature + notarization.
ShuttleX stands on the shoulders of two projects:
- SSHMenu — the original GNOME panel applet that kept all your SSH connections one click away: grouped, opening a terminal, with your choice of terminal. ShuttleX is essentially that idea, modernized for the macOS menu bar.
- Shuttle — the macOS take that popularized the menu-bar +
~/.ssh/configapproach.
The design choices follow from this lineage: a menu-bar dropdown (not a windowed app), ~/.ssh/config as a first-class source, grouped one-click connections, and a configurable terminal — plus modern additions like search, collapsible groups, table import, version backups, and in-app server management.
The app icon is based on a space-shuttle vector from Pixabay (Pixabay Content License), recolored and composited.


