Skip to main content

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.

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.

High-level overview

  1. The OutRay client (CLI or @outray/core) opens an outbound WebSocket to the tunnel server.
  2. The tunnel server assigns a public URL or port to that connection.
  3. Visitors connect to the public URL or port.
  4. The tunnel server forwards the traffic through the WebSocket to the client.
  5. 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_tunnel message.
  • Receives request (HTTP), tcp_connection / tcp_data (TCP), or udp_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.
The underlying client implementation lives in @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 at outray.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

LayerTechnologyRole
TLS terminationCaddyTerminates 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 serverNode.js / WebSocketManages client connections, routes traffic, and encodes/decodes the message protocol.
Session stateRedisStores active tunnel metadata (assigned URL, port, client connection ID) for fast lookup on every incoming request.
User dataPostgreSQLPersists users, organizations, API keys, and custom-domain records.
AnalyticsTimescaleDBTime-series storage for per-tunnel request metrics.

HTTP request flow

  1. Caddy receives an HTTPS request, terminates TLS, and forwards it to the tunnel server over plain HTTP.
  2. The tunnel server looks up the client connection for the requested subdomain or custom domain in Redis.
  3. The server sends a request WebSocket message containing the method, path, headers, and base64-encoded body.
  4. The CLI receives the message, makes a local HTTP request to localhost:<localPort>, and collects the response.
  5. The CLI sends a response message back with the status code, headers, and base64-encoded body.
  6. 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-encoded tcp_data messages.

UDP packet flow

UDP is connectionless, so each packet is forwarded independently. The server wraps each incoming UDP datagram in a udp_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:
  1. The tunnel server receives an HTTP Upgrade request and sends a ws_upgrade message to the CLI.
  2. The CLI opens a WebSocket connection to the local service.
  3. Frames are relayed in both directions using ws_frame messages.
  4. When either side closes the connection, a ws_close message 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_FAILED and LIMIT_EXCEEDED errors 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.

Build docs developers (and LLMs) love