cf-vfs is a tree-shakable byte-oriented virtual filesystem and Bash-compatible
runtime for Cloudflare Workers. The default shell API executes independent,
complete source units; an optional /shell/interactive entry point preserves
shell state between units. One SQLite-backed Durable Object owns a strongly
consistent pathname namespace. Files up to 8 MiB can be stored inline and read
by shell utilities; large payloads live as immutable R2 objects and are
intentionally opaque to the shell.
import { DurableObject } from "cloudflare:workers";
import { Shell, type RemoteExecuteTextOptions } from "@corca-ai/cf-vfs/shell";
import { defaultShellCommands } from "@corca-ai/cf-vfs/shell/commands/default";
import { DurableObjectFileSystem } from "@corca-ai/cf-vfs/storage/do-sql";
import { R2OpaqueStore } from "@corca-ai/cf-vfs/storage/r2";
interface Env {
WORKSPACES: DurableObjectNamespace<WorkspaceFiles>;
FILE_BODIES: R2Bucket;
}
export class WorkspaceFiles extends DurableObject<Env> {
private readonly fileSystem: DurableObjectFileSystem;
private readonly shell: Shell;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.fileSystem = new DurableObjectFileSystem(ctx.storage, {
opaqueStore: new R2OpaqueStore(env.FILE_BODIES),
workspaceId: ctx.id.toString(),
});
this.shell = new Shell({
fileSystem: this.fileSystem,
commands: defaultShellCommands,
});
}
executeText(options: RemoteExecuteTextOptions) {
return this.shell.executeText(options);
}
override async alarm() {
await this.fileSystem.drainGarbage();
}
}
export async function execute(env: Env, workspaceId: string) {
const workspace = env.WORKSPACES.getByName(workspaceId);
return workspace.executeText({
script: `find src -name '*.ts' | sort > files.txt`,
cwd: "/workspace",
});
}WorkspaceFiles extends only Cloudflare's required DurableObject; the cf-vfs
parts are composed normally. DurableObjectFileSystem stores paths and inline
bytes in ctx.storage (SQLite), while R2OpaqueStore connects the FILE_BODIES
R2 binding for opaque-body verification and garbage collection. WORKSPACES
routes each workspace to its Durable Object. See Getting
started for the Wrangler bindings, migration, and
direct-to-R2 upload path.
From a repository checkout, run npm run repl for a local line-oriented
session backed by Node's in-memory SQLite. npm run repl:sqlite runs the same
terminal UI against a disposable SQLite-backed Durable Object in local
workerd, exercising Cloudflare's storage API as well as the shared SQL VFS.
This is an application runtime, not an operating-system shell or POSIX ABI. It does not launch processes, mount a host filesystem, or provide OS TTY/job control. The supported language is an explicit versioned subset, and every parser, execution, stream, mutation, and storage boundary is bounded.
The pre-1.0 stream-first redesign is intentionally breaking. The old
{ command, input } structured executor and text/binary storage split have
been removed.
Read docs/index.md for setup, language and command semantics, the SQLite/R2 lifecycle, limits, benchmarks, and compatibility details.
MIT