NAT and connectivity
How the agent finds a direct path through NAT, why some tunnels stay on a relay, and what you can change when they do.
Almost every machine sits behind NAT and has no inbound port open. Two such machines can still talk directly, because both dial outward at the same moment and their NATs each mistake the other's packet for a reply to one already sent. That is hole punching, and it is what ICE automates.
The tunnel does not wait for ICE. It starts on an SXAccess relay and moves to a direct path as soon as one is found.
How a tunnel is negotiated
- 1
Exchange offers over Discovery
The two agents exchange an offer and an answer through the Discovery service, sealed to each other's keys. Discovery forwards messages it cannot read.
- 2
Start on the relay
The tunnel comes up over an SXAccess relay right away, so traffic flows while the search for a direct path goes on. The relay forwards ciphertext it cannot open.
- 3
Gather candidates
In parallel, each agent lists every address it might be reachable on: its LAN addresses (host candidates) and the public address and port a STUN server reports (server-reflexive). A TURN address (relayed) is only added when one of the two devices has no SXAccess relay. The candidates travel over Discovery too.
- 4
Probe every pair
Both sides send connectivity checks across the candidate pairs at the same time. The first pair that answers in both directions wins.
- 5
Move to the winner
The tunnel moves off the relay onto the winning pair. If that path is lost later, traffic goes back to the relay and the agents look for a direct path again.
What makes a direct path fail
| Situation | Effect | What helps |
|---|---|---|
| Symmetric NAT on both ends | Each outbound flow gets a different external port, so the reflexive candidate is stale by the time the far device uses it. | Nothing local; the tunnel stays on the relay. Mobile networks often behave this way. |
| Carrier-grade NAT (CGNAT) | The machine has no public address of its own, and the carrier's NAT picks its external port. If that NAT gives every flow a new port, as a symmetric NAT does, no direct pair gets through. | The relay carries the tunnel when no pair gets through. |
| UDP blocked outbound | No candidate can be verified. | Allow UDP out, or accept the relay, which runs over TLS on TCP 443. |
| Both devices on the same LAN | Usually succeeds immediately on host candidates. | Nothing needed - this path never leaves the building. |
| One device has a public address | Almost always succeeds. | Put routing devices and servers on a public address where you can. |
Reading the connection status
sxaccess info -d prints, for each device, the Path - P2P for direct, Relayed for a relay - the Candidates that won (local and remote candidate types), the Round trip time and the Last handshake. sxaccess info --filter-by-connection-type Relayed lists only the devices whose tunnel is relayed.
In the SXAccess app, open Devices: the Path of each device shows whether its tunnel is direct or relayed, in a column on macOS and Windows and in a device's details on iPhone and Android.
A relayed tunnel is fully encrypted end to end - the relay forwards ciphertext it cannot open. It costs latency and bandwidth, not confidentiality.
sxaccess info -dsxaccess info --filter-by-connection-type RelayedTo compare, keep every tunnel of this device on the relay with SX_RELAY_ONLY=true. reconfigure restarts a running agent by itself. It also replaces the agent's whole service environment: list every variable you set before in the same --service-env, separated by commas. To undo it, run reconfigure again without SX_RELAY_ONLY.
sudo sxaccess agent reconfigure --service-env SX_RELAY_ONLY=truesxaccess stop && sxaccess start --external-ip-map 203.0.113.10/10.0.0.4
