Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesUploads R2Uploads & R2

Uploads & R2

Two-stage upload pipeline that fronts Cloudflare R2 for every user-supplied binary in FitKit — progress photos, exercise comment media, message attachments, organization logos, signed compliance PDFs, and tokenized form signatures.

What

  • r2.service.ts — thin S3-flavoured client over Cloudflare R2 with bucket-aware reads, presigned URL minting, and a Redis-cached presigned-GET helper.
  • uploads.service.ts + uploads.controller.ts — the “generic” presigned upload pipeline used for any owner type that follows the canonical stage-then-link pattern (exercise_comment, progress_photo, plus an internal message policy).
  • Domain-specific upload paths inside progress-photos, workouts, messages, forms, organizations, and class-sessions all funnel through R2Service for object storage, even when they bypass the staged-upload Redis flow.

Why

  • R2’s S3-compatible API is cheap, geographically distributed, and Cloudflare-hosted alongside the platform’s other infra.
  • Direct-to-R2 presigned URLs eliminate proxying large files through the API host.
  • A Redis-staged design lets us atomically link an upload to its parent on the writing transaction, preventing dangling objects and avoiding “orphan after auth failure” cleanup work.
  • Per-owner policy tables (MIME + size) keep media constraints out of the leaf services.

Who

  • Members — upload progress photos, exercise comment media, message images, completed form signatures.
  • Coaches — upload media on exercise comments, messages.
  • Owners — upload organization logos, minisite hero / gallery images.
  • Anonymous (token-gated) — submit tokenized form signatures from a mobile signing flow without a Clerk session.

Persona impact

PersonaImpact
MemberSmooth media uploads with size limits enforced before the client wastes bandwidth.
CoachReliable media attached to coaching threads without proxying through the API.
OwnerOrg branding (logo, minisite hero) hosted at the CDN edge for sub-second LCP.
Compliance signatoryTokenized signature upload from a phone, no signup required.

Capabilities

  • Presigned PUT URL minting (10-min TTL by default).
  • Per-owner MIME + size policy enforcement at presign time.
  • Two-stage flow: presign → client uploads → parent write consumeMany deletes the Redis staging key.
  • Presigned GET URL minting with optional Redis cache (TTL = expiresIn - 300s) keyed by (bucket, key, expiresIn).
  • Dedicated compliance bucket (R2_COMPLIANCE_BUCKET_NAME) for FIT-158 signed PDFs to enable bucket-level retention policies.
  • Token-gated public PUT for tokenized form signatures (no Clerk auth required).
  • progress-photos — owner type progress_photo; thumbnails generated server-side after upload.
  • exercises / workout-results — owner type exercise_comment; image + video allowed.
  • messages-commentsmessage policy (images only, no video to keep thread bandwidth low).
  • forms (FIT-158 signature uploads) — uses R2Service directly with the compliance bucket.
  • organizations — logo upload uses getPresignedUploadUrl directly, no staging.
  • minisites — references R2 objects published from the dashboard editor.

Status

Shipped. The staged-upload pipeline is the canonical pattern for new owner types.

Gaps

  • No background cleanup for Redis staging keys that expire without being consumed. The R2 object becomes orphaned. Acceptable for now (low volume); a sweeper job is on the backlog.
  • No virus scanning (ClamAV / S3 ObjectLambda). Risk surface: small org-scoped media accepted from authenticated members.
  • No content-type sniffing — we trust the client Content-Type. A malicious upload could spoof image/png for a binary; downstream consumers must not execute uploaded files (they don’t today).
  • No per-org storage quota — heavy uploaders are not throttled today. Tracked as future work alongside platform-billing.
  • Compliance bucket lifecycle policy (7-year immutability) must be set bucket-side; the code uses the bucket but doesn’t manage retention.