Help us shape the future of Go! Take the 2026 Go Developer Survey

client

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Oct 18, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package client is how a mesh component reads from its control plane. Node and router share it, so the body cap, the status handling and the signature check on /keys are a single code path. It depends on api/ and build metadata: importing it pulls in none of the control plane server.

Index

Constants

View Source
const MaxBodyBytes = 8 << 20

MaxBodyBytes caps every response body read from a control plane: a misbehaving or impersonated server must not be able to make a client buffer arbitrary amounts of memory. It is sized for the largest legitimate answer, the ban set in /info at roughly 55 bytes per peer ID, so about 150k banned peers fit; the policy is bounded by the control plane's own 1 MiB cap on POST /policies, and /keys is a few hundred bytes.

Variables

View Source
var DefaultEnrollBackoff = Backoff{Initial: time.Second, Max: 15 * time.Second, Budget: 3 * time.Minute}

DefaultEnrollBackoff keeps trying for three minutes, which admits a fleet of about 1500 members that start at once through the control plane's limiter of ten a second. A larger fleet starts in waves.

View Source
var ErrBodyTooLarge = errors.New("control plane answer exceeds the body cap")

ErrBodyTooLarge marks an answer over MaxBodyBytes. It is an error, never a prefix: a protobuf message cut at a field boundary still decodes, so a truncated ban set or router list would be read as a smaller, valid one.

View Source
var ErrNotFound = errors.New("control plane does not serve this endpoint")

ErrNotFound marks a 404: the control plane does not serve the endpoint, as one predating it does not. Callers of an endpoint added after the first release check for it, so a newer node works against an older control plane.

Functions

func DoWithChallenge

func DoWithChallenge(httpClient *http.Client, now func() time.Time, build func(ts int64) (*http.Request, error)) (*http.Response, error)

DoWithChallenge executes a request built with the local clock's millisecond timestamp. If the control plane answers 401 with api.ErrStaleChallengeTimestampMessage and a valid HTTP Date header, it recomputes the timestamp from Date and retries once. No other 401 is retried.

func DoWithChallengeRetry

func DoWithChallengeRetry(ctx context.Context, httpClient *http.Client, now func() time.Time, b Backoff, build func(ts int64) (*http.Request, error)) (*http.Response, error)

DoWithChallengeRetry is DoWithChallenge for a request the control plane may answer with 429 or 503. Such an answer is retried after Retry-After when the control plane names a wait, and after the backoff's own delay when it does not, with jitter so a fleet told "not now" together does not come back together. Any other answer, and the last one within the budget, is returned to the caller.

func NewHTTPClient

func NewHTTPClient(timeout time.Duration, allowInsecure func() bool, component string) *http.Client

NewHTTPClient is the HTTP client for every request a mesh component makes to its control plane. A nil allowInsecure never allows plaintext.

func ReadBody

func ReadBody(r io.Reader) ([]byte, error)

ReadBody reads a control plane response body of at most MaxBodyBytes and reports ErrBodyTooLarge for anything larger.

func SetChallengeHeaders

func SetChallengeHeaders(req *http.Request, priv crypto.PrivKey, payload []byte, ts int64) error

SetChallengeHeaders signs payload with priv and sets HeaderChallengeTimestamp and HeaderChallengeSignature on req.

Types

type Backoff

type Backoff struct {
	// Initial is the first wait when the answer carries no Retry-After;
	// each further wait doubles, up to Max.
	Initial, Max time.Duration
	// Budget is how long in all the caller keeps trying. The last answer
	// before the budget runs out is returned to the caller as it came.
	// Time, not a count of attempts: a control plane that names a short
	// Retry-After is asking for many short waits, and a fleet of N members
	// behind a limiter of R a second needs N/R seconds whatever it answers.
	Budget time.Duration
}

Backoff is how a caller waits for a control plane that is busy. A 429 or 503 is the control plane saying "not now": its enrollment limiter admits a fixed number of members per second for the whole mesh, so a fleet that joins at once is told to come back, and a member that gave up at the first such answer never joined.

type Client

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

Client reads the pull side of the mesh protocol from one control plane.

func New

func New(baseURL string, httpClient *http.Client) *Client

New normalizes baseURL, https:// when no scheme is given and no trailing slash, and speaks through httpClient, which the caller builds with NewHTTPClient so its own transport policy applies.

func (*Client) ExchangeToken

func (c *Client) ExchangeToken(ctx context.Context, biscuit []byte, req *api.TokenExchangeRequest) (*api.TokenExchangeResponse, error)

ExchangeToken is POST /token/exchange, authenticated with the calling node's biscuit: verifies an external OIDC/K8s/SPIFFE JWT and mints a short-lived Delegated Session Biscuit bound to the calling node.

func (*Client) FetchEgress

func (c *Client) FetchEgress(ctx context.Context, biscuit []byte) (*api.EgressAssignmentsResponse, error)

FetchEgress is GET /egress, authenticated with the caller's biscuit and signed challenge: the egress destinations the control plane assigned to this node.

func (*Client) FetchInfo

func (c *Client) FetchInfo(ctx context.Context) (*api.ControlPlaneInfoResponse, error)

FetchInfo is GET /info: the router addresses, the ban set and the OIDC details a node needs to enroll.

func (*Client) FetchKeys

func (c *Client) FetchKeys(ctx context.Context, trusted []ed25519.PublicKey) ([]ed25519.PublicKey, error)

FetchKeys is GET /keys: the control plane's currently valid signing keys. The set is accepted only if signed by a key in trusted (api.VerifyKeysResponse): whoever answers the URL must already be the control plane, not become it.

func (*Client) FetchPolicy

func (c *Client) FetchPolicy(ctx context.Context, biscuit []byte) (*api.PolicyConfigGetResponse, error)

FetchPolicy is GET /policies, authenticated with the caller's biscuit and signed challenge: the mesh policy as the Datalog rules a member adds to its authorizer.

func (*Client) FetchRevocations

func (c *Client) FetchRevocations(ctx context.Context, biscuit []byte) (*api.RevocationsResponse, error)

FetchRevocations is GET /revocations, authenticated with the caller's biscuit and signed challenge: the revoked Biscuit IDs and banned peer IDs currently tracked by the control plane.

func (*Client) MintSTSToken

func (c *Client) MintSTSToken(ctx context.Context, biscuit []byte, req *api.STSTokenRequest) (*api.STSTokenResponse, error)

MintSTSToken is POST /sts/token, authenticated with the egress node's biscuit: verifies a caller Biscuit for an egress destination and mints a short-lived ES256 JWT for cloud STS federation.

func (*Client) ReportCatalog

func (c *Client) ReportCatalog(ctx context.Context, biscuit []byte, services []*api.ServiceInfo) error

ReportCatalog is POST /nodes/catalog, authenticated with the calling node's biscuit and signed challenge: reports the services currently registered on the node.

func (*Client) WithClock

func (c *Client) WithClock(now func() time.Time) *Client

WithClock overrides the clock used for challenge timestamps.

func (*Client) WithIdentity

func (c *Client) WithIdentity(peerID string, priv crypto.PrivKey) *Client

WithIdentity configures the peer ID and private key used to sign proof-of-possession challenges on authenticated requests.

type FileTokenSource

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

FileTokenSource reads a JWT from a file path (e.g. a Kubernetes projected ServiceAccount token volume or a SPIRE spiffe-helper JWT-SVID file).

func NewFileTokenSource

func NewFileTokenSource(path string) *FileTokenSource

NewFileTokenSource creates a TokenSource that reads path on every FetchToken call.

func (*FileTokenSource) FetchToken

func (s *FileTokenSource) FetchToken(_ context.Context) (string, error)

FetchToken reads and trims the JWT from disk.

type StatusError added in v0.1.1

type StatusError struct {
	Code   int
	Status string
	Body   string
}

StatusError is a control plane answer other than success, with the status code a caller can act on (a 401 on an authenticated endpoint says the control plane does not know this node any more) and the body it sent.

func (*StatusError) Error added in v0.1.1

func (e *StatusError) Error() string

type TokenSource

type TokenSource interface {
	FetchToken(ctx context.Context) (string, error)
}

TokenSource fetches a fresh platform or OIDC JWT for enrollment, continuous attestation at POST /refresh, or fallback re-enrollment.

Jump to

Keyboard shortcuts

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