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]
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 byworkers/api/src/index.ts) - never servable through/api/files/:key, only throughapps/admin-web/worker/index.ts's authenticated/api/referenceroute.freelancer-avatar-<userId>-<uuid>.<ext>for freelancer profile photos (written byapps/software-web/worker/index.ts) - the one key shape/api/files/:keyactually 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.