Written by Claude 4.5 Opus via Claude Code
Forward xdg-open calls from remote SSH sessions to your local browser, using ntfy.sh as a message bus.
Remote Host ntfy.sh Local Mac
┌──────────────┐ ┌───────────┐ ┌──────────────┐
│ xdg-open URL ├───POST /topic──▶ ├───subscribe────▶ open $URL │
└──────────────┘ └───────────┘ └──────────────┘
When working on a remote dev server over SSH, CLI tools like gh auth login, gcloud auth login, or npm login try to open a browser. These fail silently because there's no browser on the remote host. This tool forwards those URLs back to your local machine.
-
Source the shell function:
# Add to ~/.zshrc or ~/.bashrc source /path/to/ssh-reverse-open/shell.zsh
-
Configure SSH to send the environment variable:
# Add to ~/.ssh/config Host devbox SendEnv URL_FORWARD_TOPIC
-
Install the shim:
ssh-ntfy devbox 'bash -s' < install-remote.sh
-
Ensure
~/.local/binis in your PATH (before/usr/bin) -
Ask your sysadmin to add to
/etc/ssh/sshd_config:AcceptEnv URL_FORWARD_TOPIC
ssh-ntfy devbox # connect with URL forwarding enabled
xdg-open https://example.com # opens in your local browser
gh auth login --web # OAuth flow completes locallyssh-ntfygenerates a random topic ID and starts a background subscriber- The topic ID is passed to the remote via
SendEnv - On the remote, the
xdg-openshim POSTs URLs to ntfy.sh - The local subscriber receives the URL and calls
open - When SSH exits, the subscriber is cleaned up
| File | Where | Purpose |
|---|---|---|
shell.zsh |
Local | ssh-ntfy function |
xdg-open |
Remote ~/.local/bin/ |
Intercepts URL opens |
install-remote.sh |
— | Installs the shim |
ssh_config |
Local ~/.ssh/config |
Example SendEnv config |
- Random topic per session — 128 bits of entropy, unguessable
- URL-only — Only
http://andhttps://URLs are forwarded - No eval — The local subscriber only calls
open, never executes received content - Graceful fallback — If ntfy is unreachable, falls through to normal
xdg-open
For additional security, you can self-host ntfy and change the base URL in both scripts.
- Local: macOS with
curlandopenssl - Remote: Linux with
curland~/.local/binin PATH
MIT