bug: Desktop stores the relay hostname:443 as directTcp; probe times out and downloads hit the relay vhost
What's broken
After pairing a daemon with paseo daemon pair --relay, Settings → Connections can show both:
Relay (relay.example:443)— correct, chat/control worksTCP (relay.example:443)— same hostname:port stored asdirectTcp
The TCP row is not a daemon. It is the relay HTTP/WebSocket vhost. Desktop then:
- Probes it as a daemon (
GET /wswithout relay query params / wrong protocol) → HTTP 400 or TCP timeout (TCP (relay.example:443) 超时on Windows). - Uses it as the first
directTcpfor file downloads (resolveDaemonDownloadTargetinpackages/app/src/stores/download-store.ts), so even a working Relay (and even a working127.0.0.1:6768tunnel if it is not first) can fail or hit the wrong origin.
Official #offer= parsing only creates type: "relay" (upsertConnectionFromOffer → upsertRelayConnection). connectionFromListen / Add Host TCP will happily parse host:443 as directTcp with no check that the endpoint is a relay.
Expected: either refuse to save a relay hostname as TCP, or never prefer a directTcp whose endpoint equals an existing relay.relayEndpoint. Download/control should use the active connection (see also #2436).
Steps to reproduce
- Run a daemon with
daemon.relay.enabledandlisten: 127.0.0.1:6767(not public). paseo daemon pair --relay --jsonand add the host in Desktop via the#offer=URL. Confirm Connections showsRelay (…:443)only and chat works.- Settings → host → Connections → Add a TCP connection using the same
relay-host:443(TLS on or off). - Observe a second row:
TCP (relay-host:443). - Desktop marks that TCP row connecting/timeout. CLI:
paseo ls --host relay-host:443→Unexpected server response: 400.paseo ls --host tcp://relay-host:443?ssl=true→ same 400. - Files → Download on that host: either
Download host is unavailable(if TCP is down and resolver requires directTcp) or an HTTP request to the relay origin/api/files/downloadinstead of the daemon.
Also happens after Desktop upgrade (0.5.0-beta.5 → 0.5.0) when an existing host already had a relay endpoint: UI can surface the relay hostname as TCP.
Where did this happen
Desktop (Electron)
Paseo version
Desktop 0.5.0; daemons 0.5.0-beta.5
OS version
Windows 11 (client). Daemons: Ubuntu 24.04 and Windows Server.
Agent provider
Not relevant
Provider configuration
No response
Logs
CLI against the relay hostname as if it were a daemon (not the same as wss://…/ws?role=client):
Cannot connect to daemon at relay.example:443: Unexpected server response: 400Cannot connect to daemon at tcp://relay.example:443?ssl=true: Unexpected server response: 400Same host via a real daemon TCP tunnel works:
paseo ls --host 127.0.0.1:6768returns agents.
download-store.ts still:
const connection = daemon?.connections.find((conn) => conn.type === "directTcp") ?? null;host-runtime.ts upsertConnectionFromOffer only inserts type: "relay". connectionFromListen("relay.example:443") returns { type: "directTcp", endpoint: "relay.example:443" }.
Raw daemon log around a working relay control channel (not the failing TCP probe): relay_control_connected after pairing. The TCP probe never completes hello on the daemon.
Screenshots or video
UI rows look like:
TCP (relay.example:443) ← timeout / 超时
Relay (relay.example:443) ← onlineversus a host that was given a real daemon TCP reverse-proxy hostname:
TCP (daemon-proxy.example:443)
TCP (daemon-proxy.example:443)(duplicate TCP is a separate older issue, e.g. #1201).
Source: getpaseo/paseo