Documentation
¶
Overview ¶
Local-subprocess implementation of GetHostByID + OpenInternalConn.
In local mode the daemon and the spawned clank-host (and any dev servers it forks) all run on the same machine. The "internal" network is just loopback — port `port` lives at 127.0.0.1:<port> regardless of what URL clank-host's HTTP listener is on.
Note that c.url is clank-host's own listener URL (the HTTP control plane), not the per-worktree dev-server port the caller wants. The dev server (Metro, etc.) binds an OS-allocated port that nobody outside the preview manager knows in advance — the caller passes it in via the `port` arg here. So even though we have a URL on the child record, we ignore its host and dial 127.0.0.1 explicitly.
This also makes the local provisioner a useful test substrate for previewtunnel.RoundTripper: stand up an httptest server, dial through OpenInternalConn, and you've exercised the full Provisioner.OpenInternalConn → http.Transport → upstream chain without needing a real cloud provider.
Package local implements provisioner.Provisioner by spawning a clank-host subprocess on the local machine. Laptop-mode counterpart to flysprites / flymachines: same Provisioner interface, same HostRef shape, different "compute" — a child process bound to a random localhost port instead of a remote sandbox.
EnsureHost is idempotent within a daemon lifetime: the first call spawns clank-host, subsequent calls return the cached HostRef. Subprocesses don't survive daemon restarts — cross-restart persistence is the cloud provisioners' job. SuspendHost is a no-op; DestroyHost kills the child.
Index ¶
- type Options
- type Provisioner
- func (p *Provisioner) DestroyHost(_ context.Context, _ string) error
- func (p *Provisioner) DestroyHostsByUser(ctx context.Context, _ string) error
- func (p *Provisioner) EnsureHost(_ context.Context, _ string) (provisioner.HostRef, error)
- func (p *Provisioner) GetHostByID(_ context.Context, hostID string) (provisioner.HostRef, error)
- func (p *Provisioner) GetHostByNotifierToken(_ context.Context, notifierToken string) (hoststore.Host, error)
- func (p *Provisioner) OpenInternalConn(ctx context.Context, hostID string, port int) (net.Conn, error)
- func (p *Provisioner) Stop()
- func (p *Provisioner) SuspendHost(context.Context, string) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Options ¶
type Options struct {
// BinPath is the absolute path to the clank-host binary. Empty
// → resolved via PATH at first EnsureHost.
BinPath string
// DataDir is passed to clank-host as --data-dir for its host.db.
// Empty → clank-host uses its own default ($HOME/.clank-host).
DataDir string
// WorkRoot is passed to clank-host as --work-root — the parent for
// worktrees and repo canonicals. Empty → clank-host uses its own
// default ($HOME/work, the dedicated-sandbox layout). Laptop
// deployments set this under the user's clank config dir so the
// host doesn't drop a work/ directory into their home.
WorkRoot string
// ProvisionTimeout caps how long EnsureHost waits for the child
// to print its bound listen address. Default: 10 seconds.
ProvisionTimeout time.Duration
// NotifierWebhookURL, when non-empty, configures the subprocess
// clank-host to POST agent-lifecycle notifications back to this
// URL — typically the same clankd that spawned it. Empty disables
// the notifier (laptop dev without push delivery).
NotifierWebhookURL string
// PreviewWebhookURL, when non-empty, configures the subprocess
// clank-host to POST preview register/revoke webhooks back to
// this URL on the gateway. Same auth as NotifierWebhookURL (the
// per-host bearer below). When set without NotifierWebhookURL,
// the bearer is still generated and passed so the preview
// webhook can authenticate.
PreviewWebhookURL string
// UserID is the identity the local provisioner reports for its
// single in-memory host record. Must match the `sub` claim the
// gateway's authenticator extracts from inbound JWTs, otherwise
// the preview surface's owner-ship check rejects owner_only
// tokens as cross-tenant. Empty falls back to "local".
UserID string
// TunnelInternalConn routes OpenInternalConn through clank-host's
// /tunnel endpoint (pkg/provisioner/tunnelclient) instead of
// dialing 127.0.0.1 directly — the same data path machine-style
// cloud backends use. Off for laptop speed; turn on (e.g. in the
// docker dev stack) to exercise the production preview path
// end-to-end without a cloud provider.
TunnelInternalConn bool
// Templates is the builtin create-project catalog, forwarded to the
// subprocess as --templates-json (the builtin half of its GET
// /templates). Passed as an explicit flag so it beats any
// CLANK_TEMPLATES the child would inherit from this process's env.
// Empty passes no flag — clank-host then serves only the user's
// own GitHub template repos.
Templates []provisioner.Template
}
Options configures the local-subprocess provisioner.
type Provisioner ¶
type Provisioner struct {
// contains filtered or unexported fields
}
Provisioner manages a single persistent clank-host subprocess.
func New ¶
func New(opts Options, lg *log.Logger) *Provisioner
New constructs a Provisioner. log may be nil.
func (*Provisioner) DestroyHost ¶
func (p *Provisioner) DestroyHost(_ context.Context, _ string) error
DestroyHost kills the subprocess and clears the cache.
func (*Provisioner) DestroyHostsByUser ¶
func (p *Provisioner) DestroyHostsByUser(ctx context.Context, _ string) error
DestroyHostsByUser kills the single subprocess this provider manages. userID is ignored (the local provisioner serves one user), mirroring EnsureHost. Idempotent: a no-op when no child is running.
func (*Provisioner) EnsureHost ¶
func (p *Provisioner) EnsureHost(_ context.Context, _ string) (provisioner.HostRef, error)
EnsureHost implements provisioner.Provisioner. Returns the cached HostRef when the child is healthy, otherwise spawns a fresh one.
userID is accepted for interface symmetry with flysprites/flymachines but is ignored — the local provisioner serves a single user. Multi- tenant routing is PR 4.
func (*Provisioner) GetHostByID ¶
func (p *Provisioner) GetHostByID(_ context.Context, hostID string) (provisioner.HostRef, error)
GetHostByID returns the cached HostRef when the running child's hostID matches; otherwise reports the host as missing.
The local provisioner only ever holds one child (one user), so any non-matching hostID is by definition not on this host. Mirrors the EnsureHost contract: we don't spawn a new child on a lookup.
func (*Provisioner) GetHostByNotifierToken ¶
func (p *Provisioner) GetHostByNotifierToken(_ context.Context, notifierToken string) (hoststore.Host, error)
GetHostByNotifierToken implements gateway.PreviewHostLookup (and notify.HostLookup): given a notifier_token bearer presented by a webhook caller, return the matching host row.
In local mode the row isn't in Postgres — it's in p.current. The preview webhook flow (clank-host → POST /webhooks/preview/register) uses this lookup to authenticate the inbound bearer.
func (*Provisioner) OpenInternalConn ¶
func (p *Provisioner) OpenInternalConn(ctx context.Context, hostID string, port int) (net.Conn, error)
OpenInternalConn dials 127.0.0.1:<port> directly — or, with Options.TunnelInternalConn, through clank-host's /tunnel endpoint like the machine-style cloud backends. hostID must match the currently-running child so we don't paper over a stale ref pointing at a different machine state.
func (*Provisioner) Stop ¶
func (p *Provisioner) Stop()
Stop kills the subprocess if running. Safe to defer.
func (*Provisioner) SuspendHost ¶
func (p *Provisioner) SuspendHost(context.Context, string) error
SuspendHost is a no-op: subprocesses don't auto-suspend.