concepts
Concepts
How Verklet runs Node.js-style workloads in a browser tab — workers, OPFS, service workers, WASM.
Verklet is built from four browser primitives, each doing one job. None of them is new; the work is in stitching them into something that feels like a real Node host.
Web Workers — process isolation
Every "process" in Verklet is a Web Worker. The main thread doesn't execute user code. It owns the runtime container and the UI; user processes run in workers that get their own message channel, their own stdio streams, and their own crash boundary.
This matters because:
- A user process that throws or hangs does not freeze the page. It freezes its worker.
- Multiple processes can run in parallel —
node server.jsandnode build.jsare independent workers, not coroutines on a single event loop. - The coordinator worker brokers RPC between processes and the host, which keeps the main thread responsive even while a build is grinding.
You don't manage workers directly. runtime.spawn() allocates them;
process exit reclaims them.
OPFS — persistent storage
The virtual filesystem lives in memory while a session is open. When
you pass persistenceKey to Runtime.boot(), Verklet writes a
content-addressed snapshot to the Origin Private File System — a
per-origin sandbox that survives reloads but is invisible to other sites.
What that gets you:
- Reload-and-restore. Files the user wrote in their last session are mounted automatically on next boot.
- Cross-tab handoff. A second tab on the same origin can lock, inherit, or fork the snapshot without colliding.
- Deduplication. Identical file blobs are stored once and pointed
at by hash — handy when many sessions share
node_modules.
OPFS support varies. Chromium and Firefox are solid; WebKit is partial.
Verklet detects this at boot via capabilities.opfs and degrades to
in-memory if necessary — your code runs either way.
Service Workers — preview routing
When a process inside the runtime opens a port, that port doesn't
actually exist at the network level. The browser has no concept of "an
HTTP server in a worker." So Verklet registers a service worker at
your origin that intercepts requests to a preview path
(/__runtime by default) and routes them into the runtime as
in-process function calls.
The upshot:
- An
<iframe src="..."/>pointing at a preview URL renders the runtime's HTTP response. No tunneling, no relay server, no externally reachable port. - You can
fetch()the preview from the same page that owns the runtime — convenient for tests and screenshotting. - The visitor sees a normal same-origin URL, so cookies, storage, and CSP work the way they would for any first-party page.
The service worker is registered automatically on Runtime.boot(). If
you've disabled service workers in your hosting setup, pass
serviceWorker: false and use runtime.fetchPreview() directly.
WebAssembly — the hot paths
Anything that needs to be fast or POSIX-shaped is WASM. The synchronous
virtual filesystem (so fs.readFileSync works without yielding to the
event loop), the package archive parser, the path-traversal validator,
and a handful of binary codecs all live in Rust-built WASM modules.
JavaScript handles policy and orchestration. WASM handles the bytes.
This split is why a Node process inside Verklet can call fs and
Buffer APIs without the runtime stalling on every read.
Putting it together
A typical boot looks like this:
- Main thread imports
@verklet/sdk, callsRuntime.boot()with a publicprojectId. - SDK asks Verklet for the compatible asset base URL and package registry for that project and SDK version.
- SDK probes the host: SAB, OPFS, service worker scope, WASM streaming.
- Coordinator worker starts; preview service worker registers.
- If
persistenceKeymatches a stored snapshot, OPFS rehydrates the VFS. - The returned
runtimeis ready tomount()files andspawn()processes.
From the user's perspective they opened a tab and typed code. From the host's perspective, four pieces clicked together and a Node-shaped sandbox came up. See the compatibility matrix for what behaviour is wired up today.