# sneaker An HTTP tunnel server and client. Expose a local server to the internet through a WebSocket-based tunnel with auto-assigned subdomains. ## Install ```sh bun add @because/sneaker ``` ## Server Run the tunnel server: ```sh bun run src/server.tsx ``` The server exposes: - `GET /health` — returns the current git SHA - `GET /tunnels` — lists active tunnel connections - `GET /tunnel/:app` — redirects to the app's subdomain - `GET /tunnel?app=NAME` — WebSocket endpoint for clients to establish a tunnel (optional `subdomain` query param to request a specific subdomain) Incoming HTTP requests to `SUBDOMAIN.your-server.com` are forwarded through the WebSocket to the connected client and proxied to the local target. The server adds an `x-sneaker` header with the server's hostname to all proxied requests. ## Client ```ts import { connect } from "@because/sneaker" const tunnel = connect({ server: "ws://your-server.com", app: "my-app", target: "http://localhost:3000", subdomain: "my-app", // optional: request a specific subdomain reconnect: true, // optional: auto-reconnect (default: true) onOpen(subdomain) { console.log(`Tunnel open: ${subdomain}.your-server.com`) }, onClose() { console.log("Tunnel closed") }, onRequest(req) { console.log(`${req.method} ${req.path}`) }, onError(err) { console.error(err) }, }) // Later: tunnel.close() ``` ### Client options | Option | Type | Description | |---|---|---| | `server` | `string` | WebSocket URL of the tunnel server | | `app` | `string` | App name identifier | | `target` | `string` | Local URL to proxy requests to | | `subdomain` | `string?` | Request a specific subdomain | | `reconnect` | `boolean?` | Auto-reconnect on disconnect (default: `true`) | | `onOpen` | `(subdomain: string) => void` | Called when tunnel is established | | `onClose` | `() => void` | Called when tunnel disconnects | | `onRequest` | `(req) => void` | Called for each proxied request | | `onError` | `(error: Error) => void` | Called on errors | Binary responses (images, fonts, etc.) are automatically base64-encoded over the tunnel and decoded by the server.