fix(desktop): build browser RtcPeerConnection with domain ICE servers #77

Merged
David merged 1 commit from fix/VAPP-67-browser-ice-config into main 2026-07-03 21:45:22 +02:00
Owner

What

The browser desktop viewer built its RtcPeerConnection with RtcPeerConnection::new() (empty iceServers), so it only ever gathered host / mDNS candidates and never a server-reflexive (srflx) one. VS-82 already wired the agent side: the agent receives the domain ICE config and its answer SDP carries srflx candidates. But with nothing to pair against on the browser side, every candidate pair went failed, the WebRTC data channel never opened, and the desktop tab stayed black for any cross-network (NAT'd) agent.

This is the browser half of VAPP-67: source the browser's iceServers from the same domain.ice_servers the agent is pushed, so both peers gather srflx (and relay when TURN is set) and ICE can pair.

How

  • open_relay_tunnel (server fn) now reads the serverinfo frame's webrtcconfig (the {"iceServers":[...]} RTCConfiguration object) and returns it on TerminalTunnel.webrtc_config. serverinfo is captured before the authcookie request because vervain-server pushes it unconditionally as the first frame on connect, before any client action, so the request-style skim would otherwise discard it.
  • The desktop viewer threads that config through attach -> mount -> build -> RtcCtx, and the new RtcCtx::new_peer_connection builds the peer connection with new_with_configuration({"iceServers":[...]}) when present.
  • No config (domain sets no ICE servers, so serverinfo omits webrtcconfig) falls back to RtcPeerConnection::new() (host candidates only), matching the agent's default IceConfig and prior behavior. A webrtcconfig that does not parse as a JSON object falls back the same way with a console warning rather than failing the upgrade.
  • Only the desktop tunnel (usage 2) consumes the config; the terminal and file tunnels ignore the new field.

Invariant sweep

Every WebRTC-offering peer must build its RtcPeerConnection with the domain iceServers from serverinfo, falling back to unconfigured only when none is supplied. Every RtcPeerConnection construction site in the client (all in desktop.rs) now routes through new_peer_connection. The terminal and files tunnels are WebSocket-only (no WebRTC upgrade), so they are N/A.

Testing

just pre-commit green: fmt, clippy -D warnings, cargo check (server + wasm32-unknown-unknown), and 43 tests pass.

Notes

Relates to VS-82 (agent side). The client cannot be exercised headless here, so the about:webrtc acceptance criteria (populated RTCConfiguration.iceServers, srflx/relay browser candidates, ICE reaching connected) still want a manual cross-network check on a deployed build.

#VAPP-67

## What The browser desktop viewer built its `RtcPeerConnection` with `RtcPeerConnection::new()` (empty `iceServers`), so it only ever gathered host / mDNS candidates and never a server-reflexive (`srflx`) one. VS-82 already wired the agent side: the agent receives the domain ICE config and its answer SDP carries `srflx` candidates. But with nothing to pair against on the browser side, every candidate pair went `failed`, the WebRTC data channel never opened, and the desktop tab stayed black for any cross-network (NAT'd) agent. This is the browser half of VAPP-67: source the browser's `iceServers` from the same `domain.ice_servers` the agent is pushed, so both peers gather `srflx` (and `relay` when TURN is set) and ICE can pair. ## How - `open_relay_tunnel` (server fn) now reads the `serverinfo` frame's `webrtcconfig` (the `{"iceServers":[...]}` `RTCConfiguration` object) and returns it on `TerminalTunnel.webrtc_config`. `serverinfo` is captured before the `authcookie` request because vervain-server pushes it unconditionally as the first frame on connect, before any client action, so the request-style skim would otherwise discard it. - The desktop viewer threads that config through `attach` -> `mount` -> `build` -> `RtcCtx`, and the new `RtcCtx::new_peer_connection` builds the peer connection with `new_with_configuration({"iceServers":[...]})` when present. - No config (domain sets no ICE servers, so `serverinfo` omits `webrtcconfig`) falls back to `RtcPeerConnection::new()` (host candidates only), matching the agent's default `IceConfig` and prior behavior. A `webrtcconfig` that does not parse as a JSON object falls back the same way with a console warning rather than failing the upgrade. - Only the desktop tunnel (usage 2) consumes the config; the terminal and file tunnels ignore the new field. ## Invariant sweep Every WebRTC-offering peer must build its `RtcPeerConnection` with the domain `iceServers` from `serverinfo`, falling back to unconfigured only when none is supplied. Every `RtcPeerConnection` construction site in the client (all in `desktop.rs`) now routes through `new_peer_connection`. The terminal and files tunnels are WebSocket-only (no WebRTC upgrade), so they are N/A. ## Testing `just pre-commit` green: fmt, clippy `-D warnings`, `cargo check` (server + `wasm32-unknown-unknown`), and 43 tests pass. ## Notes Relates to VS-82 (agent side). The client cannot be exercised headless here, so the `about:webrtc` acceptance criteria (populated `RTCConfiguration.iceServers`, `srflx`/`relay` browser candidates, ICE reaching `connected`) still want a manual cross-network check on a deployed build. #VAPP-67
fix(desktop): build browser RtcPeerConnection with domain ICE servers
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m31s
Create release / Create release from merged PR (pull_request) Has been skipped
7e8d7034db
The desktop viewer built its RtcPeerConnection with RtcPeerConnection::new() (empty iceServers), so it only gathered host / mDNS candidates and never a server-reflexive one. The agent side (VS-82) already receives the domain ICE config and gathers srflx candidates, but with nothing to pair against on the browser side every candidate pair failed, the data channel never opened, and the desktop tab stayed black across NAT.

Capture the WebRTC ICE configuration from the serverinfo frame (webrtcconfig, the same domain.ice_servers source the agent is pushed) in open_relay_tunnel and thread it to the desktop viewer, which now constructs the peer connection with new_with_configuration({"iceServers":[...]}) when present. serverinfo is read before authcookie because vervain-server pushes it unconditionally as the first frame on connect, before any client action, so the request-style authcookie skim would otherwise discard it.

When the domain configures no ICE servers the serverinfo frame omits webrtcconfig, and the viewer falls back to an unconfigured connection (host candidates only), matching the agent's default IceConfig and prior behavior. A webrtcconfig that does not parse as a JSON object falls back the same way with a console warning rather than failing the upgrade. Only the desktop tunnel (usage 2) consumes the config; the terminal and file tunnels ignore it.

#VAPP-67
David merged commit a376846ec9 into main 2026-07-03 21:45:22 +02:00
David deleted branch fix/VAPP-67-browser-ice-config 2026-07-03 21:45:22 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!77
No description provided.