concepts
Concepts
How Verklet runs Node.js-style and Python workloads across browser workers, Pyodide, OPFS, service workers, WASM, and server promotion.
Verklet is browser-first. The browser backend is built from 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 and Python host. The server backend adds a managed Linux workspace for workloads the browser cannot run.
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.js,node build.js, andpython analysis.pyare 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.
Server-runtime persistence uses a managed workspace instead of OPFS.
When backend: 'auto' promotes a browser session to the server, Verklet
exports a binary VFS snapshot and mounts it into that workspace.
Browser-only dependency artifacts such as node_modules are not copied;
successful install commands are replayed on the server by default, and
explicit server setup can be declared with server.prepare.
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 uses an isolated preview origin
like https://<session>.preview.verklet.com with a service worker and
message bridge that routes preview requests 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. - Untrusted preview code runs on the Verklet preview origin, not on your application or account origin.
The hosted boot service returns
https://{previewSessionId}.preview.verklet.com as the default preview
origin template. If your application uses a Content Security Policy,
allow https://*.preview.verklet.com in frame-src or child-src so
preview iframes can load. Use runtime.fetchPreview() when your host
page needs to inspect preview responses programmatically; it routes
inside the runtime instead of relying on cross-origin browser fetches.
Corporate firewalls or egress allowlists need the same wildcard
hostname.
The preview bridge 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.
Server runtime — Linux when the browser is the wrong host
Python starts in the browser through Pyodide. The server runtime is a
managed backend reached through the same SDK. It is used directly with
backend: 'server' or automatically with backend: 'auto' when native
Python wheels, uv, subprocesses, or native tools need capabilities
that do not exist inside a browser worker.
The server backend exposes a remote VFS, process stdout/stderr, process exit events, diagnostics, persistence, and usage events through the same runtime object. From the UI's perspective, the important thing is that the programming model does not split into a separate local API and a separate remote API.
Putting it together
A typical boot looks like this:
- The developer creates a Verklet account, selects or creates an
organization, and copies the organization-owned
projectId. - Main thread imports
@verklet/sdk, callsRuntime.boot()with that publicprojectIdand the desired backend mode. - 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 browser snapshot, OPFS rehydrates the VFS. - If Python cannot run in Pyodide or another server-only command runs
in
backend: 'auto', Verklet snapshots the browser VFS, mounts it into a server workspace, and runs server dependency setup before retrying the command. - The returned
runtimeis ready tomount()files andspawn()processes through the same SDK surface.
From the user's perspective they opened a tab and typed code. From the host's perspective, the runtime pieces clicked together and a browser Node/Python sandbox came up. See the compatibility matrix for what behaviour is wired up today.