Tiny native macOS bridge for pasting screenshots into AI agents running on SSH hosts.
sshclip watches your Mac clipboard, and can also process Desktop screenshot
files when macOS permissions allow it. When it sees an image, it
converts/verifies it as PNG, uploads it to explicit SSH hosts, and replaces your
Mac clipboard with the remote path:
/tmp/sshclip-clipboard-20260702-155635-12345.png
Paste that path into Termius, Terminal, Codex, Claude Code, or any remote shell.
Paste this into Codex, Claude Code, or another local coding agent on your Mac:
Set up sshclip for me on this Mac.
Use only existing SSH aliases from my ~/.ssh/config. Do not ask for, print, or
store private keys, passwords, tokens, or secrets. If an SSH alias does not work
non-interactively, stop and tell me which alias failed.
Steps:
1. Clone or open this repo:
https://github.com/heyalerio/sshclip
2. Run ./test.
3. Ask me which SSH alias or aliases to configure, using generic names like
devbox, staging, or prod in any examples.
4. Verify each alias with:
ssh -o BatchMode=yes -o NumberOfPasswordPrompts=0 HOST true
5. Install with:
./install HOST
6. Pin the default target with:
sshclip target HOST
7. If I use Termius and want active-tab detection, run sshclip detect-termius
first and only then set sshclip target termius if the title clearly matches
one configured SSH alias.
8. Tell me to use Cmd+Ctrl+Shift+4, wait a short beat, then paste in the remote
terminal. Do not tell me to use Cmd+Shift+4 for the main workflow.
Before finishing, run ./test again and confirm the LaunchAgent is running as
com.sshclip.agent.
-
Create or confirm an SSH alias:
Host devbox HostName devbox.example.com User you
Tailscale is optional but recommended for private infrastructure. If the SSH host is on your tailnet, keep using a normal SSH alias and point it at the Tailscale name or
100.x.y.zaddress:Host devbox HostName devbox User you
-
Confirm SSH works without prompts:
ssh -o BatchMode=yes -o NumberOfPasswordPrompts=0 devbox true -
Install the watcher:
./install devbox
-
Copy a screenshot on your Mac, wait a short beat, then paste in your remote terminal. Use
Cmd+Ctrl+Shift+4, notCmd+Shift+4:Cmd+Ctrl+Shift+4copies the screenshot image to the clipboard, which is whatsshclipwatches.Cmd+Shift+4saves a file on Desktop instead, which is less reliable for a background LaunchAgent because of macOS privacy permissions.
You should paste text like:
/tmp/sshclip-clipboard-20260702-155635-12345.png
- macOS
- OpenSSH aliases that already work, for example
ssh devbox - macOS built-ins only:
zsh,osascript,sips,ssh,scp - Optional: Tailscale for private-network SSH to hosts
No package manager or background server is required.
./install devboxThe installer creates a LaunchAgent and symlinks:
~/.local/bin/sshclip
Use ~/.local/bin/sshclip directly, or add ~/.local/bin to your shell
PATH before using the shorter sshclip command.
Stop and remove the LaunchAgent:
./uninstallCopy an image or take a screenshot to the clipboard, wait a short beat, then
paste in your remote terminal. For macOS screenshots, use Cmd+Ctrl+Shift+4.
If you see a Codex/Claude X11 clipboard error, you pasted before the image
clipboard was replaced with the remote text path.
Desktop screenshot files are best effort in background mode. macOS privacy
controls may prevent a LaunchAgent from seeing ~/Desktop, depending on the
machine. If Desktop screenshots do not trigger, use a screenshot-to-clipboard
shortcut or run sshclip --once HOST from a terminal that has Desktop access.
Run once instead of watching:
sshclip --once devboxDefault target is the first configured host. Upload to every configured host
only when you explicitly choose all:
sshclip target allTo follow the visible Termius window title:
First check what macOS exposes for Termius:
sshclip detect-termiusOnly enable Termius mode if that output contains exactly one configured host
alias. If Termius cannot be detected or the title is ambiguous, sshclip
does not upload by default.
sshclip target termiusTermius tab/window titles should contain the SSH alias or its compact form:
devbox - frames
staging - logs
prod - codex
For aliases ending in -vps, the compact form also matches. For example,
app matches app-vps.
Pin uploads to one host:
sshclip target devboxMultiple hosts are useful, but target mode all increases disclosure risk
because it sends every image to every configured host. After installing more
than one host, pin one target or verify Termius detection:
./install devbox staging prod
sshclip target devboxsshclip is intentionally not a general file uploader.
- only copied images and
~/Desktop/Screenshot*.pngare considered - clipboard images are written to a private temp directory and converted to PNG
- every candidate is rechecked as a regular non-symlink PNG before upload
- default max upload size is 25 MB
- remote paths are generated by the script under
/tmp - uploaded files are sent with mode
0600 - SSH hosts must be explicit safe aliases passed during install
- no Termius vault access
- no command injection into terminal tabs
- no remote pre-upload shell command
- no public listener or remote agent
- Images are uploaded to every configured host when target mode is
all. - Anything visible in a copied screenshot may be sent to the selected hosts.
- SSH aliases are trusted. If an alias points to the wrong machine, the image is uploaded there.
- Remote files are written under
/tmpand are not automatically deleted by this project. The script sends files with mode0600, but unusual remote filesystems or SSH/SCP implementations may handle modes differently. - A remote host with broken SSH/SCP may fail uploads. Other hosts continue.
- macOS Accessibility may expose different Termius titles than the labels shown inside a terminal pane.
target termiusrequires macOS Accessibility access. Without it, uploads are skipped.- Desktop screenshot file watching may require macOS privacy permissions for the shell/LaunchAgent. Clipboard screenshots avoid this issue.
-
Review the license and copyright owner before publishing.
-
Use a least-privilege remote user instead of
root. -
Prefer SSH over a private network such as Tailscale when managing many hosts.
sshclipdoes not need Tailscale-specific code; the SSH alias hides that detail. -
Add remote cleanup, for example a cron job that removes old screenshots:
find /tmp -maxdepth 1 -type f -name 'sshclip-*.png' -mtime +2 -delete -
Prefer one or two allowlisted hosts. Large allowlists increase accidental disclosure risk.
-
Keep SSH host key checking enabled. Do not use
StrictHostKeyChecking=no. -
Consider setting a lower max size for teams:
SSHCLIP_MAX_BYTES=10000000 sshclip devbox
Check what would be pasted:
pbpasteCheck Termius detection:
sshclip detect-termiusCheck logs:
tail -f ~/Library/Logs/sshclip.log
tail -f ~/Library/Logs/sshclip.err.logIf the clipboard still contains an image, wait briefly and try again. If a host does not receive files, confirm non-interactive SSH and SCP work:
ssh -o BatchMode=yes devbox 'true'
scp screenshot.png devbox:/tmp/sshclip-test.pngIf Desktop screenshots are not picked up by the background service, use
Cmd+Ctrl+Shift+4 to copy screenshots directly to the clipboard, or run:
sshclip --once devboxRun the built-in local checks on macOS:
./testThe test script uses macOS built-ins only. It checks shell syntax, command validation, target handling, and unsafe configuration rejection. It does not require real SSH hosts.
To scan for private strings before publishing, pass your own pattern:
PRIVATE_SCAN_PATTERN='old-host|old-user|private-ip' ./testThese examples apply to foreground runs:
SSHCLIP_MAX_BYTES=25000000 sshclip devbox
SSHCLIP_POLL=0.2 sshclip devbox
SSHCLIP_UPLOAD_TIMEOUT=8 sshclip devbox
SSHCLIP_CONNECT_TIMEOUT=4 sshclip devboxHosts can also come from an environment variable:
SSHCLIP_HOSTS=devbox,staging sshclipThe installed LaunchAgent does not inherit one-off shell environment variables.
To change background settings, edit the EnvironmentVariables block in
~/Library/LaunchAgents/com.sshclip.agent.plist, then reload it:
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/com.sshclip.agent.plist
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.sshclip.agent.plistLogs:
~/Library/Logs/sshclip.log
~/Library/Logs/sshclip.err.log