tsp

package
v1.98.1 Latest Latest
Warning

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

Go to latest
Published: May 7, 2026 License: BSD-3-Clause Imports: 25 Imported by: 0

Documentation

Overview

Package tsp provides a client for speaking the Tailscale protocol to a coordination server over Noise.

Index

Constants

View Source
const DefaultMaxMessageSize = 4 << 20

DefaultMaxMessageSize is the default cap, in bytes, on the size of a single compressed map response frame. See [MapOpts.MaxMessageSize].

View Source
const DefaultServerURL = ipn.DefaultControlURL

DefaultServerURL is the default coordination server base URL, used when ClientOpts.ServerURL is empty.

Variables

This section is empty.

Functions

func DiscoverServerKey

func DiscoverServerKey(ctx context.Context, serverURL string) (key.MachinePublic, error)

DiscoverServerKey fetches the coordination server's public key from the given server URL. It is a standalone function that requires no client state.

func WriteNodeFile

func WriteNodeFile(path string, nf NodeFile) error

WriteNodeFile writes a node JSON file. The file is created with mode 0600.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a Tailscale protocol client that speaks to a coordination server over Noise.

func NewClient

func NewClient(opts ClientOpts) (*Client, error)

NewClient creates a new Client configured to talk to the coordination server specified in opts. It performs no I/O; the server's public key is discovered lazily on first use or can be set explicitly via SetControlPublicKey.

func (*Client) AnswerC2NPing

func (c *Client) AnswerC2NPing(ctx context.Context, pr *tailcfg.PingRequest, doNoiseRequest func(*http.Request) (*http.Response, error)) (handled bool)

AnswerC2NPing handles a c2n PingRequest from the control plane by parsing the embedded HTTP request in the payload, routing it locally, and POSTing the HTTP response back to pr.URL using doNoiseRequest. The POST is done in a new goroutine so this method does not block.

It reports whether the ping was handled. Unhandled pings (nil pr, non-c2n types, or unrecognized c2n paths) return false.

func (*Client) Close

func (c *Client) Close() error

Close closes the client and releases resources.

func (*Client) DiscoverServerKey

func (c *Client) DiscoverServerKey(ctx context.Context) (key.MachinePublic, error)

DiscoverServerKey fetches the server's public key from the coordination server and stores it for subsequent use. Any existing noise client is invalidated.

func (*Client) Map

func (c *Client) Map(ctx context.Context, opts MapOpts) (*MapSession, error)

Map sends a map request to the coordination server and returns a MapSession for reading the framed, zstd-compressed response(s).

func (*Client) Register

func (c *Client) Register(ctx context.Context, opts RegisterOpts) (*tailcfg.RegisterResponse, error)

Register sends a registration request to the coordination server and returns the response.

func (*Client) SendMapUpdate

func (c *Client) SendMapUpdate(ctx context.Context, opts SendMapUpdateOpts) error

SendMapUpdate sends a one-shot, non-streaming MapRequest to push small updates (such as the node's endpoints, hostinfo, or disco public key) to the coordination server without starting or disturbing a streaming map session.

func (*Client) SetControlPublicKey

func (c *Client) SetControlPublicKey(k key.MachinePublic)

SetControlPublicKey sets the server's public key, bypassing lazy discovery. Any existing noise client is invalidated and will be re-created on next use.

type ClientOpts

type ClientOpts struct {
	// ServerURL is the base URL of the coordination server
	// (e.g. "https://controlplane.tailscale.com").
	// If empty, DefaultServerURL is used.
	ServerURL string

	// MachineKey is this node's machine private key. Required.
	MachineKey key.MachinePrivate

	// Logf is the log function. If nil, logger.Discard is used.
	Logf logger.Logf

	// HealthTracker, if non-nil, is the health tracker passed through
	// to the underlying noise client. May be nil.
	HealthTracker *health.Tracker
}

ClientOpts contains options for creating a new Client.

type MapOpts

type MapOpts struct {
	// NodeKey is the node's private key. Required.
	NodeKey key.NodePrivate

	// Hostinfo is the host information to send. Optional;
	// if nil, a minimal default is used.
	Hostinfo *tailcfg.Hostinfo

	// Stream is whether to receive multiple MapResponses over
	// the same HTTP connection.
	Stream bool

	// OmitPeers is whether the client is okay with the Peers list
	// being omitted in the response.
	OmitPeers bool

	// MaxMessageSize is the maximum size in bytes of any single
	// compressed map response frame on the wire. If zero,
	// [DefaultMaxMessageSize] is used.
	MaxMessageSize int64
}

MapOpts contains options for sending a map request.

type MapSession

type MapSession struct {
	// contains filtered or unexported fields
}

MapSession wraps an in-progress map response stream. Call Next to read each MapResponse. Call Close when done.

func (*MapSession) Close

func (s *MapSession) Close() error

Close returns the session's zstd decoder to the pool and closes the underlying HTTP response body. It is safe to call Close multiple times and from multiple goroutines, including while a MapSession.Next or MapSession.NextInto call is in flight on another goroutine (which will return an error once the body close propagates).

func (*MapSession) Next

func (s *MapSession) Next() (*tailcfg.MapResponse, error)

Next reads and returns the next MapResponse from the stream. For non-streaming sessions, the first call returns the single response and subsequent calls return io.EOF. For streaming sessions, Next blocks until the next response arrives or the server closes the connection.

Each call allocates a fresh MapResponse. Callers that want to amortize the allocation across calls can use MapSession.NextInto.

Next and NextInto are not safe to call concurrently from multiple goroutines on the same MapSession; a concurrent call panics, akin to the Go runtime's concurrent map access detection. MapSession.Close may be called concurrently to abort an in-flight Next.

func (*MapSession) NextInto

func (s *MapSession) NextInto(resp *tailcfg.MapResponse) error

NextInto is like MapSession.Next but decodes the next MapResponse into the caller-supplied *resp rather than allocating a new one. The pointer's pointee is zeroed before decoding so fields from a prior response do not persist.

For non-streaming sessions, the first call decodes the single response and subsequent calls return io.EOF. For streaming sessions, NextInto blocks until the next response arrives or the server closes the connection.

See MapSession.Next for concurrency rules; those apply to NextInto too.

func (*MapSession) NoiseRoundTrip

func (s *MapSession) NoiseRoundTrip(req *http.Request) (*http.Response, error)

NoiseRoundTrip sends an HTTP request over the Noise channel used by this map session.

type NodeFile

type NodeFile struct {
	// NodeKey is the node's WireGuard private key. The corresponding
	// public key identifies this node to other peers.
	NodeKey key.NodePrivate `json:"node_key"`

	// MachineKey is the machine's private key. It authenticates this
	// machine to the coordination server over Noise.
	MachineKey key.MachinePrivate `json:"machine_key"`

	ServerInfo // server_url and server_key
}

NodeFile is the JSON structure for a node credentials file. It contains the private keys that authenticate a node to a coordination server.

Example:

{
  "node_key": "privkey:...",
  "machine_key": "privkey:...",
  "server_url": "https://controlplane.tailscale.com",
  "server_key": "mkey:..."
}

Note that node and machine private keys share the same "privkey:" textual form; they are disambiguated by the surrounding JSON field names rather than by any prefix in the key itself.

func ReadNodeFile

func ReadNodeFile(path string) (NodeFile, error)

ReadNodeFile reads and parses a node JSON file.

func (NodeFile) AsJSON

func (nf NodeFile) AsJSON() []byte

AsJSON returns nf as a pretty-printed JSON object, terminated by a newline.

It always succeeds and always returns a valid JSON object. It does not validate that the fields of nf are non-zero; it is the caller's responsibility to call NodeFile.Check first if they want to reject incomplete NodeFiles.

func (NodeFile) Check

func (nf NodeFile) Check() error

Check reports whether nf has all required fields set. It returns an error describing the first zero-valued field, if any.

type RegisterOpts

type RegisterOpts struct {
	// NodeKey is the node's private key. Required.
	NodeKey key.NodePrivate

	// Hostinfo is the host information to send. Optional;
	// if nil, a minimal default is used.
	Hostinfo *tailcfg.Hostinfo

	// Ephemeral marks the node as ephemeral.
	Ephemeral bool

	// AuthKey is a pre-authorized auth key.
	AuthKey string

	// Tags is a list of ACL tags to request.
	Tags []string

	// MaxResponseSize is the maximum size in bytes of the register
	// response body. If zero, [DefaultMaxMessageSize] is used.
	MaxResponseSize int64
}

RegisterOpts contains options for registering a node.

type SendMapUpdateOpts

type SendMapUpdateOpts struct {
	// NodeKey is the node's private key. Required.
	NodeKey key.NodePrivate

	// DiscoKey, if non-zero, is the node's disco public key.
	// Peers use it to verify disco pings from this node, which is
	// what enables direct (non-DERP) paths.
	DiscoKey key.DiscoPublic

	// Hostinfo is the host information to send. Optional;
	// if nil, a minimal default is used.
	Hostinfo *tailcfg.Hostinfo
}

SendMapUpdateOpts contains options for Client.SendMapUpdate.

type ServerInfo

type ServerInfo struct {
	// URL is the base URL of the coordination server, without any path
	// (e.g. "https://controlplane.tailscale.com").
	//
	// There is no default value; a URL must always be supplied.
	URL string `json:"server_url"`

	// Key is the server's Noise public key, used to establish an encrypted
	// channel between the client and the coordination server.
	Key key.MachinePublic `json:"server_key"`
}

ServerInfo identifies a coordination server by its URL and Noise public key.

Jump to

Keyboard shortcuts

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