flysprites

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: 24 Imported by: 0

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

View Source
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.

View Source
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

func New(opts Options, st hoststore.HostStore, lg *log.Logger) (*Provisioner, error)

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.

Jump to

Keyboard shortcuts

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