Introduction

The problem: NAT traversal

Most computers on the internet sit behind a NAT (Network Address Translation) router. Your laptop’s IP address is something like 192.168.1.5; your router has the real public IP, say 203.0.113.42. When your laptop opens a connection outward, the router translates the source address. But when something outside wants to connect to your laptop directly, it has no idea where to route that packet — the router blocks it.

Internet
    │
    │ 203.0.113.42 (public IP)
┌───┴────────────────────┐
│   Router / NAT         │
└───┬────────────────────┘
    │ 192.168.1.0/24 (private)
    │
    ├── 192.168.1.5  (your laptop)
    └── 192.168.1.8  (your phone)

When two people are each behind their own NAT, a direct connection is hard:

Person A                         Person B
192.168.1.5                      10.0.0.3
    │                                │
    │ NAT A                          │ NAT B
    │ 203.0.113.42                   │ 198.51.100.7
    │                                │
    └──────── Internet ──────────────┘

A wants to connect to B. B's packets reach NAT B, not B's laptop.

How warpgate solves it

Warpgate runs a cascade of plugins in parallel and uses whichever works first:

                    ┌──────────────┐
                    │ Signal Server│  (MQTT broker — public)
                    │   (relay)    │
                    └──────┬───────┘
                 signals   │   signals
          ┌────────────────┴────────────────┐
          │                                  │
     ┌────┴─────┐                      ┌─────┴────┐
     │  Node A  │◄────── direct ───────►  Node B  │
     └──────────┘    (if NATs allow)   └──────────┘

The strategies, tried concurrently:

  1. Direct connect — just try TCP. Works if one side has an open port.

  2. Reverse connect — ask the other side to connect to us.

  3. TCP / UDP hole punching — both sides open the NAT simultaneously so packets get through.

  4. Random probe — birthday-paradox bridge for cone↔symmetric pairs.

  5. UPnP / IPv6 pinhole — opportunistically opens a port on the router.

  6. TURN relay — fall back to relaying traffic through a public server.

  7. Custom — drop in your own plugin via the @register decorator.

Key concepts

Node

A Node is your local endpoint. It listens for incoming connections, manages its identity, and knows how to reach other nodes.

Node
 ├── identity (ECDSA key pair, node_id)
 ├── address (addr_bytes — shareable with peers)
 ├── nickname (optional: registered name in the PNP system)
 ├── servers (one TCP/UDP socket per interface × address family)
 └── traversal (the plugin engine that establishes connections)

Address

A node’s address is a compact byte string encoding:

  • The node’s public key (for authentication)

  • All its network interfaces and their IPs/NAT types

  • The listen port

You exchange this address with peers out-of-band (paste it in a chat, put it in a file, etc.). Warpgate also offers a nickname service so you can register a human-readable name.

Pipe

A Pipe is a connected channel between two nodes. Once you have one, you call send and recv just like a socket.

pipe, plugin = await auto_connect(node, dest_addr)
await pipe.send(b"hello world")
data = await pipe.recv(SUB_ALL)

Plugin

Plugins are the traversal strategies. Each plugin tries to establish a connection in its own way. auto_connect runs them all in parallel and returns the first winner. You can also invoke a specific plugin by name if you know what you need.

Signal channel

Plugins coordinate by exchanging small control messages over a public MQTT broker (the “signal channel”). These are just handshake packets — your actual data flows directly between the two nodes.

What warpgate is NOT

  • It is not a P2P networking framework (like libp2p or BitTorrent’s DHT). It only handles the connectivity part — getting a socket open between two machines.

  • It does not encrypt your data. Use TLS/DTLS or your own encryption on top of a Pipe.

  • It is not a VPN.