2.1 KiB
2.1 KiB
sneaker
Sneakers sneaks your home server onto the internet by tunneling traffic between a public URL and your private server using WebSockets.
Install
bun add @because/sneaker
Server
Run the tunnel server:
bun run src/server.tsx
The server exposes:
GET /health— returns the current git SHAGET /tunnels— lists active tunnel connectionsGET /tunnel/:app— redirects to the app's subdomainGET /tunnel?app=NAME— WebSocket endpoint for clients to establish a tunnel (optionalsubdomainquery 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
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.