Skip to content

Cloudflare R2

R2 is the Information Bearing Entity (IAO) store — it carries the physical representations of information content. Three categories of IBEs are stored:

CategoryPath patternScenarioOntological character
Design assetsassets/{hash_prefix}/{filename}Asset IngestIBE carrying design asset ICE
JSONL archivesarchives/ingest/{run_id}.jsonlBulk SyncIBE carrying Process Record data
CSV exportsexports/csv/{export_id}.csvCSV ExportIBE carrying Document Artifact

The rewrite uses R2 Worker bindings instead of the current @aws-sdk/client-s3:

// Current (removed)
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const client = new S3Client({
region: 'auto',
endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: { accessKeyId, secretAccessKey }
});
// Target (binding API)
await env.R2_BUCKET.put(key, body, {
httpMetadata: { contentType: 'image/png' }
});
const object = await env.R2_BUCKET.get(key);
const url = `${env.R2_PUBLIC_URL}/${key}`;

Benefits of the binding API:

  • No credentials — access is automatic via Worker binding
  • No SDK dependency — eliminates @aws-sdk/client-s3 (and its transitive deps)
  • Lower latency — avoids external HTTP hop
  • Simpler error handling — native Worker error types

Asset uploads detect content type from file extension:

ExtensionsContent type
.png, .jpg, .jpeg, .gif, .webp, .svg, .ico, .bmpimage/*
.pdfapplication/pdf
.jsonapplication/json
.txt, .csvtext/*
(default)application/octet-stream

Assets are served via a public R2 custom domain configured in the Cloudflare dashboard. The URL pattern is:

https://assets.bluntcases.com/{key}

This bearer_entity_url replaces the CLOUDFLARE_R2_PUBLIC_URL environment variable — the domain is stable and doesn’t need per-environment configuration. The URL is stored in information_content_entity.bearer_entity_url and written to the Shopify metafield.

For large files (design assets > 5MB), R2 supports multipart upload via the binding API:

const upload = await env.R2_BUCKET.createMultipartUpload(key);
// ... upload parts
await upload.complete(parts);

Most design assets are under 5MB so single-part put() is the common path.