Flow

Gateway

Reach a service on a remote pilot node using curl, a browser, or any TCP client. Ports are not translated - the port you connect to locally must match the port the remote service is listening on.

Separate binary: pilot-gateway does not ship with the standard install (release tarballs contain pilot-daemon, pilotctl, and pilot-updater only). pilot-protocol/gateway is currently published as a Go library (import "github.com/pilot-protocol/gateway") that you embed to bridge pilot streams to local loopback IPs; a standalone pilot-gateway binary is not published from it today. When a pilot-gateway binary is present, pilotctl extras gateway … locates it in this order: $PILOT_GATEWAY_BIN first, then a sibling next to pilotctl, then $PATH.

How it works

The gateway lets you connect to a TCP service running on a remote pilot node using normal tools - curl, a browser, netcat, anything.

When you start the gateway with a pilot address, it:

  1. Picks a local IP from a private subnet (default 10.4.0.0/16) and adds it as a loopback alias on your network interface
  2. Starts TCP listeners on that IP for every port you specify
  3. When a connection comes in, tunnels it through the encrypted pilot overlay to the remote machine

On the remote side, the incoming pilot connection arrives at the same port number. So if you start the gateway on port 8080, the remote machine needs a service actually listening on port 8080 - the gateway does not translate ports.

sudo is always required. Adding the loopback alias requires root on both macOS and Linux, regardless of which port you use.

Access a remote server

This is the most common use case: a peer is running a server and you want to reach it.

Example: reach a peer running a web server on port 80:

# 1. Trust the peer first (required)
pilotctl handshake agent-alpha

# 2. Start the gateway - maps 0:0000.0000.037D to 10.4.0.1
sudo pilotctl extras gateway start --ports 80 0:0000.0000.037D

# 3. Connect using any TCP tool
curl http://10.4.0.1/
# or open http://10.4.0.1/ in a browser

# 4. Stop when done
# pilotctl no longer tracks the gateway; stop it directly:
sudo pkill -TERM pilot-gateway

The first pilot address you map gets 10.4.0.1, the second gets 10.4.0.2, and so on.

Multiple peers at once

sudo pilotctl extras gateway start --ports 80,8080 0:0000.0000.037D 0:0000.0000.0002
# First peer  → http://10.4.0.1/  and  http://10.4.0.1:8080/
# Second peer → http://10.4.0.2/  and  http://10.4.0.2:8080/

Expose your own server on pilotprotocol network

For a peer to reach a plain TCP service on your machine, the pilot stream must be bridged to your local port on your side — a plain OS listener (e.g. python3 -m http.server) does not register a pilot listener, so the daemon has nothing to hand the inbound stream to. Run the gateway (or a pilot port-forward) on the server side to bridge the pilot port to your local TCP service; the peer then runs their gateway to dial you.

Your machine (the server)

# Start your server on whatever port you want
python3 -m http.server 8080
# nginx, caddy, your app - anything that listens on a TCP port

# Find your pilot address to share with the peer
pilotctl info
# Address: 0:0000.0000.xxxx  ← share this

When the peer sends a handshake, approve it:

pilotctl pending            # see incoming requests
pilotctl approve <node_id>

Peer's machine (the client)

# --ports 8080 must match the port your server is actually on
pilotctl handshake 0:0000.0000.xxxx
sudo pilotctl extras gateway start --ports 8080 0:0000.0000.xxxx
curl http://10.4.0.1:8080/

No port forwarding, no VPN, no firewall changes needed on your side. The pilot overlay handles the traversal.

Manage mappings

List current mappings

pilotctl extras gateway list

Note: this prints "no mappings" — live mappings are held inside the pilot-gateway process, not pilotctl.

Mappings

pilot-gateway owns its in-memory mapping table for its lifetime; there is no IPC to mutate a running gateway. pilotctl extras gateway map execs a transient pilot-gateway that registers a mapping and exits. To change the mapping set, restart pilot-gateway with the mappings you want.

pilotctl extras gateway map 0:0000.0000.0007           # transient: register + exit

Remove a mapping

pilotctl extras gateway unmap is not supported — unmapping is owned by the pilot-gateway process (there is no remote control path). Stop the process to release its mappings.

Stop the gateway

# pilotctl no longer tracks the gateway; stop it directly:
sudo pkill -TERM pilot-gateway

Notes & limits

Further reading: Connect AI Agents Behind NAT Without a VPN · Service Agents (for HTTP-style query/response without gateway plumbing)