How Exposr Works
Understand the reverse tunneling architecture and connection flow.
Overview
Exposr uses a relay server architecture. The client (agent) initiates an outbound TCP connection to the server. This is important because it means the client doesn't need any inbound ports open — it works behind NAT and firewalls.
The relay server has two internal ports: a control port (9000) for managing tunnels, and a data port (9001) for forwarding traffic.
Connection flow
Here is the step-by-step flow when you run exposr expose 3000:
Agent connects to control port
The client creates a persistent TCP connection to SERVER_IP:9000 and sends the agent token for authentication.
Agent registers a public port
The agent sends REGISTER 25565 to request a public port. The server checks availability and confirms.
Server starts listening
The server begins accepting public connections on the registered port (e.g., SERVER_IP:25565).
Internet user connects
An external user connects to the public port. The server generates a UUID for this connection.
Server notifies the agent
The server sends CONNECT <connection-id> over the control channel.
Agent creates data connection
The agent connects to SERVER_IP:9001 and identifies itself with DATA <connection-id>.
Traffic flows bidirectionally
The server connects the public socket to the data connection. All TCP data is forwarded in both directions between the internet user and the local service.
Port registration
When no public port is specified, Exposr follows this strategy:
- Try the default preferred port:
25565 - If unavailable, pick a random port from
20000-30000 - Send the port to the server for registration
- If still unavailable, try another random port (up to 100 attempts)
When a public port is explicitly specified with the to keyword, Exposr requests that exact port and does not fall back.
# Default port assignment
exposr expose 3000
# Explicit port
exposr expose 3000 to 21342Data forwarding
Each incoming public connection receives its own dedicated data tunnel. The agent opens a new connection to port 9001 for every public connection, identified by a UUID. Simultaneously, the agent connects to the local service on the specified port.
Traffic is forwarded using Python asyncio — data read from one socket is immediately written to the other, creating a transparent bidirectional pipe.
Multiple connections
Multiple users can connect to the same public port simultaneously. Each connection gets a unique UUID and a separate data tunnel:
Client A --+
|
Client B --+----> Exposr Server
| |
Client C --+ +-- Tunnel A --> Local Service
+-- Tunnel B --> Local Service
+-- Tunnel C --> Local ServiceThe server also supports multiple agents, each owning different public ports.