Seamlessly edit files over SSH with a local editor of your choice, using the rmate protocol.
- ✅ Lightweight & Simple - No external dependencies: runs in ~1MB RAM, installs with a single binary
- ✅ Multiple concurrent files with real-time OS-level file watching
- ✅ Cross-platform (Linux, macOS) with statically linked binaries
- ✅ Editor agnostic - VS Code, Sublime Text, Zed, etc.
- ✅ Always available - Runs as standalone service, independent of editor
- RMate client on remote server connects via SSH tunnel to local RMate Launcher
- Server saves file content to temp file and watches for changes with OS-level notifications
- Temp files mirror the remote path under a per-host directory:
~/.rmate_launcher/<hostname>/<remote path>
(e.g.,hostname:/etc/foo/bar.txt
->~/.rmate_launcher/hostname/etc/foo/bar.txt
).
- Temp files mirror the remote path under a per-host directory:
- Server spawns local editor to edit temp file
- Changes trigger
save
commands; editor close triggersclose
command
Originally developed for TextMate, this is a proven protocol for editing remote files through SSH tunnels. It's widely supported with clients in Ruby (original), Bash, Python, Perl, Nim, C, Node.js, and Go. Use any existing client with RMate Launcher - no changes required.
Editor-specific extensions (RemoteSubl, Remote VSCode) require the editor to be running, have inconsistent behavior, and lock you into one editor. RMate Launcher provides consistent functionality across all editors and future-proof remote editing.
# 1. Start server locally (skip this step if already running as a service)
RMATE_EDITOR="code --wait" rmate_launcher &
# 2. Start SSH session with tunnel
ssh -R 52698:$HOME/.rmate_launcher/rmate.sock user@remote-server
# 3. Edit remote files (opens in your local editor!)
# In remote SSH session:
rmate /path/to/remote/file.txt
Add to ~/.ssh/config
for automatic forwarding:
Host myserver.example.com # For specific hosts
RemoteForward 52698 ~/.rmate_launcher/rmate.sock
Host * # For all hosts (optional)
RemoteForward 52698 ~/.rmate_launcher/rmate.sock
On the remote host (after you have rmate
installed and executable, and your SSH session is forwarding as shown above):
echo 'export VISUAL="rmate -w"' >> ~/.zshrc
echo 'export EDITOR="$VISUAL"' >> ~/.zshrc
. ~/.zshrc
Use sudoedit
(aka sudo -e
) so your editor runs unprivileged while sudo writes back with root permissions.
echo 'export SUDO_EDITOR="rmate -w"' >> ~/.zshrc
. ~/.zshrc
sudoedit /etc/ssh/sshd_config # or: sudo -e /etc/hosts
sudo EDITOR="rmate -w" visudo
Download binaries from the releases page:
Platform | Architecture | Binary |
---|---|---|
Linux | Intel/AMD (x86_64) | rmate_launcher-linux-x86_64.tar.gz |
Linux | ARM64 (aarch64) | rmate_launcher-linux-aarch64.tar.gz |
macOS 10.15+ | Intel (x86_64) | rmate_launcher-macos-x86_64.tar.gz |
macOS 10.15+ | Apple Silicon (aarch64) | rmate_launcher-macos-aarch64.tar.gz |
# Download and install (example for Linux x86_64)
curl -L -o rmate_launcher.tar.gz https://github.com/danielpgross/rmate_launcher/releases/latest/download/rmate_launcher-linux-x86_64.tar.gz
tar -xzf rmate_launcher.tar.gz && chmod +x rmate_launcher-linux-x86_64
mv rmate_launcher-linux-x86_64 /usr/local/bin/rmate_launcher
Requires Zig 0.15.1+:
git clone https://github.com/danielpgross/rmate_launcher.git && cd rmate_launcher
zig build -Doptimize=ReleaseSafe # or just 'zig build' for development
For daily use, run it as a system service. Templates are provided to set up the service on macOS and Linux:
mkdir -p ~/.rmate_launcher
cp macos-launchd.plist.example ~/Library/LaunchAgents/com.user.rmate_launcher.plist
# Edit plist: paths, RMATE_EDITOR, RMATE_SOCKET, sandbox settings
launchctl load ~/Library/LaunchAgents/com.user.rmate_launcher.plist
launchctl start com.user.rmate_launcher
mkdir -p ~/.rmate_launcher ~/.config/systemd/user
cp linux-systemd.service.example ~/.config/systemd/user/rmate_launcher.service
# Edit service: paths, RMATE_EDITOR, RMATE_SOCKET
systemctl --user daemon-reload && systemctl --user enable --now rmate_launcher
All configuration is defined in environment variables.
RMATE_EDITOR
- Editor command to run
- Path to the temp file will be passed as the first argument.
- Command must block until editing is done (pass
--wait
flag or similar).
export RMATE_EDITOR="code --wait" # VS Code
export RMATE_EDITOR="vim" # Vim
export RMATE_EDITOR="subl --wait" # Sublime Text
RMATE_SOCKET
- Unix socket path (default:~/.rmate_launcher/rmate.sock
)RMATE_PORT
/RMATE_IP
- Legacy TCP options (default:52698/127.0.0.1
, less secure)
Since RMATE_EDITOR
can be any command, you can use a bash script to launch different editors based on file patterns. The script receives the mirrored temp path (e.g., ~/.rmate_launcher/<hostname>/...
), so you can route by hostname, remote path, or file extension:
#!/bin/bash
# ~/.rmate_launcher/editor-selector.sh
case "$(basename "$1")" in
*.md|*.txt|README*) zed --wait "$1" ;; # Docs in Zed
*.js|*.ts|*.json|*.html) code --wait "$1" ;; # Web dev in VS Code
*) subl --wait "$1" ;; # Everything else in Sublime
esac
chmod +x ~/.rmate_launcher/editor-selector.sh
export RMATE_EDITOR="$HOME/.rmate_launcher/editor-selector.sh"
Prerequisites: Zig 0.15.1+
zig build # Development build
zig build -Doptimize=ReleaseSafe # Optimized build
zig build test # Run tests
zig build -Dtarget=x86_64-linux-gnu -Doptimize=ReleaseSafe # Cross-compile
# Run locally
export RMATE_EDITOR="code --wait" && zig build run
File Watching: Uses native OS APIs - kqueue
on macOS/BSD and inotify
on Linux - for efficient, real-time file change notifications without polling or external dependencies.
Threading: Multi-threaded architecture with dedicated threads for the main accept loop, individual client handlers, and per-file watchers.
Static Compilation: Direct use of native OS APIs enables static compilation to a single binary with zero runtime dependencies.
MIT