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 ¶
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 ¶
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. |