Hosting

Host contract

What each host owns, from the storage Layer and the runtime edge to expiry, wakes, streaming limits, and placement.

A host runs the same Streamsy routes and storage contract. It owns the parts that depend on the runtime: the listener, the main, expiry, wakes, and where data lives.

Hosts

HostStorage LayerRuntime edge ownerExpiry ownerWake sourceStreaming limitsPlacement
BunBunStorage.layer from @streamsy/storage/bun (bun:sqlite), or MemoryStorage.layerBunRuntime.runMain over Serve.layer and listener from @streamsy/serve/bunThe protocol on access. The app can sweep with Protocol.expireDue()Commit push in the process. The repair pass for other processesidleTimeout must stay 0, the listener default. Long poll 30 s, SSE 60 sOne process for each port. Several processes can share one file
NodeSqlStorage.layer from @streamsy/storage over an @effect/sql-sqlite-node client (node:sqlite), or MemoryStorage.layerNodeRuntime.runMain over Serve.layer and listener from @streamsy/serve/nodeThe protocol on access. The app can sweep with Protocol.expireDue()Commit push in the process. The repair pass for other processesThe node:http defaults hold live reads. requestTimeout limits only the request. Long poll 30 s, SSE 60 sOne process for each port
DenoAs Node. Run nodeSqliteCompat from @streamsy/serve/deno before the client opensDenoRuntime.runMain over Serve.layer and listener from @streamsy/serve/denoThe protocol on access. The app can sweep with Protocol.expireDue()Commit push in the process. The repair pass for other processesDeno.serve has no response timeout. Long poll 30 s, SSE 60 sOne process for each port
Durable ObjectDurableObjectStorage.layer from @streamsy/storage/durable-objectThe Worker router and StreamsyObject from @streamsy/serve/cloudflareThe object alarm, reconciled after each writeCommit push in the objectLong poll 25 s, SSE 60 sPlacement picks one object for each stream path

Shared parts

Every process host uses Serve.layer from @streamsy/serve and passes Serve.teardown to its runMain. On SIGINT or SIGTERM the drain answers live reads, and the process exits 0. See Long-running hosts.

Each listener takes port (default 3000), hostname (default 127.0.0.1), and gracefulShutdownTimeout. Without a bound, a stop waits for in-flight requests and the drain ends them. The Node listener also takes server, the options for node:http createServer.

SQLite on Node and Deno

SqlStorage.layer from @streamsy/storage takes a SqlClient. The client and the storage Layer must share one Reactivity. SqlStorage.clientLayer builds that pair from the driver's SqliteClient.make. The Bun and Durable Object Layers build it for you.

On Deno, @effect/sql-sqlite-node calls DatabaseSync.enableLoadExtension, which Deno's node:sqlite does not have. nodeSqliteCompat adds a no-op method. It changes nothing on a runtime that has the method.

Node and Deno pass the official conformance suite with SQLite through these Layers.

Several processes

BunStorage checks the file format before it opens and retries a busy transaction without blocking the event loop. Several Bun processes can share one file.

On Node and Deno, @effect/sql-sqlite-node opens the file in WAL mode with a 5 s busy timeout. A busy lock blocks the event loop for up to that time. Use one process for each SQLite file there.

A commit in one process wakes followers in another within repairIntervalMs (default 1 s).

On this page