@computesdk/cloudflare
Cloudflare provider for ComputeSDK — execute code in secure, isolated containers on Cloudflare's edge network.
Installation
npm install @computesdk/cloudflare
Setup
Deploy the bridge Worker in worker/. It needs a Cloudflare account on the Workers Paid plan and Docker running locally; wrangler deploy builds the container images and pushes them to Cloudflare's registry.
git clone https://github.com/computesdk/computesdk.git
cd computesdk/packages/cloudflare/worker
npm ci
npx wrangler login
openssl rand -hex 32 # use the output as the API key
npx wrangler secret put SANDBOX_API_KEY
npx wrangler deploy
Then configure your application with the deployed Worker URL and the same API key:
CLOUDFLARE_SANDBOX_URL=https://computesdk-sandbox.<your-subdomain>.workers.dev
CLOUDFLARE_SANDBOX_API_KEY=<same value as SANDBOX_API_KEY>
You can also run:
npx @computesdk/cloudflare
to print these setup instructions.
A Worker deployed from the Cloudflare sandbox demo keeps working for commands and files. The adapter checks GET /v1/info and uses the bridge's native file transfer, preview URLs and snapshots only when the Worker lists them.
Preview URLs
With the bridge Worker, sandbox.getUrl({ port }) returns <sandboxUrl>/v1/sandbox/<id>/ports/<port>/, which forwards HTTP and WebSocket requests to that port. The server must listen on 0.0.0.0. Preview URLs need no API key: anyone with the URL can reach the port.
Snapshots
With the bridge Worker, compute.snapshot.create(sandboxId) saves the sandbox's filesystem and compute.instances.create({ snapshotId }) starts a new sandbox from it. Snapshots keep files only, not memory or running processes. Cloudflare keeps snapshot data for 30 days after its creation or last restore; snapshot.delete removes the handle from the bridge.
Quick Start
import { cloudflare } from '@computesdk/cloudflare';
const compute = cloudflare({
sandboxUrl: process.env.CLOUDFLARE_SANDBOX_URL,
sandboxApiKey: process.env.CLOUDFLARE_SANDBOX_API_KEY,
});
const sandbox = await compute.instances.create();
const result = await sandbox.runCommand(
"node -e 'console.log(\"Hello from Cloudflare!\")'"
);
console.log(result.stdout);
await sandbox.destroy();
Usage
Run Commands
Use normal shell commands inside the sandbox:
await sandbox.runCommand('echo "Hello from Cloudflare"');
await sandbox.runCommand("node -e 'console.log(\"Hello Node.js\")'");
List Files
const result = await sandbox.runCommand('ls -la /tmp/app');
console.log(result.stdout);
File System
// Create directories
await sandbox.filesystem.mkdir('/tmp/app/data');
// Write and read files
await sandbox.filesystem.writeFile('/tmp/app/config.json', JSON.stringify({ key: 'value' }));
const content = await sandbox.filesystem.readFile('/tmp/app/config.json');
// List directory contents
const files = await sandbox.filesystem.readdir('/tmp/app');
// Check existence
const exists = await sandbox.filesystem.exists('/tmp/app/config.json');
// Remove files
await sandbox.filesystem.remove('/tmp/app/temp.txt');
Environment Variables
Pass environment variables to sandbox commands at initialization:
const compute = cloudflare({
sandboxUrl: process.env.CLOUDFLARE_SANDBOX_URL,
sandboxApiKey: process.env.CLOUDFLARE_SANDBOX_API_KEY,
envVars: {
API_KEY: 'your-api-key',
DATABASE_URL: 'postgresql://localhost:5432/mydb',
},
});
Or per-sandbox at creation time:
const sandbox = await compute.instances.create({
envs: { NODE_ENV: 'production' },
});
Sandbox Size and Image
Choose the size and image of a new sandbox when you create it:
const sandbox = await compute.instances.create({
vcpu: 2,
memoryMib: 6144,
diskMb: 12000,
image: 'builder',
});
| Option | Description |
|---|---|
vcpu | vCPUs of a custom instance type |
memoryMib | Memory of the custom instance type, in MiB |
diskMb | Disk of the custom instance type, in MB |
image | Name of an image declared by the Worker. builder has Node.js 24 and common build tools. |
Set vcpu, memoryMib, and diskMb together. Without them, the sandbox uses the Worker's default instance type. Without image, it uses the Worker's default image.
Configuration
interface CloudflareConfig {
/** URL of the deployed bridge Worker (or sandbox demo Worker) */
sandboxUrl?: string;
/** API key that matches the Worker's SANDBOX_API_KEY secret */
sandboxApiKey?: string;
/** Deprecated compatibility alias for sandboxApiKey */
sandboxSecret?: string;
/** Durable Object binding to the Worker's Sandbox class (direct mode only) */
sandboxBinding?: any;
/** Execution timeout in milliseconds (default 30,000; maximum 900,000) */
timeout?: number;
/** Environment variables to pass to sandbox commands */
envVars?: Record<string, string>;
}
Direct Mode
If your code already runs inside a Cloudflare Worker, bind directly to the Sandbox Durable Object exported by the Worker:
{
"durable_objects": {
"bindings": [
{
"name": "SANDBOX",
"class_name": "Sandbox",
"script_name": "sandbox"
}
]
}
}
Then pass that binding to the provider:
import { cloudflare } from '@computesdk/cloudflare';
const compute = cloudflare({
sandboxBinding: env.SANDBOX,
});
Error Handling
const result = await sandbox.runCommand('command-that-does-not-exist');
if (result.exitCode !== 0) {
console.error(result.stderr);
}
Limitations
- Resource limits apply based on your Cloudflare plan
- Some system calls may be restricted in the container environment
- Preview URLs and snapshots need the bridge Worker, and are not available in direct mode
- Listing all sandboxes is not supported — use
getByIdto reconnect to a specific sandbox ID.
License
MIT