Filesystem

The virtual filesystem — an in-memory tree with pluggable providers, blob-backed large files, persistence, and tar.gz snapshots.

Every box has a VFS — a virtual filesystem that behaves like a POSIX tree (files, directories, symlinks, permissions, mtimes) but lives in memory as plain JavaScript objects. It's the single source of truth that the shell, commands, and the Node fs shim all read and write.

The tree and its API

The VFS exposes the operations you'd expect, synchronously at the core level:

kernel.vfs.writeFile("/home/user/a.txt", "hello");
kernel.vfs.readFileString("/home/user/a.txt"); // "hello"
kernel.vfs.readdir("/home/user"); // Dirent[]
kernel.vfs.stat("/home/user/a.txt"); // Stat
kernel.vfs.mkdir("/tmp/x", { recursive: true });

The Sandbox fs helper wraps these in a promise-based, node:fs-shaped API (readFile, writeFile, readdir, stat, mkdir, rm, exists, rename, cp), and the Node runtime maps node:fs / node:fs/promises onto the same tree.

The VFS also emits watch events (vfs.watch(listener)), which power fs.watch in the Node layer, HMR file watching for dev servers, and the kernel's own save-on-change persistence.

Providers: synthetic and mounted paths

Not every path is a plain in-memory file. The VFS supports providers mounted at a path prefix:

  • /proc — synthetic process/system info generated on read.
  • /dev — device-like files.
  • NativeFsProvider — backs a subtree with a real host directory. In Node, the mounts option uses this so a box reads and writes files on your machine; it can also take a custom NativeFsModule.

Large files: blob & content store

Small files live inline in the tree. Large or binary files are chunked and stored in a content store backed by a blob store — MemoryBlobStore by default, or IndexedDBBlobStore in the browser. Content is addressed by hash (hashBytes), so identical data is stored once. CHUNK_THRESHOLD and CHUNK_SIZE control when and how a file is chunked.

Persistence

The kernel drives persistence through a PersistenceManager and a pluggable PersistenceBackend:

  • IndexedDBPersistenceBackend — the browser default; the VFS is serialized and saved to IndexedDB, debounced on change, so a box survives a reload.
  • MemoryPersistenceBackend — volatile; nothing is written out.

Serialization is exposed directly too (serialize / deserialize, SerializedNode) if you want to manage box images yourself.

Snapshots

Beyond incremental persistence, a box's whole disk can be exported as a tar.gz and restored elsewhere — the basis for saving, cloning, and moving boxes between environments.

const image = await sandbox.fs.exportSnapshot(); // Uint8Array (tar.gz)
await otherSandbox.fs.importSnapshot(image);

// Skip large, reproducible trees for a much smaller snapshot:
const slim = await sandbox.fs.exportSnapshot({ exclude: ["node_modules"] });

exclude matches path segments; a snapshot taken without node_modules restores in milliseconds but needs a reinstall (npm install) before it runs.

Syncing

Snapshots are a whole-disk dump. To mirror a box live — between two tabs, over a socket, or to a database one row per file — use the change primitives (dumpChanges / applyChanges / watchChanges) built on the VFS's watch events. See Sync the filesystem for full-dump-vs-incremental guidance and copy-paste recipes.

Note

Snapshots are whole-disk, point-in-time images. A write-ahead log for crash-consistent, incremental durability is on the roadmap, alongside choosing the backing store per environment (IndexedDB/OPFS in the browser, the host filesystem via the CLI, or purely volatile).