public urls for anything
a cli and sdk to create end-to-end encrypted public urls for apps running on your machine reachable from anywhere in the world
cli
$ opentunnel create created f7a2mx4kq9vn.opentunnel.xyz $ opentunnel route add opencode localhost:47365 added route opencode.f7a2mx4kq9vn.opentunnel.xyz -> localhost:47365 $ curl https://opencode.f7a2mx4kq9vn.opentunnel.xyz hello from localhost:47365
sdk
import { create } from "@opentunnel/client"
const client = create()
await client.route.add({
name: "opencode",
target: "localhost:47365",
})
const connection = await client.tunnel.connect()
console.log(connection.routes[0].hostname)
opencode.f7a2mx4kq9vn.opentunnel.xyz
how it works
-
opentunnel create reserves your hostname and generates a private key on your machine. the key never leaves it.
-
the cli sends a certificate request for that hostname. a certificate is issued and bound to your tunnel name. the relay only ever sees the public half.
-
opentunnel connect opens an encrypted bridge from your machine to the relay.
-
visitors hit your public url. the relay reads only the hostname from the tls handshake and forwards the encrypted stream through the bridge.
-
your machine terminates tls with its private key and proxies the traffic to your local apps.
privacy
- the relay can't read your traffic
- connections are routed by the hostname in the tls handshake. the bytes stay encrypted until they reach your machine. the relay has no key to decrypt them.
- your tunnel hostname is public
- anyone with your url can reach your services. when a tunnel is created, its certificate is published to certificate transparency logs, so the hostname is discoverable by anyone watching them.
- route names are private, not secret
- the certificate is a wildcard, so route names never appear in any log. they are still guessable, especially common names like api or postgres, so don't treat them as authentication.
- put auth in the services themselves
- anything sensitive behind a tunnel should authenticate on its own.