Skip to content

R2 Operations

Purpose

Inspecting the production R2 bucket safely, without uploading or deleting real objects as part of routine operations work.

Architecture/context

Single bucket, bound as FILES by three Workers: workers/api, apps/software-web, and apps/admin-web (see Cloudflare → R2) - corrected from an earlier version of this page, which also listed corporate-web; verified directly against apps/corporate-web/wrangler.jsonc, which has no r2_buckets block. Object keys follow two application-level conventions for access-gating - not a bucket ACL - see Infrastructure → Security: a private/... prefix for embroidery reference images (authenticated-admin-only download), and an explicit freelancer-avatar-<userId>-<uuid>.<ext> allowlist shape for the one publicly downloadable category.

Safe read commands

npx wrangler r2 object get <bucket>/<key> --pipe > /dev/null              # [REMOTE, READ-ONLY]
# list is not natively supported by `wrangler r2` for arbitrary prefixes in this Wrangler version;
# use the Cloudflare dashboard (R2 → bucket → browse) for a read-only listing instead.

Mutating commands - avoid outside a deliberate, understood change

npx wrangler r2 object put <bucket>/<key> --file <path>                   # [REMOTE / MUTATING]
npx wrangler r2 object delete <bucket>/<key>                              # [REMOTE / MUTATING]
Do not run these against production objects as part of routine documentation, testing, or exploration - per this portal's own operating constraint (no uploading/deleting real production objects). Use local wrangler dev R2 emulation for any hands-on testing instead (see Deployment → Local).

Object naming

  • private/embroidery-references/<contactId>/<uuid>.<ext> for embroidery reference-image uploads (written by workers/api/src/index.ts) - never servable through /api/files/:key, only through apps/admin-web/worker/index.ts's authenticated /api/reference route.
  • freelancer-avatar-<userId>-<uuid>.<ext> for freelancer profile photos (written by apps/software-web/worker/index.ts) - the one key shape /api/files/:key actually serves.

Troubleshooting

Symptom Check
/api/files/:key returns 404 for a key you can see in the dashboard Key isn't the recognized public freelancer-avatar-... shape - refused by the allowlist in workers/api/src/public-file-access.ts, not missing from the bucket
Upload fails from the embroidery form 5MB limit / JPEG-PNG-WebP-only validation in the API Worker - check the actual file against those constraints first
Upload fails from freelancer profile-photo settings 3MB limit / JPEG-PNG-WebP sniffed-content validation in apps/software-web/worker/index.ts
An app-bound R2 access (Software/Admin) behaves unexpectedly Confirm that app's own Worker code path, not just the central API Worker - three Workers share this bucket, see Architecture → Data Platform

Rollback/recovery

No object versioning or lifecycle policy found configured on this bucket (verified: no lifecycle rules present in any wrangler.jsonc's r2_buckets block, and the bootstrap script only creates the bucket with defaults). A deleted object has no automated recovery path.

Security considerations

See Infrastructure → Security. /api/files/:key was hardened in fix/platform-hardening from a private/-prefix blocklist to an explicit allowlist of the one publicly-servable key shape - still an application convention, not enforced by Cloudflare, so treat workers/api/src/public-file-access.ts as security-critical if modifying it. The private embroidery category has always required, and still requires, apps/admin-web/worker/index.ts's requireAdmin() session auth to retrieve.