Documentation
¶
Overview ¶
Package relay implements a DERP-style relay fallback for peers that cannot establish a direct UDP path via NAT hole punching (e.g. symmetric NATs).
The relay is a volunteer mesh peer — never the coord server. Clients connect to it over TCP and open a Noise IK-encrypted channel (X25519 + SHA-256 + ChaCha20-Poly1305) using the relay's pinned static key. The handshake's encrypted payload carries the *remote peer's* X25519 public key; the relay derives the rendezvous channel from that key plus the initiator key it just authenticated (ChannelID), and splices the two connections that arrive on the same channel from opposite directions.
Because the channel is derived from the authenticated initiator key rather than from a client-supplied channel ID, a third party who merely knows both peers' public keys cannot join or hijack a pair's channel — only connections authenticated as one of the two peers map onto it. (A malicious relay can still refuse service or splice wrongly, but it learns nothing and no honest relay will pair an impostor.)
The relay is blind: payloads flowing over a channel are the peers' own data-plane datagrams, which are already end-to-end encrypted by the session layer (peer-to-peer Noise IK + ChaCha20-Poly1305). The relay learns only endpoints, volumes, and timing.
Wire protocol (client → relay is the Noise IK initiator):
frame 1: Noise IK message_1, payload = 32-byte remote peer X25519 key frame 2: Noise IK message_2 from the relay, empty payload thereafter: length-prefixed frames carrying encrypted messages
Every frame on the wire is [uint16 big-endian length][ciphertext].
This package holds the MIT-licensed relay client (Dial/Conn/Proxy) and the wire protocol. The relay server lives in the top-level relay package.
Index ¶
Constants ¶
const ChannelIDSize = 16
ChannelIDSize is the size of a derived relay channel identifier in bytes.
Variables ¶
var NoiseSuite = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)
NoiseSuite is the Noise cipher suite used by the relay channel protocol (X25519 + ChaCha20-Poly1305 + SHA-256). It is exported so the relay server (top-level relay package) speaks exactly the same protocol.
Functions ¶
func ChannelID ¶
func ChannelID(a, b [32]byte) [ChannelIDSize]byte
ChannelID derives the 16-byte rendezvous channel ID for a key pair. It is symmetric: ChannelID(a, b) == ChannelID(b, a), so both peers of a failing NAT pair arrive at the same channel without any extra signalling.
Types ¶
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a Noise IK-encrypted message stream to a relay, paired with the remote peer's connection on the same channel.
func Dial ¶
func Dial(ctx context.Context, addr string, relayX25519 [32]byte, id *crypto.Identity, remotePeerKey [32]byte) (*Conn, error)
Dial connects to the relay service at addr ("host:port", TCP), completes a Noise IK handshake against the relay's pinned X25519 static key, and requests a channel to remotePeerKey — the X25519 public key of the peer this node is trying to reach. The relay derives the rendezvous channel from the caller's authenticated static key and remotePeerKey; the returned Conn starts delivering messages once that peer has also connected.
func (*Conn) ReadMessage ¶
ReadMessage reads and decrypts the next frame from the relay.
func (*Conn) WriteMessage ¶
WriteMessage encrypts b and writes it as one length-prefixed frame. Concurrent calls are serialised: both encryption and the write happen under the write mutex so the cipher state is never raced.
type Proxy ¶
type Proxy struct {
// contains filtered or unexported fields
}
Proxy bridges a relay channel to the local data plane. It listens on a loopback UDP socket; datagrams the daemon sends to the proxy address are framed and forwarded over the relay channel, and datagrams arriving from the relay channel are injected into the local data-plane socket.
To the dispatcher, the relay path looks like an ordinary peer endpoint: the peer table entry's endpoint is simply set to the proxy's local address.
func NewProxy ¶
NewProxy creates a Proxy that forwards datagrams between stream (a paired relay channel from Dial) and the local data-plane socket at target (typically "127.0.0.1:<daemon UDP port>").
The proxy starts forwarding immediately. Set the peer's endpoint to LocalAddr() and initiate the Noise handshake through it.