The local Supabase stack runtime. Its public API is a greenfield, Effect-native managed runtime; implementation modules are private to the package.
The package runs independently of the CLI and accepts normalized StackConfig values;
it does not load config.toml. CLI command wiring, configuration translation, and
presentation belong to the M5 integration work in separate PRs.
The supported entrypoints are:
@supabase/stack— Promise facade@supabase/stack/effect— Effect-native API@supabase/stack/testing— test helpers
Stacks are managed identities: closing a handle does not stop a running stack. Creating or opening a
handle starts nothing, and a stopped stack retains no Supervisor, workload, container, network, or
listener. Status and retained logs remain available directly from durable state while stopped; a
later start on the same handle launches a fresh Supervisor.
With no configuration override, all capabilities are enabled, PostgreSQL is the only eager
capability, and every other capability is lazy. Starting the stack therefore launches only
PostgreSQL by default; capabilities configured as eager join its startup dependency closure.
The remaining lazy capabilities activate through the stack's listeners on demand for the current
running session.
Native workloads have a two-minute readiness budget to allow cold starts to load shared libraries;
container workloads retain a 30-second budget, and PostgreSQL uses its configured health_timeout.
Each readiness probe returns immediately when its endpoint becomes healthy.
Artifact preparation is controlled independently from capability activation through the optional
top-level preparation setting:
await stack.start({ config: { preparation: "on-demand" } });The default "background" mode prepares all enabled lazy artifacts after PostgreSQL has started,
without launching those services. A single Supervisor-owned background operation prepares the
finite selected workload set concurrently, streams downloads through hashing and decompression,
is canceled and awaited by stop() or destroy(), and keeps completed cache entries.
"on-demand" skips that background work for callers that want full lazy preparation. In either
mode, activating a lazy service prepares its requested dependency closure concurrently, while
explicit stack.prepare(...) remains available as a cache-only warmup. Eager capabilities remain
independent of this preparation policy. The setting is persisted with the stack definition and
survives openStack() and restart. Changing it for a running stack follows the existing
stop-before-change configuration rule.
Running status includes an artifacts array for the current session. Each entry identifies a
workload and capability and reports queued, preparing, downloading, ready, or
failed; failed includes an error message and can be retried by activating the capability again.
preparing covers validation, verification, and extraction around the transfer. During stopping
or destroying, status may retain active preparation until teardown clears it. Once stopped, status
reports an empty array even when completed artifacts remain in the cache.
The Functions inspector can be exposed through its own loopback listener. Set the Edge Runtime
mode and enable that listener together; the resulting functionsInspector endpoint forwards the
runtime's /json/list and WebSocket inspector paths.
await stack.start({
config: {
capabilities: { functions: { settings: { inspector: { mode: "run", main: true } } } },
listeners: { functionsInspector: { enabled: true, address: "127.0.0.1", port: 9223 } },
},
});
const inspector = (await stack.status()).endpoints.functionsInspector;Native runtimes bind a private inspector port on loopback. Container runtimes bind port 9229
inside the service and publish that private port to the configured loopback listener. Connect your
debugger through inspector.url; the workload's private inspector port stays local to the stack.
Explicit stack.prepare(...) accepts a synchronous onProgress callback for
caller-owned preparation. It receives the same phase values, including ready when an artifact is
available while its capability remains dormant. This transfer-local callback is not reconstructed by
a separate status request.
The package's end-to-end contract is exercised through the same public Stack API in native and Docker modes. It begins from the PostgreSQL-only default, progressively activates every service with realistic traffic, and verifies stop/start cycles, stable ports, and persistent data. The CLI is not involved in these runtime tests.
Podman is supported only on local Linux hosts and must be selected explicitly; the runtime does not auto-detect container engines.
createTestStack gives each test stack a unique temporary project root and identity while sharing
the managed state root used by ordinary package callers. Automatic ports therefore
coordinate across all default callers. Helper project roots and identities remain isolated; a
temporary test stack is excluded from listings scoped to another project root but appears in an
unfiltered package listStacks() result. A failed destroy retains the affected project root and
managed state for recovery.
Callers can warm selected native artifacts or container images with stack.prepare(...) while a
stack is stopped or running; explicit preparation is cache-only and cancellation does not affect
completed entries.
Each capability may opt into eager activation in StackConfig; omitted settings keep every
non-PostgreSQL capability lazy. Prepared artifacts are not automatically pruned. followLogs(...)
provides filterable live entries through a stateless client-polled cursor.
Database reset is intentionally outside the current API. Applying migrations, declarative schemas,
and seeds remains the caller's responsibility. The runtime bootstrap only reconciles the _realtime
schema owner, closed database role passwords, and JWT settings in one transaction; the slim database
artifact owns its initialization and migrations.
