tunnel

package
v1.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 26, 2026 License: MIT Imports: 17 Imported by: 0

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

View Source
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

View Source
var ErrUnauthorized = errors.New("invalid or expired token")

ErrUnauthorized is returned by an Authorizer for an unknown token.

Functions

func NormalizeRelayURL

func NormalizeRelayURL(raw string) (string, error)

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

func ValidSubdomain(name string) bool

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.

func (*Agent) Connect

func (a *Agent) Connect(ctx context.Context) (*Session, error)

Connect dials the relay and returns the established session. The caller then runs Session.Serve.

type AuthError

type AuthError struct {
	Status  int
	Message string
}

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.

func (*AuthError) Error

func (e *AuthError) Error() string

func (*AuthError) Unwrap

func (e *AuthError) Unwrap() error

Unwrap lets errors.Is(err, ErrUnauthorized) keep working for 401s.

type Authorizer

type Authorizer func(ctx context.Context, token, subdomain string) (Grant, error)

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

type ConnectError struct {
	Status  int
	Message string
}

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.

func (*Relay) Active

func (r *Relay) Active() int

Active returns the number of open tunnels.

func (*Relay) ServeHTTP

func (r *Relay) ServeHTTP(w http.ResponseWriter, req *http.Request)

type RequestLog

type RequestLog struct {
	Method   string
	Path     string
	Status   int
	Duration time.Duration
}

RequestLog describes one request that passed through the tunnel.

type Session

type Session struct {
	// URL is the public address visitors use.
	URL string
	// Expires is when the relay will close the session (zero = never).
	Expires time.Time
	// contains filtered or unexported fields
}

Session is an established tunnel.

func (*Session) Close

func (s *Session) Close() error

Close ends the session.

func (*Session) Serve

func (s *Session) Serve(ctx context.Context) error

Serve proxies relay streams to the local app until the session ends or ctx is cancelled. It returns nil on a clean shutdown and the underlying error when the relay went away.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL