Documentation
¶
Overview ¶
Sprites-side implementation of the GetHostByID + OpenInternalConn capability extensions on provisioner.Provisioner.
GetHostByID is a non-mutating store lookup that builds the same HostRef shape EnsureHost returns — but without provisioning, waking or installing. Used by the preview-route proxy to resolve a token's target host before tunneling. (The Sprites edge wakes on traffic to the public URL, so OpenInternalConn's WSS to api.sprites.dev is what actually causes a hibernated sprite to wake on the first preview request; GetHostByID itself doesn't touch sprite state.)
OpenInternalConn wraps sprites-go's ProxySocket: a transparent TCP relay tunneled over a fresh WSS to api.sprites.dev/v1/sprites/<name> /proxy, authenticated with the org's SPRITES_TOKEN that the Provisioner already holds. The same primitive the Phase 0 spike validated.
Package flysprites implements provisioner.Provisioner using Fly.io Sprites (https://sprites.dev) — one persistent sprite per user. The public URL is "public" mode; clank-host's bearer middleware is the only auth gate, with the per-sprite token persisted on the host row so it survives daemon restarts.
Index ¶
- Constants
- type Options
- type Provisioner
- func (p *Provisioner) DestroyHost(ctx context.Context, hostID string) error
- func (p *Provisioner) DestroyHostsByUser(ctx context.Context, userID string) error
- func (p *Provisioner) EnsureHost(ctx context.Context, userID string) (provisioner.HostRef, error)
- func (p *Provisioner) GetHostByID(ctx context.Context, hostID string) (provisioner.HostRef, error)
- func (p *Provisioner) OpenInternalConn(ctx context.Context, hostID string, port int) (net.Conn, error)
- func (p *Provisioner) Stop()
- func (p *Provisioner) SuspendHost(ctx context.Context, hostID string) error
Constants ¶
const HostPort = 8080
HostPort is clank-host's listen port inside the sprite. We set it on Service.HTTPPort explicitly rather than relying on Sprites' default.
const Provider = "flysprites"
Provider is the hoststore row discriminator for this backend.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
APIToken string // SPRITES_TOKEN; required when SDKClient is nil
OrganizationSlug string // optional; default org used when empty
Region string // optional Sprites region
// SpriteNamePrefix is prepended to the userID. Defaults to
// "clank-host".
SpriteNamePrefix string
RamMB int // 0 = sprite default
CPUs int // 0 = sprite default
StorageGB int // 0 = sprite default
// ProvisionTimeout caps how long EnsureHost waits for the sprite
// to become reachable. Default: 5 minutes.
ProvisionTimeout time.Duration
// NotifierWebhookURL, when non-empty, configures clank-host to
// POST agent-lifecycle notifications (idle, permission, error)
// back to this URL. The dispatcher at that URL resolves the
// host's bearer token to its owning user. Empty disables the
// subsystem — laptop dev without a dispatcher.
NotifierWebhookURL string
// GitHubOAuthClientID is the Clank GitHub OAuth App client_id
// forwarded to clank-host as --github-oauth-client-id. Empty
// disables GitHub Connect on the provisioned sprite (its status
// endpoint returns available:false). Supaclank reads it from its
// own env at startup and passes it here.
GitHubOAuthClientID string
// PreviewWebhookURL, when non-empty, is the gateway base for the
// preview register/revoke webhooks (e.g.
// "https://api.example.dev/webhooks/preview"), forwarded to clank-host
// as --preview-webhook-url. The host calls it when it spawns a
// per-worktree preview dev server so the gateway mints a public token.
// Empty disables cloud preview registration — servers still spawn but
// no public URL is minted. clank-host reuses the notifier token to
// authenticate these calls, so NotifierWebhookURL must also be set.
PreviewWebhookURL string
// Templates, when non-empty, is forwarded to clank-host as
// --templates-json: the builtin half of its GET /templates catalog.
// A change here drifts the service args and recreates the service
// on the next EnsureHost — how template-catalog updates roll out.
Templates []provisioner.Template
// SDKClient overrides the sprites.Client constructor for tests.
SDKClient *sprites.Client
}
Options configures the SpritesProvisioner.
type Provisioner ¶
type Provisioner struct {
// contains filtered or unexported fields
}
Provisioner manages one persistent Sprite per (userID, "flysprites").
func New ¶
New constructs a Provisioner. The HostStore is the persistence boundary — laptop daemons pass the SQLite-backed store from clank/internal/store; external integrators (e.g. multi-tenant cloud control planes) pass a Postgres-backed implementation. See pkg/provisioner/hoststore.
func (*Provisioner) DestroyHost ¶
func (p *Provisioner) DestroyHost(ctx context.Context, hostID string) error
DestroyHost permanently deletes the sprite and the store row.
func (*Provisioner) DestroyHostsByUser ¶
func (p *Provisioner) DestroyHostsByUser(ctx context.Context, userID string) error
DestroyHostsByUser destroys the user's flysprites sprite, if any. Idempotent: returns nil when the user has no row. Force-destroys regardless of session state (account erasure must not be blocked by a busy session).
func (*Provisioner) EnsureHost ¶
func (p *Provisioner) EnsureHost(ctx context.Context, userID string) (provisioner.HostRef, error)
EnsureHost implements provisioner.Provisioner.
Detaches from the caller's cancellation (cold install runs 30–90s, far longer than typical TUI request budgets) and bounds work with ProvisionTimeout instead. A per-userID mutex serializes concurrent callers onto a single in-flight provision.
func (*Provisioner) GetHostByID ¶
func (p *Provisioner) GetHostByID(ctx context.Context, hostID string) (provisioner.HostRef, error)
GetHostByID looks up a sprite by stored host_id and builds a HostRef pointing at it. Mirrors the shape EnsureHost returns but skips provisioning, waking and installation — strictly a read.
Errors:
- hoststore.ErrHostNotFound (wrapped) when the row doesn't exist
- any other store error wrapped with context
- a bad LastURL in the row is treated as a corrupt row and surfaced as a hard error (we can't construct a transport without parsing the host out)
func (*Provisioner) OpenInternalConn ¶
func (p *Provisioner) OpenInternalConn(ctx context.Context, hostID string, port int) (net.Conn, error)
OpenInternalConn opens a transparent TCP tunnel from the gateway to (port) inside the sprite identified by hostID. Uses sprites-go's ProxySocket — the same primitive Phase 0 verified handles HTTP/1.1, WebSocket upgrade, and concurrent reuse cleanly.
The returned net.Conn is single-shot: closing it closes the underlying WSS. The caller (typically previewtunnel.RoundTripper) is expected to pool conns at the http.Transport layer.
host inside the sprite is always "localhost" — the spike confirmed Sprites' proxy resolves it to the sprite's loopback iface, where per-worktree dev servers (Metro etc.) actually bind.
func (*Provisioner) Stop ¶
func (p *Provisioner) Stop()
Stop is a no-op: sprites auto-hibernate natively.
func (*Provisioner) SuspendHost ¶
func (p *Provisioner) SuspendHost(ctx context.Context, hostID string) error
SuspendHost is a no-op for Sprites. Hibernation is gated sprite-side on session-event activity (see internal/keepalive) — clank-host renews a Sprites Task lease while events flow and stops renewing when they stop, letting the platform's last-consumer timer take over. No daemon-side action is needed; we keep the method on the Provisioner interface so other backends that lack an in-VM signal can hook explicit suspend later.