Skip to content

This document describes the environment variables required for file storage functionality using Cloudflare R2.

Required Variables

S3_BUCKET

  • Description: Name of your Cloudflare R2 bucket
  • Example: tendsocial
  • Required: Yes
  • Used by: S3 library for all upload/download operations

S3_REGION

  • Description: AWS region (use auto for Cloudflare R2)
  • Example: auto
  • Required: Yes
  • Used by: S3 client initialization

S3_ENDPOINT

  • Description: Cloudflare R2 endpoint URL
  • Example: https://370e3c7ed6e50972fea1a19da4eb21c6.r2.cloudflarestorage.com
  • Required: Yes (for R2)
  • Used by: S3 client to connect to R2 instead of AWS S3
  • How to find: Available in your R2 bucket settings under "Bucket Details"

AWS_ACCESS_KEY_ID

  • Description: R2 access key ID
  • Example: ad056f0bb0e13dbd9683a87936492083
  • Required: Yes
  • Used by: S3 client authentication
  • How to get: Create in Cloudflare Dashboard → R2 → Manage R2 API Tokens

AWS_SECRET_ACCESS_KEY

  • Description: R2 secret access key
  • Example: 4486318247cbd52ef5b1a2b9ebef8d0c011940361a5988881520adc459680db0
  • Required: Yes
  • Used by: S3 client authentication
  • Security: Store as a secret in production (Google Cloud Secret Manager)

Private Media Delivery (Story 6.10.1)

⚠️ IMPORTANT: Media delivery is PRIVATE-ONLY. Do NOT configure public CDN domains.

Forbidden Variables (Story 6.10.1)

These variables MUST NOT be set. The server will refuse to boot if any are present:

  • CDN_DOMAIN - Custom public delivery domain
  • CDN_BASE_URL - Public asset base URL
  • R2_PUBLIC_DOMAIN - Public R2 domain
  • R2_PUBLIC_BUCKET - Public bucket flag
  • R2_PUBLIC_BUCKET_URL - Public bucket URL
  • S3_PUBLIC_URL - Public S3 URL
  • PUBLIC_BUCKET - Public bucket flag
  • R2_DEV_URL - Direct r2.dev binding
  • ASSET_BASE_URL - Legacy public-delivery binding
  • MEDIA_CDN_URL - Legacy public-delivery binding

Media Proxy Configuration

For asset delivery to external platforms and browsers:

  • MEDIA_PROXY_BASE_URL: Base URL of the private media proxy (e.g., https://media-proxy.tendsocial.com)
  • MEDIA_PROXY_SECRET: Shared secret for HMAC validation of proxy requests
  • Description: Routes external asset requests through a verified-domain proxy that enforces authentication and lease-based access
  • Required: Recommended for production
  • Used by: Media delivery to social platforms and in-browser rendering

Environment File Examples

Development (.env.local)

bash
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/tendsocial

# Auth
JWT_SECRET=your-dev-jwt-secret
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_PUBLISHABLE_KEY=your-supabase-key

# AI
GOOGLE_GENAI_API_KEY=your-gemini-api-key

# Storage (Cloudflare R2) - PRIVATE ONLY
S3_BUCKET=tendsocial-dev
S3_REGION=auto
S3_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
AWS_ACCESS_KEY_ID=your-r2-access-key-id
AWS_SECRET_ACCESS_KEY=your-r2-secret-access-key

# Media Proxy (for private asset delivery)
MEDIA_PROXY_BASE_URL=https://media-proxy-dev.tendsocial.com
MEDIA_PROXY_SECRET=your-dev-media-proxy-secret

# Cloudflare API (optional, for deployment verification)
CLOUDFLARE_API_TOKEN=your-cloudflare-api-token
CLOUDFLARE_ACCOUNT_ID=your-account-id

Production (Google Cloud Run Secrets)

Set these as environment variables or secrets in your Cloud Run service:

bash
# Storage (Cloudflare R2) - PRIVATE ONLY
S3_BUCKET=tendsocial
S3_REGION=auto
S3_ENDPOINT=https://370e3c7ed6e50972fea1a19da4eb21c6.r2.cloudflarestorage.com
AWS_ACCESS_KEY_ID=<from-secret-manager>
AWS_SECRET_ACCESS_KEY=<from-secret-manager>

# Media Proxy (for private asset delivery)
MEDIA_PROXY_BASE_URL=https://media-proxy.tendsocial.com
MEDIA_PROXY_SECRET=<from-secret-manager>

# Cloudflare API (for deployment verification)
CLOUDFLARE_API_TOKEN=<from-secret-manager>
CLOUDFLARE_ACCOUNT_ID=your-account-id

Validation & Deployment Verification

The application enforces private media delivery at startup:

  1. Configuration Gate (assertPrivateMediaDelivery):

    • Fails if any public-delivery env vars are set
    • Fails if S3_ENDPOINT or R2_ENDPOINT_URL contains .r2.dev
    • Runs automatically when the server starts (see apps/backend/src/index.ts)
  2. Deployment Verification (optional, recommended):

    • Run npx tsx apps/backend/src/scripts/verify-deployment.ts
    • Checks config gate + live R2 bucket state (if Cloudflare API creds present)
    • Verifies that r2.dev managed domain is disabled
    • Verifies that custom domains are not active
    • Exit code: 0 (success) or 1 (violation)

Example:

bash
cd apps/backend
npx tsx src/scripts/verify-deployment.ts

Security Best Practices

  1. Never commit credentials to Git

    • Use .env files (add to .gitignore)
    • Use environment variables in CI/CD
    • Use secret managers in production
  2. Rotate credentials regularly

    • Create new R2 API tokens every 90 days
    • Update in all environments
  3. Use least privilege

    • R2 API tokens should only have permissions for the specific bucket
    • Avoid using account-wide admin tokens
  4. Monitor usage

    • Set up alerts for unusual upload patterns
    • Monitor R2 storage and request metrics

Troubleshooting

"S3_BUCKET environment variable is not set"

  • Ensure S3_BUCKET is set in your environment
  • Check that the variable is loaded (e.g., via dotenv)

"Access Denied" errors

  • Verify AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are correct
  • Check that the R2 API token has permissions for the bucket
  • Ensure the token hasn't expired

Files upload but authenticated previews don't work

  • Check S3_ENDPOINT and private R2 credentials.
  • Confirm CDN_DOMAIN, CDN_BASE_URL, public custom domains, and r2.dev access are disabled.
  • Verify the asset API returns a fresh short-lived viewUrl; do not test or expose direct R2 URLs.

CORS errors in browser

  • Ensure CORS is configured in R2 bucket settings
  • See CDN Setup Guide for CORS configuration

References

TendSocial Documentation