How to Share Files via R2 Presigned URLs with @cloudflare/computer/assets

The @cloudflare/computer/assets module provides a streamlined API to upload files to Cloudflare R2 and generate time-limited, presigned GET URLs that allow secure, temporary public access without exposing your credentials.

The cloudflare/computer repository provides a powerful framework for building AI agents that execute code in secure, isolated workspaces. When these agents generate files that need to be shared with end users—such as charts, screenshots, or build artifacts—the @cloudflare/computer/assets module offers a secure way to upload those files to Cloudflare R2 and create presigned URLs for temporary access.

Setting Up the Assets Client

Prerequisites

Before generating presigned URLs, you need an R2 bucket binding configured in your Worker environment. The assets client requires S3-compatible credentials (account ID, access key, and secret key) to authenticate with R2's API, though these are typically read from environment variables rather than hardcoded.

Initializing the Client

Create an assets client by calling createAssets() from packages/computer/src/assets/index.ts (lines 3-14). The function accepts a configuration object containing your workspace instance, R2 bucket binding, and optional S3 credentials.

import { createAssets } from "@cloudflare/computer/assets";

const assets = createAssets({
  ws,                                 // WorkspaceLike (provides fs.stat/readFile and sessionId)
  bucket: env.ASSETS,                // R2 binding from the Worker environment
  s3: { bucket: "agent-assets" },    // R2 bucket name; credentials are read from env by default
});

The ws parameter must implement the WorkspaceLike interface, providing filesystem methods (fs.stat, fs.readFile) and a sessionId for organizing uploads.

Uploading and Sharing Files

The share Method

The primary method for generating presigned URLs is assets.share(), implemented in packages/computer/src/assets/index.ts (lines 101-130). This method handles the complete workflow: streaming the file from the workspace's virtual file system, uploading it to R2 via bucket.put(), and returning a presigned URL generated using AWS Signature Version 4.

const url = await assets.share("/workspace/out/image.png", {
  expiresAfter: 30_000,                 // 30 seconds (maximum 7 days)
  prefix: `/agent-${ws.sessionId}`,     // optional key prefix
  disposition: "attachment",           // force download (optional)
});

Configuration Options

The share method accepts an options object that controls the generated URL's behavior:

  • expiresAfter: The time-to-live in milliseconds, capped at 7 days (604,800 seconds) per R2's maximum limit
  • prefix: A string prepended to the object key for organizational purposes, such as namespacing by agent session ID
  • disposition: When set to "attachment", sets the Content-Disposition header to force browser downloads rather than inline display

How Presigned URLs Work Under the Hood

When you call share(), the module streams the file using a FixedLengthStream sized according to fs.stat(path). The actual upload occurs via the R2 binding's put() method, defined in packages/computer/src/assets/upload.js.

After a successful upload, the presigned URL is generated using the AWS SigV4 algorithm implemented in packages/computer/src/assets/sigv4.ts (lines 101-118). This algorithm creates a signed request that embeds temporary authentication parameters into the URL query string, allowing anyone with the URL to access the object without possessing your actual R2 credentials.

Consuming the Presigned URL

The returned URL points directly to the R2 object at /assets/<bucket>/<key>. Because it includes the presigned authentication parameters, anyone with the URL can download the file via standard HTTP GET requests without additional authentication.

// Example consumer (could be a browser, curl, etc.)
const response = await fetch(url);
const blob = await response.blob();
// Save or display the file

Summary

Frequently Asked Questions

What is the maximum expiration time for R2 presigned URLs?

R2 accepts a maximum expiration of 7 days (604,800 seconds) for presigned URLs. While the expiresAfter parameter in the share() method accepts milliseconds, values exceeding this limit will be constrained by R2's service-side validation.

How does @cloudflare/computer/assets handle file streaming?

The implementation creates a FixedLengthStream sized from fs.stat(path) to stream the file content efficiently from the workspace's virtual file system to R2 via bucket.put(). This approach avoids loading entire files into memory, making it suitable for large artifacts generated by Computer agents.

Can I customize the object key prefix when sharing files?

Yes. Pass the prefix option when calling share() to prepend a custom string to the object key. This is particularly useful when organizing assets by session ID or agent type, as shown in the pattern prefix: /agent-${ws.sessionId}.

Where does the assets client obtain R2 authentication credentials?

While you explicitly configure the bucket name via the s3.bucket parameter, the S3-compatible credentials (account ID, access key, secret key) are read from environment variables by default. You can override this behavior by passing a complete credentials object to the S3 configuration in createAssets().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →