isolate

package
v0.7.23 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package isolate is the V8-isolate (Workers-model) runtime driver — the fifth implementation of internal/runtime.Runtime, after pkg/docker (docker + gvisor), internal/runtime/firecracker, and internal/runtime/wasm (plans/isolate-runtime.md).

Like WASM, the isolate runtime is host-mediated: it satisfies only the core Runtime interface, never ContainerRuntime. Isolates get no IP, no TAP/veth, and no per-IP iptables — egress policy is enforced by a host-side proxy the driver owns, and inbound HTTP is proxied to the isolate's fetch handler.

Phase 1 (this file) is the dispatch skeleton: every lifecycle method returns models.ErrRuntimeNotImplemented so the service layer's fifth dispatch branch, daemon wiring, and store schema can land and be proven stable before the engine work (Phase 2: pkg/isolate + pkg/jsbundle).

Index

Constants

View Source
const (
	GroupPerTenant  = config.IsolateGroupPerTenant
	GroupPerSandbox = config.IsolateGroupPerSandbox
)

Group granularity values, mirrored from internal/config so the driver does not force every consumer (tests, future pkg/isolate) through daemon config.

View Source
const DefaultGroupKey = "default"

DefaultGroupKey is the isolate-group key for the null tenant: creates whose group key fell back to an unscoped (operator/PAT) identity share one default group, which is the single-tenant self-hoster's zero-config path.

Variables

This section is empty.

Functions

func SanitizeGroupKey

func SanitizeGroupKey(key string) (string, error)

SanitizeGroupKey validates a group key for use in host paths and cgroup names. Empty maps to DefaultGroupKey; anything else must match groupKeyPattern exactly — no normalization, because two keys that normalize to the same directory would silently merge two tenants into one process (§2.1's forced-co-residency attack, at the filesystem layer).

func SeccompAllowlist

func SeccompAllowlist(jitless bool) []string

SeccompAllowlist returns the syscall names a group process may make. jitless=false is the default profile (base + JIT extension); jitless=true is the reduced paranoid-tier profile.

func SeccompNeverAllow

func SeccompNeverAllow() []string

SeccompNeverAllow returns the deny-regardless list (see seccompNeverAllow).

Types

type BundleResolver

type BundleResolver interface {
	Resolve(ctx context.Context, tenant, ref string) (*jsbundle.Bundle, error)
}

BundleResolver resolves a bundle reference (path, file://, uploaded name, digest) for a tenant to the concrete bundle code the group host injects. Production implementation: pkg/jsbundle (adapted in bundleresolver.go), the analog of pkg/wasmmod for .wasm modules. tenant scopes uploaded-name lookups.

func NewBundleResolver

func NewBundleResolver(inner *jsbundle.Resolver) BundleResolver

NewBundleResolver wraps a jsbundle.Resolver as a driver BundleResolver.

type Config

type Config struct {
	// WorkerdPath is the workerd binary that hosts isolate groups. Ping()
	// stats it so /healthz reports a missing binary before the first create.
	WorkerdPath string
	// RunDir holds per-group runtime state (sockets, capnp configs,
	// per-sandbox egress UDS endpoints).
	RunDir string
	// GroupGranularity is the isolate-group key granularity (§2.1):
	// GroupPerTenant (default) or GroupPerSandbox (hostile-code tier).
	GroupGranularity string
	// UseJail wraps each workerd group process in the isolate jail (jail.go).
	UseJail bool
	// JailChrootBase is the parent directory for per-group chroots.
	JailChrootBase string
	// JailUID / JailGID are the unprivileged uid/gid workerd drops to.
	JailUID int
	JailGID int
	// Jitless runs V8 with --jitless so the seccomp allowlist can drop the
	// W^X/JIT syscall surface (per-sandbox paranoid tier, §2.1).
	Jitless bool
	// IdleTTL is how long a group may sit without Create/Invoke before the
	// idle reaper tears it down. Zero disables the reaper.
	IdleTTL time.Duration
	// EgressPoolSize is the per-group egress-slot pool size (§4): the cap on
	// concurrently-egressing sandboxes in a group. Zero → pkg/isolate default.
	EgressPoolSize int
}

Config holds host-side isolate runtime settings.

func FromDaemonConfig

func FromDaemonConfig(cfg config.Config) Config

FromDaemonConfig projects the isolate slice of daemon config into driver config.

type Driver

type Driver struct {
	// contains filtered or unexported fields
}

Driver implements runtime.Runtime for isolate sandboxes. Isolate satisfies only the core Runtime interface — not ContainerRuntime (host-mediated networking, same posture as WASM).

func New

func New(cfg Config, logger *slog.Logger) *Driver

New constructs an isolate driver. The zero value is not usable.

func (*Driver) Create

func (d *Driver) Create(ctx context.Context, req models.CreateSandboxRequest, sandboxID, toolboxToken string, hostMounts []mounts.ContainerBind) (*models.SandboxRuntimeState, error)

Create is the cold path (plans/isolate-runtime.md §10): resolve the sandbox's bundle, route it to its isolate group (spawning the group's workerd process under single-flight on the first create — or claiming a warm blank host when the pool is enabled), and load the bundle onto that host. The sandbox is "running" the moment its bundle is pinned — the isolate itself is compiled lazily on first invoke (the §2.2 warm path). No per-IP networking, no container: this is host-mediated like WASM.

Failure rule (LIFO): if load fails after the group was acquired, the sandbox is released from the group, which tears the group's process down when this create had spawned it and no other member joined — so a failed first-create never leaks an empty workerd process (§11 empty-group rule).

func (*Driver) CreateSnapshot

func (d *Driver) CreateSnapshot(ctx context.Context, sandboxID, imageRef string) (string, error)

CreateSnapshot is unsupported by design, not just unimplemented: V8 exposes no serialize-a-running-isolate API, so the warm pool is the plan of record and any snapshot phase requires an upstream feasibility spike first (plans/isolate-runtime.md §4, §13 Q6).

func (*Driver) Destroy

func (d *Driver) Destroy(ctx context.Context, sandbox *models.Sandbox) error

Destroy removes the sandbox from its group host and, when it was the group's last member, tears the group process down (§2.1 last-member teardown). Nil, or an unknown sandbox, is a no-op — cleanup paths may run without a record.

func (*Driver) DrainNetworkByteCounters

func (d *Driver) DrainNetworkByteCounters() map[string]struct{ BytesIn, BytesOut int64 }

func (*Driver) EnsureHTTPListener

func (d *Driver) EnsureHTTPListener(ctx context.Context, sandboxID string, guestPort int) (string, error)

func (*Driver) Inspect

func (d *Driver) Inspect(ctx context.Context, sandboxID string) (*models.SandboxRuntimeState, error)

func (*Driver) InvokeHTTP

func (d *Driver) InvokeHTTP(ctx context.Context, sandboxID string, r *http.Request) (*http.Response, error)

InvokeHTTP calls the sandbox's fetch handler directly (tests + P0 gate).

func (*Driver) ListManaged

func (d *Driver) ListManaged(ctx context.Context) (map[string]*models.SandboxRuntimeState, error)

ListManaged returns the runtime state of every isolate this daemon owns, so restart reconcile can match live isolates against persisted rows and terminal-ize any strays.

func (*Driver) Ping

func (d *Driver) Ping(ctx context.Context) error

Ping verifies the workerd binary exists. Config load deliberately does not stat the path; this is the seam /health surfaces it through, mirroring the Firecracker driver.

func (*Driver) ReleaseHTTPListener

func (d *Driver) ReleaseHTTPListener(sandboxID string, guestPort int)

func (*Driver) RemoveImage

func (d *Driver) RemoveImage(ctx context.Context, imageRef string) error

RemoveImage GCs a bundle from the content-addressed store (Phase 2; bundles are content-addressed so removal is reference-counted, never destructive to a live sandbox).

func (*Driver) Resize

func (d *Driver) Resize(ctx context.Context, sandboxID string, req models.ResizeSandboxRequest) error

func (*Driver) RunIdleReaper

func (d *Driver) RunIdleReaper(ctx context.Context)

RunIdleReaper tears down isolate-group processes that have been idle longer than cfg.IdleTTL (plans/isolate-runtime.md §11 I22 / Phase 2 leftover). Last-member teardown already stops empty groups; this reaper covers the scale-to-zero case — a group that still has pinned sandboxes but has seen no Create/Invoke traffic for IdleTTL. Members are marked stopped and their bundles stay in the content-addressed store so the next Start/Create path can respawn the group and re-pin.

Nil or non-positive IdleTTL disables the reaper (tests / operators that want groups sticky until last-member teardown). The loop exits when ctx is cancelled.

func (*Driver) ServeToolbox

func (d *Driver) ServeToolbox(ctx context.Context, sandboxID, token string, w http.ResponseWriter, r *http.Request)

ServeToolbox implements ToolboxHost for the isolate driver.

func (*Driver) SetBundleResolver

func (d *Driver) SetBundleResolver(r BundleResolver)

SetBundleResolver injects the JS/TS bundle resolver (pkg/jsbundle in production, Phase 2).

func (*Driver) SetHostSupervisor

func (d *Driver) SetHostSupervisor(s HostSupervisor)

SetHostSupervisor injects the workerd group supervisor (pkg/isolate in production, Phase 2).

func (*Driver) SetNetworkBlocks

func (d *Driver) SetNetworkBlocks(sandboxID string, blockIngress, blockEgress bool)

func (*Driver) SetWarmPool

func (d *Driver) SetWarmPool(p WarmPool)

SetWarmPool injects the blank-workerd-host pool (internal/pool/isolate, Phase 3). Nil (the default) keeps every first-create on the cold spawn path.

func (*Driver) Start

func (d *Driver) Start(ctx context.Context, sandboxID string) (*models.SandboxRuntimeState, error)

Start re-affirms a running isolate, and re-pins the bundle after an idle-TTL reap tore the group down. An unknown id is a 404-shaped nil,nil per the Runtime contract.

func (*Driver) Stop

func (d *Driver) Stop(ctx context.Context, sandboxID string) error

Stop marks a sandbox stopped but keeps its bundle pinned so a later Start is instant. The group process stays up for co-resident sandboxes; true teardown is Destroy. Idle groups are reaped by RunIdleReaper.

func (*Driver) SyncAllowedPorts

func (d *Driver) SyncAllowedPorts(sandboxID string, ports []int)

type EgressPolicy

type EgressPolicy struct {
	BlockAll bool
	Allow    []string // CIDRs / hosts; empty + !BlockAll = allow-all (self-host default)
	Deny     []string
}

EgressPolicy is the per-sandbox outbound policy the host-side egress proxy enforces (plans/isolate-runtime.md §4 Phase 3). Mapped from existing CreateSandboxRequest fields (NetworkBlockAll / NetworkAllowOut / NetworkDenyOut) — net-new grant fields wait for the §10.1 checkpoint.

type EgressPolicySetter

type EgressPolicySetter interface {
	SetEgressPolicy(sandboxID string, p EgressPolicy)
}

EgressPolicySetter is implemented by GroupHost production adapters so the driver can push per-sandbox policy after Load. Fakes used in unit tests need not implement it (egress is fail-closed until policy is set on a real host).

type GroupHost

type GroupHost interface {
	// Load pins a sandbox's bundle so the host can serve it; idempotent.
	Load(id string, b *jsbundle.Bundle) error
	// Unload drops a sandbox and returns the number still loaded (for
	// last-member teardown).
	Unload(id string) int
	// LoadedCount is the number of sandboxes currently on this host.
	LoadedCount() int
	// Invoke proxies an HTTP request to the sandbox identified by id.
	Invoke(ctx context.Context, id string, r *http.Request) (*http.Response, error)
	// Stop kills the group process and releases its resources.
	Stop() error
}

GroupHost is a running workerd group process the driver loads sandboxes into and routes requests to. One host backs one isolate-group key (§2.1). Production implementation: *pkg/isolate.Host.

type HostSupervisor

type HostSupervisor interface {
	SpawnGroup(ctx context.Context, spec JailSpec) (GroupHost, error)
}

HostSupervisor spawns group hosts, one per isolate-group key (§2.1). SpawnGroup realizes the group's JailSpec (chroot + cgroups + drop-priv + seccomp) and starts the workerd process. Production implementation: pkg/isolate via hostsupervisor.go.

func NewHostSupervisor

func NewHostSupervisor(cfg Config) HostSupervisor

NewHostSupervisor builds the production supervisor over the isolate config.

type JailSpec

type JailSpec struct {
	// GroupKey is the sanitized isolate-group key (per-tenant granularity:
	// the authorized tenant id; per-sandbox granularity: the sandbox id;
	// empty tenant falls back to DefaultGroupKey).
	GroupKey string
	// ChrootDir is the group's private root: <JailChrootBase>/<GroupKey>.
	ChrootDir string
	// CgroupName is the per-group cgroup that enforces the GROUP-level
	// resource caps (§2.1): OSS workerd has no per-isolate CPU enforcement,
	// so the enforced blast radius is the group.
	CgroupName string
	// UID / GID are the unprivileged identity the process drops to.
	UID int
	GID int
	// CPUQuota / MemoryLimitMB are the group cgroup caps. Zero = unlimited
	// on that axis (the cgroup controller is left unset).
	CPUQuota      float64
	MemoryLimitMB int
	// Jitless selects the reduced seccomp profile (and --jitless on the V8
	// command line when the spawner realizes the spec).
	Jitless bool
}

JailSpec describes the OS confinement for one workerd group process. Build one with BuildJailSpec; a hand-rolled spec must pass Validate before use.

func BuildJailSpec

func BuildJailSpec(cfg Config, groupKey string, cpu float64, memoryMB int) (JailSpec, error)

BuildJailSpec computes the jail for one group under the driver config. cpu / memoryMB are the GROUP-level caps (map CreateSandboxRequest.CPU / MemoryMB onto them at create time — Phase 4 owns the exact mapping).

func (JailSpec) SeccompAllowlistFor

func (s JailSpec) SeccompAllowlistFor() []string

SeccompAllowlistFor returns the profile matching the spec's Jitless flag.

func (JailSpec) Validate

func (s JailSpec) Validate() error

Validate enforces the spec's invariants. It exists separately from BuildJailSpec so the spawner can re-assert them at realization time — the spec may have crossed a channel or been rehydrated from the store by then.

type NetworkByteCounter

type NetworkByteCounter interface {
	DrainNetworkByteCounters() map[string]struct{ BytesIn, BytesOut int64 }
}

NetworkByteCounter drains ingress/egress byte deltas observed at the mediators.

type NetworkPolicySink

type NetworkPolicySink interface {
	SetNetworkBlocks(sandboxID string, blockIngress, blockEgress bool)
}

NetworkPolicySink applies ingress/egress quota blocks at the mediator.

type PortGateway

type PortGateway interface {
	EnsureHTTPListener(ctx context.Context, sandboxID string, guestPort int) (dialAddr string, err error)
	ReleaseHTTPListener(sandboxID string, guestPort int)
	SyncAllowedPorts(sandboxID string, ports []int)
}

PortGateway is the host-mediated HTTP ingress surface for isolate sandboxes (plans/isolate-runtime.md §4 / Phase 3). Same shape as the WASM PortGateway: Caddy dials the loopback address returned by EnsureHTTPListener rather than a guest container IP, so expose_port never walks the TCP host-port pool.

func AsPortGateway

func AsPortGateway(rt any) (PortGateway, bool)

AsPortGateway returns the HTTP ingress surface when rt implements it.

type ToolboxHost

type ToolboxHost interface {
	ServeToolbox(ctx context.Context, sandboxID, token string, w http.ResponseWriter, r *http.Request)
}

ToolboxHost is the in-process toolbox surface for isolate sandboxes. Isolates have no POSIX process, so only invoke-handler is supported; everything else is 501 (plans/isolate-runtime.md §3 / Phase 3).

func AsToolboxHost

func AsToolboxHost(rt any) (ToolboxHost, bool)

AsToolboxHost returns the toolbox surface when rt implements it.

type WarmPool

type WarmPool interface {
	Acquire(ctx context.Context) (GroupHost, bool)
}

WarmPool hands out blank workerd group hosts. Production implementation: internal/pool/isolate (Phase 3). ok=false means empty or disabled — the caller cold-spawns. The group router runs before the pool: only a tenant's FIRST create claims; subsequent creates join the existing group.

Jump to

Keyboard shortcuts

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