Important
The @cloudflare/computer package should be considered suitable for
production use. The package contains work-in-progress and experimental
components such as the ContainerBackend. These should be considered
unsuitable for production use. See the status reference for more details.
The specification in this directory is forward-looking β read it for intent, not as description of the code today.
The @cloudflare/computer package provides an out of the box virtual filesystem for use in any Durable Object β it's persistent and backed by SQLite. It's primarily designed for agents that need small, portable filesystems and tools to work with.
It provides:
- A fs API for working with files and directories compatible with Worker bindings.
- Read-only mounts for pre-filling the workspace tree from an R2 bucket or from files shipped with the Worker.
- Durability over DO restarts for all file operations.
- Pluggable execution backends selected through
workspace.runtime: a Cloudflare Container shell, a just-bash Dynamic Worker, or an isolated ECMAScript-module Dynamic Worker. - Isolated JavaScript with structured input/results, durable relative imports, configured libraries, durable
node:fs/promises, host modules such asws:git,ws:container,ws:assetsandws:tools, and managed execution records. - Workspace constructable without a backend, for filesystem-only use cases.
- Out-of-the-box agent tools for the AI SDK (
createAITools()in@cloudflare/computer/tools/ai-sdk), pi (createPiTools()in@cloudflare/computer/tools/pi-ai), and TanStack AI (createTanStackTools()in@cloudflare/computer/tools/tanstack-ai).
It comes with the following limitations:
- ~10GB maximum (it shares storage with the DO).
- The container-side filesystem is held in memory, so very large trees aren't a fit. Aim for agent-scale workspaces, not full monorepos.
- Container access goes through FUSE, so heavy IO workloads (large
node_modulesinstalls, big tarball extractions) take a measurable performance hit compared to a native filesystem.
Install the package into your Worker/Agent project:
npm install @cloudflare/computerThe package ships several entrypoints:
| Entrypoint | Purpose |
|---|---|
@cloudflare/computer |
The Workspace wrapper, first-class workspace.runtime, stub types, the R2 and Worker bundle mounts, and proxy classes. |
@cloudflare/computer/backends/container |
ContainerBackend and withWorkspaceContainer, for a container the durable object schedules (scheduling_policy: "durable_object"). Same sync plumbing; the launch names the image and the instance size. |
@cloudflare/computer/backends/container-legacy |
Deprecated; use @cloudflare/computer/backends/container. LegacyContainerBackend and withLegacyWorkspaceContainer, for a container the platform schedules and sizes from the containers block. |
@cloudflare/computer/backends/worker-shell |
WorkerShellBackend and the bundled just-bash command runtime. |
@cloudflare/computer/backends/worker-javascript |
WorkerJavaScriptBackend, configured libraries, durable relative imports, node:fs/promises, and host modules. |
@cloudflare/computer/git |
Opt-in isomorphic-git glue for working with checkouts inside the workspace. Bundled lazily, with pako replaced by Workers node:zlib, and kept out of the default @cloudflare/computer graph. |
@cloudflare/computer/artifacts |
createArtifact, an optionally session-scoped wrapper over the Cloudflare Artifacts Workers binding, plus its argv CLI. |
@cloudflare/computer/modules/container |
createContainerModule() for ws:container: run container commands from isolate JavaScript. |
@cloudflare/computer/modules/git |
createGitModule() for ws:git: confined Git from isolate JavaScript. |
@cloudflare/computer/modules/artifacts |
createArtifactsModule() for ws:artifacts: Artifacts and git tokens from isolate JavaScript. |
@cloudflare/computer/modules/assets |
createAssetsModule() for ws:assets: publish a Workspace file from isolate JavaScript. |
@cloudflare/computer/modules/tools |
createToolBindings() for ws:tools: an agent's own tools, callable from isolate JavaScript. |
@cloudflare/computer/tools |
AI SDK tools for agents: read, write, edit, ls, optional exec, and optional publish. |
@cloudflare/computer/tools/core |
defineExec(): the exec tool's description, schema, and executor, with no agent library. |
@cloudflare/computer/tools/ai-sdk |
createAITools(): the AI SDK tool set for a Workspace. |
@cloudflare/computer/tools/pi-ai |
createPiTools(): the same tool set for pi, as declarations plus a function that runs a tool call. |
@cloudflare/computer/tools/tanstack-ai |
createTanStackTools(): the same tool set for TanStack AI, as the list chat({ tools }) takes. |
A consumer that only uses the container backend never imports the worker subpath, so the just-bash payload tree-shakes away.
Wire types shared with the in-container service live in the sibling package @cloudflare/computer-rpc (subpaths ./server, ./client, ./driver).
The container needs the computerd daemon alongside a FUSE runtime. The
simplest pattern, used by examples/container/Dockerfile,
copies the prebuilt binary out of the public GHCR image and into a thin
Debian base:
FROM ghcr.io/cloudflare/computer-computerd-linux-x64:0.5.1 AS computerd
FROM debian:stable-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
fuse3 libfuse2t64 ca-certificates \
&& rm -rf /var/lib/apt/lists/*
COPY --from=computerd /usr/local/bin/computerd /usr/local/bin/computerd
ENV PORT=8080
ENV MOUNT_POINT=/workspace
ENV FUSE_MOUNT=auto
EXPOSE 8080
ENTRYPOINT ["/usr/local/bin/computerd"]To build the binary from source instead, run npm run build:bin --workspace @cloudflare/computerd, which emits
artifacts/computerd/computerd-linux-x64, then COPY that into the
image.
computerd's own default port is 45678; the Cloudflare container backend pins the in-image listener to 8080, which is what examples/container uses. See 07. Injected Service for the env vars (PORT, MOUNT_POINT, FUSE_MOUNT, EXEC_LOG_MAX_BYTES) and the reverse-dial boot sequence.
import { Workspace } from "@cloudflare/computer";
import {
ContainerBackend,
withWorkspaceContainer,
} from "@cloudflare/computer/backends/container";
import { DurableObject } from "cloudflare:workers";
export class Agent extends withWorkspaceContainer(class extends DurableObject<Env> {}) {
readonly backend = new ContainerBackend({
container: () => this,
workspace: { binding: "Agent", id: this.ctx.id.toString() },
name: "app",
instance: "standard-2",
});
readonly workspace = new Workspace({
storage: this.ctx.storage, // Durable Object storage β VFS lives here
backends: [this.backend],
});
async initialize() {
await this.workspace.ready();
await this.workspace.fs.mkdir("/workspace", { recursive: true });
}
override fetch(request: Request): Promise<Response> {
return this.backend.handleFetch(request);
}
}Once you have a workspace on your Durable Object, the fs and runtime surfaces feel a lot like Node's fs/promises plus routed command/module execution β everything is async, paths are absolute, and operations are durable across DO restarts.
Create and write files:
// Write a string (utf8 by default for strings).
await this.workspace.fs.writeFile("/workspace/notes/todo.md", "- [ ] ship it\n");
// Write binary content.
await this.workspace.fs.writeFile("/workspace/data/blob.bin", new Uint8Array([1, 2, 3]));
// Stream a large upload straight to disk.
await this.workspace.fs.writeFile("/workspace/uploads/big.csv", request.body!);Read files back:
// As a string.
const todo = await this.workspace.fs.readFile("/workspace/notes/todo.md", "utf8");
// As a stream β handy for piping into a Response.
const stream = await this.workspace.fs.readFile("/workspace/uploads/big.csv");
return new Response(stream);Create and walk directories:
await this.workspace.fs.mkdir("/workspace/notes/daily", { recursive: true });
for (const entry of await this.workspace.fs.readdir("/workspace/notes")) {
console.log(entry.isDirectory ? `d ${entry.name}` : `f ${entry.name}`);
}Remove files and directories:
await this.workspace.fs.rm("/workspace/notes/todo.md");
await this.workspace.fs.rm("/workspace/notes/daily", { recursive: true });Search across the tree:
const hits = await this.workspace.fs.grep("TODO", "/workspace", { ignoreCase: true });
for (const hit of hits) {
console.log(`${hit.path}:${hit.line}: ${hit.text}`);
}Run a shell command in the sandbox β the same filesystem is mounted there, so writes from fs are immediately visible to exec and vice versa:
const run = await this.workspace.runtime.exec("ls -la /workspace", { encoding: "utf8" });
const { stdout, exitCode } = await run.result();
console.log(stdout, exitCode);exec returns a ReadableStream of events as well as the buffered result(). That makes it straightforward to forward live output to the browser as a Server-Sent Events stream β just transform each event into an SSE frame:
// Inside a fetch handler on your Agent.
async fetch(request: Request) {
const run = await this.workspace.runtime.exec("npm test", { encoding: "utf8" });
const sse = run.pipeThrough(
new TransformStream<
| { id: string; seq: number; name: "stdout" | "stderr"; value: string }
| { id: string; seq: number; name: "exit"; value: number },
Uint8Array
>({
transform(event, controller) {
// SSE frame: `event: <name>\ndata: <json>\n\n`
const frame = `event: ${event.name}\ndata: ${JSON.stringify(event.value)}\n\n`;
controller.enqueue(new TextEncoder().encode(frame));
},
}),
);
return new Response(sse, {
headers: {
"content-type": "text/event-stream",
"cache-control": "no-cache",
"connection": "keep-alive",
},
});
}On the client:
const events = new EventSource("/agent/run");
events.addEventListener("stdout", (e) => console.log(JSON.parse(e.data)));
events.addEventListener("stderr", (e) => console.warn(JSON.parse(e.data)));
events.addEventListener("exit", (e) => { console.log("exit", JSON.parse(e.data)); events.close(); });This package is documented as a set of focused topics. Start with the overview above, then dive into the area you're working on.
The status column says whether the API a document describes is stable or experimental. Reference documents describe the code or its performance rather than an API.
| Document | Status | Topic |
|---|---|---|
| 01. VFS | Stable | Layout of the workspace tree, reserved paths, and mount points. |
| 02. Sync Protocol | Experimental | How the DO-backed VFS synchronises with the sandbox container. |
| 03. Filesystem Schema | Stable | SQLite schema backing the virtual filesystem. |
| 04. Filesystem Interface | Stable | Workspace.fs API: readFile, writeFile, mkdir, grep, etc. |
| 05. Runtime Interface | Stable | Workspace.runtime.exec/getExec/killExec/disposeExec and backend routing. |
| 06. Mount Interface | Stable | Pre-filling paths from R2, Artifacts, GitHub, and custom sources. (not yet implemented) |
| 07. Injected Service | Experimental | The in-container computerd service that backs FUSE and shell. |
| 08. Capnweb Interface | Experimental | RPC wire protocol between the DO and the sandbox. |
| 09. Tool Interface (Agents) | Stable | Ready-made AI SDK tools for @cloudflare/agents. |
| 10. Project Layout | Reference | Source tree of this package and how the pieces fit together. |
| 11. Lifecycle | Mixed: stub disposal is stable; the container and capnweb lifecycle are experimental | DO incarnations, container lifetime, capnweb session lifecycle, and hibernation. |
| 12. Worker backend | Experimental | Running the shell as just-bash inside a Dynamic Worker loaded through env.LOADER. |
| 13. Git interface | Stable | workspace.git and the git CLI inside the shell, backed by isomorphic-git. |
| 14. Assets interface | Stable | share a workspace file to R2 and get back a presigned URL. |
| 15. Artifacts interface | Stable | createArtifact and the artifacts CLI, an optionally session-scoped wrapper over the Cloudflare Artifacts binding. |
| 16. Execution runtime architecture | Stable | One runtime entry point over command and module backends. |
| 17. Isolate JavaScript runtime | Stable, except ws:container |
ECMAScript modules, durable imports, configured libraries, durable node:fs/promises, host modules, and managed lifecycle. |
| 18. Runtime migration | Reference | Breaking preview-API mappings from public shell and script-execution surfaces to workspace.runtime. |
| 19. Performance | Reference | Filesystem benchmarks: fs-bench numbers, an npm install comparison, and how to reproduce them. |
interface Workspace {
fs: WorkspaceFilesystem;
runtime: WorkspaceRuntime;
/** Push pending DO-side writes to the configured backend. Resolves with the entry count. */
push(): Promise<number>;
/** Pull backend-side writes back into the DO. Resolves with { applied, skipped }. */
pull(): Promise<ApplyResult>;
/** Lazy-connect over the configured backends. Idempotent; safe to call from `onStart`. */
ready(): Promise<void>;
/** Wrap this Workspace in a stub for crossing the Workers RPC boundary. */
stub(): WorkspaceStub;
/** Tear down backend connections. */
close(): Promise<void>;
}See 04. Filesystem Interface and
05. Runtime Interface for the full surface, and 02. Sync Protocol for push/pull semantics.
