provisioner

package
v0.2.8 Latest Latest
Warning

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

Go to latest
Published: Aug 17, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package provisioner defines the contract between the gateway/hub layers and the cloud-side machinery that owns persistent per-user hosts.

A Provisioner is responsible for ensuring that a given user has exactly one persistent host on its provider (Fly.io Sprite, Fly Machine, k8s pod, …) and for surfacing the URL/token the gateway uses to reach it. Lifecycle is persistence-first: EnsureHost is idempotent (get-or-create-or-wake), SuspendHost is cooperative (compute-saving, state-preserving), and DestroyHost is the only path that throws away workspace state.

Concrete implementations live in subpackages: flysprites, flymachines, local. Each returns the same HostRef shape so upper layers stay provider-agnostic.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupported = errors.New("provisioner: capability unsupported by this provider")

ErrUnsupported is returned by provisioners for capability methods they don't implement. The preview-app feature uses this from OpenInternalConn — a provider without a TCP-tunneling primitive equivalent to Sprites' WSS proxy surfaces ErrUnsupported and the gateway returns 503 to mobile rather than a confusing 500.

Wrap with %w so callers can `errors.Is(err, provisioner.ErrUnsupported)`.

Functions

func TemplatesEnvValue

func TemplatesEnvValue(templates []Template) string

TemplatesEnvValue marshals a catalog to the JSON string clank-host reads from CLANK_TEMPLATES / --templates-json. Empty catalog → "". json.Marshal of []Template (strings only) cannot fail, so the shape is total; providers call this when building the sandbox env/args.

Types

type Closer

type Closer interface {
	Stop()
}

Closer is implemented by provisioners that own background work or SDK clients that need explicit cleanup at daemon shutdown. Optional; callers should type-assert.

type HostRef

type HostRef struct {
	// HostID is the store-internal UUID of this host record. Used by
	// the caller to invoke SuspendHost or DestroyHost later.
	HostID string

	// URL is the base URL the gateway will proxy to. For Sprites and
	// Fly Machines it is the public host URL; for the local-subprocess
	// provider it is http://127.0.0.1:<port>.
	URL string

	// Transport is the fully-wired http.RoundTripper that injects
	// every header required to reach this host: the universal
	// capability-token (Authorization: Bearer) plus any provider-edge
	// auth (e.g. a provider-edge preview token). Consumers
	// construct an HTTP client as `hostclient.NewHTTP(ref.URL,
	// ref.Transport)` and stay agnostic to the auth chain shape.
	//
	// Non-nil. The provisioner builds the chain and validates its
	// pieces before returning.
	Transport http.RoundTripper

	// AuthToken is the bearer token baked into the host's clank-host
	// require-bearer middleware. Surfaced separately from Transport
	// for storage/logging purposes; the same value is already wired
	// into Transport for outbound requests.
	AuthToken string

	// AutoWake indicates the provider's URL wakes the underlying
	// compute on incoming traffic without an explicit API call. True
	// for Sprites and Fly Machines (Flycast); false for the local
	// subprocess. The gateway uses this to decide
	// whether a probe failure means "stale URL, re-resolve" (false) or
	// "edge will wake on retry" (true).
	// todo(ae): Leaky abstraction. The provisioner should instead always implement auto-wake.
	AutoWake bool

	// Hostname is the stable identifier surfaced to upper layers
	// (session metadata, hub catalog). Stable across stop/resume of
	// the same underlying host.
	Hostname string
}

HostRef carries everything the gateway needs to reach a user's host. It is the return value of EnsureHost and is safe to cache for the duration of one daemon lifetime, but stale values must be re-resolved after a /status probe failure (URLs may rotate across stop/start cycles).

type Provisioner

type Provisioner interface {
	// EnsureHost resolves (or creates, or wakes) the persistent host
	// for userID. Idempotent across calls within a daemon lifetime AND
	// across daemon restarts (state survives via the underlying
	// store). Returns a HostRef pointing at a host that has just
	// passed a readiness probe.
	EnsureHost(ctx context.Context, userID string) (HostRef, error)

	// SuspendHost issues a cooperative suspend on the underlying
	// compute (Sprite hibernate, Machine stop, etc.) so the user's
	// workspace stops billing for compute. State is preserved; a
	// subsequent EnsureHost wakes it.
	//
	// Idempotent: suspending an already-stopped host is not an error.
	SuspendHost(ctx context.Context, hostID string) error

	// DestroyHost permanently deletes the underlying compute and
	// removes the store row. Used for explicit account/workspace
	// teardown. Out-of-band deletion at the provider is detected
	// inside EnsureHost (NotFound from Get) and handled there; callers
	// don't need to invoke DestroyHost for that case.
	DestroyHost(ctx context.Context, hostID string) error

	// DestroyHostsByUser destroys every host this provider holds for
	// userID — the account-erasure counterpart to DestroyHost. It tears
	// down compute and store rows unconditionally (a busy session must
	// not block a GDPR deletion), and is idempotent: a user with no
	// hosts returns nil. The hosts table is UNIQUE(user_id, provider),
	// so a single provider has at most one host per user; a multi-
	// provider control plane implements this over its own store and
	// loops every provider row.
	DestroyHostsByUser(ctx context.Context, userID string) error

	// GetHostByID is the non-mutating counterpart to EnsureHost:
	// resolve a host_id (from a stored row, e.g. preview_routes.host_id)
	// to its current HostRef without provisioning, waking, or creating
	// anything. Used by the preview-route proxy to resolve a token's
	// target host before tunneling — the proxy mustn't wake hosts as
	// a side effect of someone hitting a stale URL.
	//
	// Returns hoststore.ErrHostNotFound (wrapped) when no row matches.
	GetHostByID(ctx context.Context, hostID string) (HostRef, error)

	// OpenInternalConn returns a net.Conn to (hostID, port) inside
	// the provider's private network. For Sprites this is the WSS
	// proxy at api.sprites.dev/v1/sprites/{name}/proxy; for the local
	// provisioner it's a direct dial to 127.0.0.1:port; for providers
	// without such a primitive it's ErrUnsupported.
	//
	// Used by pkg/gateway/previewtunnel to fan out preview-app
	// traffic to per-worktree dev servers running on private ports.
	// The returned conn is single-shot — Close releases the tunnel —
	// but the caller (stdlib http.Transport) handles pooling.
	OpenInternalConn(ctx context.Context, hostID string, port int) (net.Conn, error)
}

Provisioner is the interface the gateway/hub uses to obtain and manage a user's persistent host. Implementations MUST be safe for concurrent use by multiple goroutines: callers will issue overlapping EnsureHost requests for the same userID and expect them to converge on the same single host.

type Template

type Template struct {
	DisplayName string `json:"display_name"`
	CloneURL    string `json:"clone_url"`
}

Template is one operator-configured ("builtin") entry of the create-project catalog, passed to a provider via its Options. The provider forwards these to the sandbox's clank-host, which serves them from GET /templates merged with the user's own GitHub template repos.

This is the control-plane-side type; it is the wire-compatible pair of internal/host.Template (the sandbox-side parse target). A template's identity is its clone URL — there is no id.

Env-var config is a caller concern: a daemon (clankd, or an embedder's binary) that wants CLANK_TEMPLATES-style config unmarshals the JSON into []Template itself, then passes the strong type here. The library API stays typed.

Directories

Path Synopsis
GetHostByID + OpenInternalConn capability extensions.
GetHostByID + OpenInternalConn capability extensions.
Sprites-side implementation of the GetHostByID + OpenInternalConn capability extensions on provisioner.Provisioner.
Sprites-side implementation of the GetHostByID + OpenInternalConn capability extensions on provisioner.Provisioner.
Package hoststore defines the persistence contract used by cloud provisioners (flysprites, flymachines, …) for tracking the per-(userID, provider) host record.
Package hoststore defines the persistence contract used by cloud provisioners (flysprites, flymachines, …) for tracking the per-(userID, provider) host record.
Local-subprocess implementation of GetHostByID + OpenInternalConn.
Local-subprocess implementation of GetHostByID + OpenInternalConn.
Package transport holds RoundTripper helpers shared across provisioners.
Package transport holds RoundTripper helpers shared across provisioners.
Package tunnelclient dials clank-host's GET /tunnel/{port} endpoint and adapts the WebSocket to a net.Conn carrying the raw TCP bytes of a port on the host's loopback.
Package tunnelclient dials clank-host's GET /tunnel/{port} endpoint and adapts the WebSocket to a net.Conn carrying the raw TCP bytes of a port on the host's loopback.

Jump to

Keyboard shortcuts

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