Skip to content

dmsg: native visor WebTransport-carrier dial rejected (ALPN 'no application protocol') when WT shares the quic-carrier UDP port #3837

Description

@0pcom

Summary

A native visor's WebTransport carrier dial to a dmsg server is rejected by the server with CRYPTO_ERROR 0x178 (remote): tls: no application protocol, while an identical dial from a clean standalone process to the same server at the same moment succeeds. So the WT carrier never wins convergence on those servers — the visor falls back to the quic carrier.

This is distinct from servers that are genuinely UDP-unreachable (a container mapping only TCP): those fail WT and quic for everyone.

Evidence

Same server (0371ab4b… @ 45.79.213.251:30087), seconds apart, using the new per-carrier probe (PR #3835):

Clean standalone client — skywire cli svc health --dmsg-server 0371ab4b… --carriers wt,quic,tcp:

CARRIER  STATUS  LATENCY  ADDR
wt       OK      116ms    https://45.79.213.251:30087/dmsg
quic     FAIL    -        45.79.213.251:30087            (fresh-socket quic timed out)
tcp      OK      71ms     45.79.213.251:30087

In-visor — skywire cli dmsg converge --carriers wt,quic,ws,tcp Note:

0371ab4b  tcp  45.79.213.251:30087  (prefers wt)
      ↳ re-dial to wt failed: quic: dial 45.79.213.251:30087:
        CRYPTO_ERROR 0x178 (remote): tls: no application protocol

Ruled out: the ALPN constant (skyquic.WebTransportNextProto == http3.NextProtoH3 == "h3"; webtransport-go only sets NextProtos when empty, so it stays ["h3"]), the pinned cert hash (the error is ALPN, not a cert mismatch), reachability, and backoff. dialSessionWT is identical code in both processes — the divergence is process-global.

Leading hypothesis

For these servers AddressWT (https://IP:30087/dmsg) and AddressUDP (IP:30087) are the same UDP host:port — the dmsg server serves the quic carrier and WebTransport on one UDP socket, ALPN-demuxed. The visor already holds a quic-carrier connection to that endpoint (ALPN skywire-quic-1); its later WT dial to the same UDP endpoint appears to negotiate the wrong ALPN (quic-go connection coalescing / shared packet-routing by server address), so the server's WT side sees no h3 and rejects. A clean process, with no pre-existing quic-carrier connection there, negotiates h3 fine. The inverse also holds: in-visor quic-OK/wt-FAIL vs clean wt-OK/quic-FAIL on the same port.

Reframing

Where AddressWT and AddressUDP are the same UDP endpoint, WebTransport-over-quic and the quic carrier are the same underlying transport to the same socket — preferring wt over quic gains a native visor nothing. WT preference is meaningful for browsers (which can't speak the raw quic carrier) and for servers that serve WT on a distinct port.

Options

  1. Serve WT on a distinct port from the quic carrier — no shared UDP socket, no ALPN collision. Cleanest, but the reporter would prefer to avoid it if the same-port case can be made to work.
  2. Make convergence co-location-aware: skip the wt-over-quic "upgrade" when AddressWT and AddressUDP resolve to the same UDP endpoint (kills the failing churn; keeps WT preference meaningful for browsers / distinct-port servers).
  3. Investigate the quic-go coalescing mechanism to force the in-visor WT dial onto a fresh handshake so same-port WT+quic both work.

Repro

skywire cli svc health --dmsg-server <pk> --carriers wt,quic,ws,tcp (clean per-carrier) vs skywire cli dmsg converge --carriers wt,quic,ws,tcp (in-visor, Note carries the real per-carrier error). Both from PR #3835.

Note: the wasm/browser visor is a separate question — it makes no quic-carrier connections at all (browser JS WebTransport only), so this collision cannot explain a wasm WS→WT failure; that remains independently open.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions