OutRay is a reverse-tunnel service. It makes a locally running service reachable from the internet without inbound firewall rules or port forwarding by maintaining a persistent outbound WebSocket connection from the client to a cloud tunnel server.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/outray-tunnel/outray/llms.txt
Use this file to discover all available pages before exploring further.
High-level overview
- The OutRay client (CLI or
@outray/core) opens an outbound WebSocket to the tunnel server. - The tunnel server assigns a public URL or port to that connection.
- Visitors connect to the public URL or port.
- The tunnel server forwards the traffic through the WebSocket to the client.
- The client proxies the traffic to the local service and sends the response back.
Components
CLI client
The CLI (outray) is a Node.js process that runs on the developer’s machine. It:
- Authenticates with the OutRay web service to obtain an org-scoped token.
- Opens a WebSocket connection to the tunnel server and sends an
open_tunnelmessage. - Receives
request(HTTP),tcp_connection/tcp_data(TCP), orudp_data(UDP) messages and proxies them to the local service. - Sends responses back through the same WebSocket.
- Reconnects automatically with exponential backoff (up to 30 seconds) if the connection drops.
- Handles WebSocket passthrough for services that upgrade HTTP connections.
@outray/core (OutrayClient) and is shared with framework plugins such as @outray/vite.
Tunnel server
The tunnel server is the internet-facing component. It:- Accepts client WebSocket connections and authenticates each one.
- Accepts incoming HTTP, TCP, and UDP traffic from the internet.
- Routes incoming traffic to the correct client connection based on subdomain, custom domain, or assigned port.
- Forwards traffic as binary-framed WebSocket messages to the connected client.
- Manages connection state in Redis and persists user data in PostgreSQL.
Web dashboard
The web dashboard is an application atoutray.dev that handles:
- User registration, login, and OAuth flows.
- Organization management and member roles.
- Custom domain configuration.
- An internal HTTP endpoint used by Caddy to validate on-demand TLS certificate requests.
Cron
Background jobs handle housekeeping tasks such as expiring stale tunnel records and rotating analytics data in TimescaleDB.Infrastructure
| Layer | Technology | Role |
|---|---|---|
| TLS termination | Caddy | Terminates HTTPS and WSS connections; issues on-demand TLS certificates via ACME; calls the web dashboard’s internal check service before issuing a certificate for a custom domain. |
| Tunnel server | Node.js / WebSocket | Manages client connections, routes traffic, and encodes/decodes the message protocol. |
| Session state | Redis | Stores active tunnel metadata (assigned URL, port, client connection ID) for fast lookup on every incoming request. |
| User data | PostgreSQL | Persists users, organizations, API keys, and custom-domain records. |
| Analytics | TimescaleDB | Time-series storage for per-tunnel request metrics. |
HTTP request flow
- Caddy receives an HTTPS request, terminates TLS, and forwards it to the tunnel server over plain HTTP.
- The tunnel server looks up the client connection for the requested subdomain or custom domain in Redis.
- The server sends a
requestWebSocket message containing the method, path, headers, and base64-encoded body. - The CLI receives the message, makes a local HTTP request to
localhost:<localPort>, and collects the response. - The CLI sends a
responsemessage back with the status code, headers, and base64-encoded body. - The tunnel server writes the HTTP response back to Caddy, which relays it to the visitor.
TCP connection flow
The tunnel server binds a public TCP port and pipes raw bytes to and from the client over the WebSocket using base64-encodedtcp_data messages.
UDP packet flow
UDP is connectionless, so each packet is forwarded independently. The server wraps each incoming UDP datagram in audp_data message with the source address and port. The client sends the datagram to the local service and returns any response in a udp_response message. The server relays the response datagram back to the original sender’s address.
WebSocket passthrough
When the local service itself uses WebSocket connections (for example, a real-time app), OutRay passes the upgrade through transparently:- The tunnel server receives an HTTP Upgrade request and sends a
ws_upgrademessage to the CLI. - The CLI opens a WebSocket connection to the local service.
- Frames are relayed in both directions using
ws_framemessages. - When either side closes the connection, a
ws_closemessage is sent.
Local network access (mDNS)
When the--local flag is set (or local = true in the config file), the CLI also advertises the service on the LAN via mDNS. Devices on the same network can then reach it at <subdomain>.local without going through the remote tunnel. This uses MDNSAdvertiser, LocalProxy, and LocalHttpsProxy from @outray/core.
Security
- All traffic between the CLI and the tunnel server uses TLS (
wss://). - Each client connection is authenticated with an org-scoped token before any tunnel is opened.
AUTH_FAILEDandLIMIT_EXCEEDEDerrors cause the client to stop reconnecting and require user action.- Each tunnel is isolated; traffic on one subdomain or port cannot be routed to a different client.