Getting started

Thirty minutes, once.

Remotly is personal infrastructure: your herdr host runs the bridge, your phones pair with it, and only devices on your Tailscale tailnet can reach it. Nobody joins an existing bridge; everyone runs their own.

Requirements

WhereWhat
HostLinux or macOS running herdr 0.8 or newer as your user (socket ~/.config/herdr/herdr.sock); Node 24 (fnm install 24); openssl, stty; systemd --user for the service on Linux.
NetworkTailscale on the host and on the phones, same tailnet. Recommended everywhere, required for iPhones. Without it the bridge listens on all interfaces with a self-signed certificate that the Android app pins.
PhonesiPhone or iPad on iOS 17 or newer; Android 8 (API 26) or newer. Lock-screen approval with the unlock check needs Android 12.
Beta inviteAccess to the TestFlight group and the Play internal-testing track, the repository, and the push credentials below. Ask for one.

1. Get the apps

Accept the TestFlight invite on iPhone or iPad and install Remotly. On Android, open the internal-testing opt-in link once, then install Remotly from Google Play; it updates like any other app from then on. Builds land in both places automatically.

2. Prepare the host

herdr must already be running as you. Optional but worth it: resume_agents_on_restore in herdr’s config, so a reboot brings your agents back too. Then let Tailscale hand out certificates:

$ sudo tailscale set --operator=$USER     # lets the bridge request certificates
# then in the Tailscale admin console → DNS: enable MagicDNS and HTTPS Certificates

iPhones refuse the self-signed fallback certificate, so MagicDNS and HTTPS Certificates are not optional for iOS.

3. Install the bridge

# on the host that runs herdr
$ git clone https://github.com/inferenceaftermath/remotly ~/remotly
$ cd ~/remotly/bridge && npm ci --omit=dev
$ node src/main.ts serve            # first run writes ~/.config/remotly/config.json and the TLS material

# as a service that survives logout
$ scripts/install-service.sh        # renders and starts the systemd --user unit remotly-bridge.service
$ loginctl enable-linger $USER
$ remotly-bridge doctor             # Node, herdr socket, Tailscale, certificate, secrets, unit, reachability

The bridge reads herdr’s Unix socket, keeps one structural subscription open, and polls only the pane a phone is watching. Its configuration lives in ~/.config/remotly/config.json; the defaults are right for a Tailscale host:

{
  "listen":   { "host": "auto", "port": 7460 },
  "tls":      { "mode": "auto" },                // auto → Tailscale certificate, else self-signed
  "security": { "require_tailnet": "auto" },    // auto → true when Tailscale is present
  "push":     { "include_excerpt": true, "debounce_ms": 2500 },
  "approvals":{ "strict_verify": true }
}

4. Push credentials

The bridge sends notifications itself, so it needs the app’s push keys. Your beta invite includes them. Store them here:

~/.config/remotly/secrets/AuthKey.p8                  # APNs auth key (iOS notifications)
~/.config/remotly/secrets/fcm-service-account.json    # Firebase service account (Android notifications)
# both mode 0600; without them everything works except notifications

5. Pair your phones

  1. Install Tailscale on the phone, log into the same tailnet and turn it on. Let it start by itself so that a notification action works while the app is closed: iOS → Tailscale → Settings → VPN On Demand; Android → system Settings → Network → VPN → Tailscale → Always-on VPN. Phones allow one VPN at a time.
  2. On the host, print a code and scan it from Remotly → Pair:
$ remotly-bridge pair --ttl 600            # prints a QR and the manual fields; one code per phone
$ remotly-bridge devices list               # who is paired; revoke <id> to throw one out
$ remotly-bridge push-test <device_id>      # a banner should appear on that phone
  1. One code per device. Codes are single-use and expire; five wrong codes lock that phone out for fifteen minutes.
  2. Allow notifications when the app asks, including Time Sensitive on iOS. push-test should then show a banner.

6. Daily use

  • Open the app: the tabs and panes of your herdr session appear live. Tap one to watch it; the bridge fits the program to your phone’s width and releases the fit when you leave.
  • Type in the composer, use the key row (Esc, Tab, Ctrl, arrows, Enter). The + button attaches photos or opens a new terminal on the desktop, ready to run claude, codex or pi.
  • Swipe up to read scrollback. Programs that draw their own screen (Claude Code, vim, less) get swipes as mouse-wheel steps instead; the pane menu can pin either behaviour.
  • Long-press a word to select, drag the handles, Copy. The pane menu also has Copy screen, Tell me when it’s done, and Close terminal.
  • A− / A+ in the title bar step the terminal text size; Settings has Require unlock to approve, Show working agents, and Tell me when it’s done by default.

Bridge commands

CommandPurpose
serveRun the daemon (what the systemd unit runs).
pair [--manual] [--ttl 300]Create a pairing code and print the QR plus the manual fields.
devices list / devices revoke <id>Paired devices; revoke one.
statusherdr connectivity, listener, certificate mode and expiry, devices, push readiness, connected clients.
push-test <device_id>Send a synthetic approval notification to one device.
doctorNode, herdr socket and protocol, Tailscale, certificate, secrets permissions, unit, daemon reachability.

When something is off

  • Pairing fails. Same tailnet on both ends? status should show tls: tailscale. Five wrong codes lock that phone out for fifteen minutes; restarting the bridge clears the counters.
  • iPhone will not connect. It is almost always the certificate. Enable HTTPS Certificates in the Tailscale admin console, run sudo tailscale set --operator=$USER, restart the bridge.
  • No notifications. Check the app was allowed to notify (it asks right after pairing), that status reports apns=ready / fcm=ready, and that the phone is not currently viewing the pane (push for the pane you are looking at is suppressed on purpose).
  • The desktop pane is narrow after a restart. Any herdr layout change restores it; the bridge restores fits itself on a clean shutdown.