SSH terminal for Mac developers
The SSH terminal for Mac that makes remote dev boxes feel local.
lpm is a native SSH terminal for Mac that imports your ~/.ssh/config, forwards remote ports to localhost, and keeps remote services in panes beside your local stack. No separate SSH client, no hand-typed ssh -L, no orphan tunnels.
Projects, terminals, Claude Code or Codex — one click each
Click anywhere below. Switch projects, start services, launch Claude Code or Codex — all running live in your browser, right now.
Your remote dev box lives in a different window from your local stack
Every modern Mac developer works partly remote — a staging server, a Linux build box, a cloud workstation, a bastion-fronted EC2. That split between “local terminal” and “ssh session” shows up as friction every hour.
You context-switch between local panes and SSH sessions all day
Your local API streams logs in one window. A second window holds ssh user@build-server for the remote service. A third tab is running an ssh -L tunnel so the browser can reach it. Three windows to debug one feature, and every time you tab between them you lose your place.
Port forwarding gymnastics break every time the remote restarts
You hand-type ssh -L 3000:localhost:3000 user@build-server, the remote dev server restarts, the listener dies, and you re-type the command from shell history. Sometimes you forget which tab the tunnel was in and lsof the orphan ssh process out by hand. The work was supposed to be the feature, not the tunnel.
Re-typing the host, user, port, and key your ~/.ssh/config already knows
Your ~/.ssh/config already has Host build, ProxyJump bastion, the right port, and the right identity file. Too many workflows still make you re-enter or copy that data into a separate vault, a saved profile, or a different connection string. The config is already there; the terminal should use it.
An SSH terminal that imports your config, forwards your ports, and runs your remote services next to local ones
Six capabilities that change how remote Mac development feels when the terminal understands SSH instead of just hosting it.
Pick any host from ~/.ssh/config
Open the SSH project picker and a dropdown appears, populated from your ~/.ssh/config. Selecting a host pre-fills the host alias, user, port, and identity file in one click. Include directives are followed up to four levels deep, so split configs at ~/.ssh/config.d/work show up too. Wildcard and Matchblocks are skipped because they aren't pickable hosts.
Remote port forwarding with a readiness check
Type a remote port, leave the local port blank, hit Enter. lpm spawns the forward and polls localhost:<port> until something actually accepts a TCP connect — only then does the success toast appear. The toast and the working tunnel are in sync, so you click the link and it works the first time. No more guessing whether ssh -L actually came up.
Remote services in panes, beside your local ones
Services declared in the project's YAML run on the remote host but stream into lpm panes the same way local services do. Switch between staging-api (remote) and frontend (local) like they're the same shape — because in lpm they are. One native Mac window holds the whole stack regardless of which side of the SSH boundary each piece lives on.
Action mode: run remote, or sync and run local
Each action declares mode: remote (run the command on the host over ssh) or mode: sync (rsync the remote source tree into a local mirror, run a local tool against it, push changes back). Local formatters, refactors, and AI coding sessions get to act on remote source without you shuttling files. The ssh action mode flips per-action, so each step picks the right side of the wire.
Connection multiplexing, ready when the tunnel is
Every ssh invocation shares an OpenSSH ControlMaster channel — the first auth pays the cost (including any 2FA on a jump host) and every subsequent service start, action run, and terminal open reuses the channel. Forwards report success only after the local listener actually accepts a connection. Server keepalive surfaces a dropped link promptly instead of leaving you staring at a dead pane.
Per-project remote profile, isolated lifecycle
Each project carries its own ssh: block — host, user, port, key, remote directory — alongside its services and actions. Forwards, port pollers, and rsync mirrors are scoped to that project. Stop the project and every forward dies; quit the app and nothing leaks. prod, staging, and your local copy are three peer projects, one click apart.
What changes when your terminal speaks SSH the way you do
Four concrete wins for Mac developers whose work crosses the SSH boundary.
You stop hand-typing `ssh -L` for every remote dev server.
Declared service ports auto-forward at start. Ad-hoc binds — a compose port, a one-off debug server — surface as one-click suggestions in the Ports popover the moment they appear on the remote. The success toast only fires when localhost:<port> actually accepts a connection, so the link in the toast works the first time.
You stop re-entering host, user, port, and key data your `~/.ssh/config` already knows.
The picker reads your existing hosts and keeps the selected Host alias intact, so OpenSSH can still apply alias-scoped options such as HostName, ProxyJump, ProxyCommand, Port, and IdentityFile. Creating a new SSH project drops to four clicks: pick host, confirm, save, start. The ~/.ssh/config file stays the source of truth.
You stop juggling a local terminal and a remote SSH window.
Local services and remote services run in adjacent panes inside one native Mac window. Switching between projects (prod, staging, your local copy) is one sidebar click; running state is preserved per project. The split between "local terminal" and "ssh session" stops existing as a UI concept.
You stop losing forwards and tunnels when something restarts.
Forwards are owned by the project. Stop the project and every forward dies cleanly. Restart and lpm re-establishes them. Quit the app and nothing leaks — no orphan ssh processes hiding in ps, no lsof archaeology to find a tunnel you started yesterday.
Three real remote-dev scenarios your Mac terminal should make trivial
Three concrete moments where the local-vs-remote split costs real time — and how lpm collapses them into one window.
Onboard to a remote dev box without typing a single connection detail
A teammate hands you their ~/.ssh/config snippet — a Host devbox entry with ProxyJump bastionand the right key path. You paste it into your config, click “Add a project” in lpm, choose “SSH Host”, and pick devbox from the dropdown. Pick any host from ~/.ssh/config and the form fills itself while preserving the devbox alias, so OpenSSH still applies the ProxyJumprule. The first ssh invocation prompts for your bastion 2FA once; from then on, the multiplexed channel stays open and every service, action, and terminal reuses it. You’re inside the dev box without typing a host, a user, a port, or a key path.
Push a hotfix to staging without stopping your local stack
Your local frontend and api are streaming logs in two panes. A bug needs to ship to staging fast. Open the staging project (already configured against the remote host), run migrate as an action with mode: remote, and watch the staging API pane stream the deploy output. Forward the staging API port to localhost from the Ports popover to verify the fix in your browser. Your local panes never stopped — when you’re done, click back to the local project and pick up exactly where you were.
Forward a remote dev server to localhost the moment it starts
You start the remote project’s api service. It prints Listening on http://0.0.0.0:8080 into its pane. lpm sees the URL in the output, matches it against the declared service port, and auto-forwards — the toast reads Auto-forwarded :8080 → http://localhost:8080. Open the URL locally; your browser is talking to the remote process through the SSH channel without you typing a single -L flag. Stop the project and the forward dies cleanly. No orphans, no lingering tunnels.
lpm vs dedicated SSH clients, Mac terminals, raw OpenSSH, and editor remotes
The distinction is not whether the other tools can SSH or forward ports. Many can. lpm is different because SSH is part of the project lifecycle: services, actions, panes, and forwards are managed together.
| Capability | lpm | Dedicated SSH client | Terminal running raw ssh | raw OpenSSH | Editor Remote-SSH |
|---|---|---|---|---|---|
| Reads ~/.ssh/config hosts without replacing OpenSSHlpm uses the selected Host alias when it connects, so OpenSSH remains responsible for options like HostName, ProxyJump, ProxyCommand, Port, and IdentityFile. | |||||
| Remote services run as project panes beside local servicesThis is the lpm project model: services, actions, terminals, and SSH settings live together instead of being separate saved sessions. | |||||
| Declared remote service ports auto-forward after detectionlpm watches remote listening ports for SSH projects and auto-forwards ports declared in the project's services config. | |||||
| Manual forwards wait for localhost readinessWhen you add a forward, lpm waits until the local listener accepts a TCP connection before reporting success. | |||||
| Project stop cleans up the SSH forwards it startedForwards are owned by the lpm project lifecycle, not by whichever tab happened to run an ssh command. | |||||
| Run local tools against a remote tree with mode: synclpm can rsync ssh.dir into a local mirror, run a local action, then push changes back to the remote host. |
lpm
- Reads ~/.ssh/config hosts without replacing OpenSSH
- Remote services run as project panes beside local services
- Declared remote service ports auto-forward after detection
- Manual forwards wait for localhost readiness
- Project stop cleans up the SSH forwards it started
- Run local tools against a remote tree with mode: sync
Dedicated SSH client
- Reads ~/.ssh/config hosts without replacing OpenSSH
- Remote services run as project panes beside local services
- Declared remote service ports auto-forward after detection
- Manual forwards wait for localhost readiness
- Project stop cleans up the SSH forwards it started
- Run local tools against a remote tree with mode: sync
Terminal running raw ssh
- Reads ~/.ssh/config hosts without replacing OpenSSH
- Remote services run as project panes beside local services
- Declared remote service ports auto-forward after detection
- Manual forwards wait for localhost readiness
- Project stop cleans up the SSH forwards it started
- Run local tools against a remote tree with mode: sync
raw OpenSSH
- Reads ~/.ssh/config hosts without replacing OpenSSH
- Remote services run as project panes beside local services
- Declared remote service ports auto-forward after detection
- Manual forwards wait for localhost readiness
- Project stop cleans up the SSH forwards it started
- Run local tools against a remote tree with mode: sync
Editor Remote-SSH
- Reads ~/.ssh/config hosts without replacing OpenSSH
- Remote services run as project panes beside local services
- Declared remote service ports auto-forward after detection
- Manual forwards wait for localhost readiness
- Project stop cleans up the SSH forwards it started
- Run local tools against a remote tree with mode: sync
What Mac developers ask before using lpm as their SSH terminal
Does lpm replace Termius as my SSH client on Mac?
For developers who want their terminal to handle remote work alongside local services, yes — lpm imports `~/.ssh/config` directly (no separate host vault), runs remote services in panes next to your local ones, and forwards ports without leaving the window. If you specifically need a saved-snippet library or an SFTP file browser, Termius still does those things; lpm is a terminal-first SSH workspace, not a feature-parity Termius alternative on Mac. For most remote-dev workflows, the terminal-first approach replaces the dedicated client entirely.How does lpm import my `~/.ssh/config`?
When you add an SSH project, lpm reads `~/.ssh/config` (and any files referenced by `Include` directives, up to four levels deep), parses out the non-wildcard `Host` blocks, and shows them in a dropdown. Pick any host from `~/.ssh/config` and lpm pre-fills the host alias, user, port, and identity file in the form. lpm connects through that Host alias, so OpenSSH can still apply alias-scoped options such as `HostName`, `ProxyJump`, and `ProxyCommand`. The config import is one read, and your `~/.ssh/config` stays the source of truth.Can I forward a remote port to localhost without typing `ssh -L`?
Yes — that's the whole point of the Ports popover. Type the remote port, leave the local port blank, hit Enter; lpm spawns the forward, polls `localhost:<port>` until something actually accepts a connection, and only then surfaces the success toast. So you know the tunnel is usable, not just spawned. Declared service ports auto-forward at start, and ad-hoc binds discovered on the remote surface as one-click suggestions — remote port forwarding without the `ssh -L` archaeology.Does lpm work with a jump host or bastion (`ProxyJump`)?
Yes, when the jump host is part of the selected `Host` entry in your OpenSSH config. lpm saves the Host alias and invokes OpenSSH with that alias, so options such as `ProxyJump bastion` or `ProxyCommand` remain in OpenSSH's hands. The first connection prompts for whatever your bastion requires (key passphrase, 2FA); the multiplexed channel keeps it open after that, so later services, actions, and terminals can reuse it.What's the difference between `mode: remote` and `mode: sync` for actions?
This is the ssh action mode switch on each action. `mode: remote` (the default for SSH projects) runs the action's command on the remote host over ssh — useful for a deploy, a migration, a remote build. `mode: sync` rsyncs the remote source tree into a local mirror, runs the action locally against the mirror, and pushes changes back — so a local tool (a code formatter, an IDE refactor, an AI coding session) can act on remote source without you shuttling files manually. Each action picks its mode independently.Is lpm a good iTerm2 or Warp alternative for SSH work specifically?
Both iTerm2 and Warp are capable Mac terminals, and raw `ssh` inside either can use your OpenSSH config. lpm is different because it adds a project model around the SSH session itself: a host picker reading `~/.ssh/config`, remote services in panes beside local ones, port forwarding with readiness checks, remote port suggestions, and per-project lifecycle for forwards. If your day is mostly local terminal work with the occasional `ssh user@host`, a general terminal is fine. If you cross the local/remote line every hour, lpm is built for that workflow.
Your local stack and your remote dev box, finally in the same window.
Free, native, and ready in two minutes.
Download a native macOS binary, drag to Applications, open the picker, and choose a host from your ~/.ssh/config. lpm auto-forwards declared remote service ports when it detects them, offers one-click forwards for other discovered ports, runs remote services in panes alongside local ones, and cleans up the ssh forwards it starts. Works on Intel and Apple Silicon Macs running macOS 12 or later.