runHeadlessSend only ran multicast discovery, so `--to <alias> --message`
could not reach a peer that is only routable over the tailnet (or another
subnet), even though the TUI and quick-send paths could via watchRemotes.
Start the same watcher in the headless path: known peers from config plus
online Tailscale peers are unicast-probed, the probe handshake records the
peer, and FindPeer picks it up like any multicast discovery.
Verified live against a tailnet-only peer (different subnet, DERP-relayed):
discovered and message delivered. TestFindPeerViaProbe covers the
composition Probe -> NotePeer -> PeerFound -> FindPeer.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Multicast only finds peers on the same LAN. Reach off-LAN boxes by probing
them directly over unicast (works over any routable address; Tailscale is the
easy, secure choice):
- discovery.Probe: unicast POST /register (https->http fallback) with a two-way
handshake, so send and receive both work; offline peers age out.
- internal/tailscale: Peers() shells `tailscale status --json` for online peers.
- config.KnownPeers: persisted manual remotes.
- main.go: watchRemotes goroutine probes knownPeers ∪ tailscale peers every 10s,
in both the normal TUI path and quick-send.
- TUI: `+` on Devices opens an add-remote modal (host/IP/Tailscale name).
- install.sh: interactive local/remote install prompt; remote mode locks port
53317 to the Tailscale interface (ufw), with container/userspace-networking
detection. README documents remote devices.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>