Cloudflare Filestore Workspace
The CloudflareFileStoreWorkspace is the lightest Cloudflare option for durable file storage. Files live in the agent's own Durable Object SQLite via @cloudflare/shell's Workspace. No container, no cold start, optional R2 binding for large-file spill.
When to use
- Cloudflare DO–hosted agents that need durable file storage.
- You want files to survive DO hibernation cleanly with no recovery dance.
- You don't need a shell or code interpreter — just files.
If you need shell or code execution on Cloudflare, use Cloudflare Sandbox.
Capabilities supported
| Capability | Supported |
|---|---|
fs | ✅ |
shell | ❌ |
code | ❌ |
snapshot | ❌ |
Provider config
interface CloudflareFileStoreWorkspaceConfig {
kind: 'cloudflare-filestore';
/** Scope for table names within the DO's SQLite. Defaults to a sanitized form of the session ID. */
namespace?: string;
/** Name of the R2 bucket binding on `env`, used for large-file spill. */
r2Binding?: string;
/** Byte threshold above which files spill to R2. Defaults to @cloudflare/shell's 1.5MB. */
inlineThreshold?: number;
}Namespace handling
@cloudflare/shell requires the namespace to match /^[a-zA-Z][a-zA-Z0-9_]*$/. Most session IDs don't satisfy that (dashes, etc.), so the provider hex-encodes invalid session IDs with an s_ prefix automatically. The transformation is deterministic, so the same session ID always yields the same namespace across reads.
If you set namespace explicitly in config, it must satisfy the regex — the provider throws on invalid explicit namespaces (no silent rewriting).
Wrangler setup
The agent DO and the filestore share the SAME DO. No separate sandbox container required.
[[durable_objects.bindings]]
name = "AGENTS"
class_name = "MyAgentServer"
[[migrations]]
tag = "v1"
new_sqlite_classes = ["MyAgentServer"]
# Optional: R2 bucket for large-file spill
[[r2_buckets]]
binding = "FILES_R2"
bucket_name = "my-files"new_sqlite_classes is required — @cloudflare/shell uses SQLite-backed DO storage.
Provider wiring
import { createAgentServer, AgentRegistry, CloudflareFileStoreWorkspaceProvider } from '@helix-agents/runtime-cloudflare';
import type { WorkspaceProvider } from '@helix-agents/core';
interface Env {
AGENTS: DurableObjectNamespace;
FILES_R2?: R2Bucket; // optional
OPENAI_API_KEY: string;
}
export const MyAgentServer = createAgentServer<Env>({
llmAdapter: (env) => /* ... */,
agents: registry,
workspaceProviders: (env, ctx) =>
new Map<string, WorkspaceProvider>([
['cloudflare-filestore', new CloudflareFileStoreWorkspaceProvider(ctx, env as unknown as Record<string, unknown>)],
]),
});The provider takes (ctx, env) because it needs ctx.storage.sql for the SQLite-backed file store. The double-cast as unknown as Record<string, unknown> is required because the typed Env doesn't have an index signature.
Cross-hibernation behavior
Cloudflare Durable Objects can hibernate after periods of inactivity. When the DO wakes:
- The framework calls
provider.resolve(ref)with the persistedWorkspaceRef. - The ref payload contains the namespace + optional R2 binding name.
resolve()reattaches by constructing a newWorkspacewith the same namespace againstctx.storage.sql— same data shows up because the SQLite tables persist.
No data loss. No recovery dance.
Auto-injected tools
All fs tools (see FileSystem module for schemas):
workspace__<name>__read_file,write_file,edit_file,ls,glob,grep,stat,mkdir,rm
Code sample (full integration)
// agent.ts
import { defineAgent } from '@helix-agents/core';
export const NoteTakingAgent = defineAgent({
name: 'note-taker',
llmConfig: { model: yourModel },
workspaces: {
notes: {
provider: { kind: 'cloudflare-filestore' },
capabilities: { fs: true },
},
},
systemPrompt: `You're a note-taking agent. Use workspace__notes__write_file to save notes to /notes/<slug>.md`,
});
// my-agent-server.ts
const registry = new AgentRegistry();
registry.register(NoteTakingAgent);
export const MyAgentServer = createAgentServer<Env>({
llmAdapter: (env) => new VercelAIAdapter(),
agents: registry,
workspaceProviders: (env, ctx) =>
new Map([['cloudflare-filestore', new CloudflareFileStoreWorkspaceProvider(ctx, env as unknown as Record<string, unknown>)]]),
});
// worker.ts
export { MyAgentServer };
export default {
async fetch(req, env): Promise<Response> {
const sessionId = new URL(req.url).searchParams.get('session') ?? `demo-${Date.now()}`;
const stub = env.AGENTS.get(env.AGENTS.idFromName(sessionId));
return stub.fetch(req);
},
};Limitations
- fs only. No shell, code, or snapshot. Use Cloudflare Sandbox for those.
- DO-local. Files are scoped to the agent's DO instance. They are not shared across DO instances; cross-DO workspace sharing is reserved for a future plan.
- Workflows runtime not supported. Workspaces require the DO runtime path (
createAgentServer) — Cloudflare Workflows currently has no native workspace providers.