Documentation
¶
Overview ¶
Package tunnel implements `nimbus expose`: a relay that publishes a developer's local port on a public HTTPS URL.
The CLI (Agent) dials the relay over a WebSocket carrying its Nimbus Cloud CLI token. Both ends wrap that connection in a yamux session, after which everything is ordinary Go HTTP: the relay runs a reverse proxy whose transport opens a yamux stream per connection, and the agent serves those streams with a reverse proxy to localhost. HTTP/1.1, server-sent events and WebSocket upgrades all pass through without a bespoke framing format.
Index ¶
Constants ¶
const ( // ConnectPath is the relay endpoint an agent dials. ConnectPath = "/connect" // HeaderSubdomain carries the name an agent asks for (agent → relay). HeaderSubdomain = "X-Nimbus-Subdomain" // HeaderTunnelURL returns the public URL on the upgrade response. HeaderTunnelURL = "X-Nimbus-Tunnel-Url" // HeaderTunnelExpires returns the session deadline (RFC 3339) or "". HeaderTunnelExpires = "X-Nimbus-Tunnel-Expires" // HeaderTunnelID marks every public response served through a tunnel, // so a page can be traced back to an account when abuse is reported. HeaderTunnelID = "X-Nimbus-Tunnel" )
Variables ¶
ErrUnauthorized is returned by an Authorizer for an unknown token.
Functions ¶
func NormalizeRelayURL ¶
NormalizeRelayURL accepts "https://host", "wss://host/connect" or a bare host and returns the WebSocket connect URL.
func RandomSubdomain ¶
func RandomSubdomain() string
RandomSubdomain returns a readable, hard-to-guess label such as "brisk-otter-3f9a".
func ValidSubdomain ¶
ValidSubdomain reports whether name is usable as a tunnel label.
Types ¶
type Agent ¶
type Agent struct {
// RelayURL is the relay's connect endpoint, e.g.
// "wss://tunnel.nimbusgo.space/connect".
RelayURL string
// Token is the Nimbus Cloud CLI token from `nimbus login`.
Token string
// LocalAddr is the app being exposed, e.g. "127.0.0.1:3333".
LocalAddr string
// Subdomain asks for a fixed name (Pro plans); "" gets a random one.
Subdomain string
// KeepHost forwards the public Host header instead of LocalAddr. Useful
// for apps that build absolute URLs from the request host.
KeepHost bool
// OnRequest, when set, is called after each proxied request.
OnRequest func(RequestLog)
}
Agent is the local side of a tunnel: it dials the relay and serves the streams it receives with a reverse proxy to LocalAddr.
type AuthError ¶
AuthError is a rejection from an Authorizer that carries the HTTP status the relay should report, so the CLI can tell "your token expired" from "that name belongs to another account" and stop retrying either way.
type Authorizer ¶
Authorizer resolves a CLI token, plus the subdomain the agent asked for ("" for none), to a Grant. Return an *AuthError to reject the agent with a specific status; ErrUnauthorized is treated as 401. Any other error is a relay-side fault and answered with 502.
An authorizer that keeps reservations answers a name request by setting Grant.Subdomain, which the relay then publishes verbatim.
type ConnectError ¶
ConnectError is a rejection by the relay before the tunnel was established. Permanent reports whether retrying without changing anything is pointless (bad token, plan limit, name taken).
func (*ConnectError) Error ¶
func (e *ConnectError) Error() string
func (*ConnectError) Permanent ¶
func (e *ConnectError) Permanent() bool
Permanent is true for rejections a reconnect loop must not retry.
type Grant ¶
type Grant struct {
UserID uint `json:"user_id"`
Plan string `json:"plan"`
// Subdomain, when set, is the name the relay MUST publish under: the
// authorizer resolved the agent's request against its own records (a
// reservation the account owns). It is authoritative — the relay does
// not second-guess it — and empty when no name was requested.
Subdomain string `json:"subdomain"`
// MaxTunnels caps concurrent tunnels for the account (0 = one).
MaxTunnels int `json:"max_tunnels"`
// SessionTTLSeconds closes the tunnel after this long (0 = unlimited).
SessionTTLSeconds int `json:"session_ttl_seconds"`
// CustomSubdomain allows choosing the name instead of a random one.
CustomSubdomain bool `json:"custom_subdomain"`
}
Grant is what Nimbus Cloud says a CLI token may do. The relay never sees plans directly; it enforces exactly these numbers.
type Relay ¶
type Relay struct {
// Domain is the suffix under which tunnels are published, e.g.
// "tunnel.nimbusgo.space" publishes "brisk-otter-3f9a.tunnel.nimbusgo.space".
Domain string
Authorize Authorizer
// Logf receives one line per tunnel open/close (nil = silent).
Logf func(format string, args ...any)
// contains filtered or unexported fields
}
Relay is the public side of a tunnel. It serves two kinds of traffic on one listener: agent connections on ConnectPath (any host that is not a tunnel subdomain) and visitor requests on "<name>.<Domain>".
func NewRelay ¶
func NewRelay(domain string, auth Authorizer) *Relay
NewRelay returns a relay publishing tunnels under domain.
type RequestLog ¶
RequestLog describes one request that passed through the tunnel.