Files
bitvid/docs/storage-r2.md
T
google-labs-jules[bot] c21bffb0c1 Update R2 documentation for manual public access setup
Updates `content/upload-content.md` and `docs/storage-r2.md` to reflect the current manual configuration workflow for Cloudflare R2 storage.

Key changes:
- Removes deprecated instructions for generating "Admin Read & Write" or "Workers R2 Storage" automation tokens.
- Documents the new requirement to manually create a bucket and enable "Public Access" (r2.dev subdomain) in the Cloudflare Dashboard.
- Adds explicit instructions for manually configuring CORS policy via the Cloudflare Dashboard, providing the required JSON configuration.
- Aligns the "Configure bitvid" steps with the application's current UI, which requires the "Public Bucket URL" as an explicit input.
- Clarifies that "Object Read & Write" permissions are sufficient and recommended for security.
2026-01-13 03:04:29 +00:00

6.1 KiB

Cloudflare R2 integration (r2Service)

Purpose and scope

js/services/r2Service.js is the client-side coordinator for Cloudflare R2 uploads. It owns:

  • Local persistence of Cloudflare credentials and bucket metadata used by the Upload modal.
  • Uploading the selected video/HLS asset via multipart S3 uploads and publishing the resulting public URL into the video note payload.
  • Verifying public access via the configured Public Bucket URL.

The service is used from the Upload modal UI (via js/ui/components/UploadModal.js) and is not a backend integration.

Assets stored in R2

R2 is used as the URL-first playback source. The upload path supports a single file per upload, but file types include:

  • Video files (MP4/WebM/MOV/MKV/MPG/MPEG).
  • HLS/DASH assets (manifest .m3u8/.mpd and segment files like .ts/.m4s).
  • Related static media if the operator chooses to upload them separately (images, subtitles such as .vtt/.srt), which benefit from cache-friendly headers set in the upload flow.

The object key format is generated by buildR2Key() in js/r2.js and looks like: u/<npub>/<YYYY>/<MM>/<slug>.<ext>, which keeps uploads scoped by user and month.

Setup Guide: Getting your Cloudflare Credentials

To enable R2 uploads, you must provide Cloudflare S3-compatible credentials and configure your bucket for public access and CORS.

Step 1: Create Bucket & Enable Public Access

  1. Log in to the Cloudflare Dashboard.
  2. Navigate to R2 -> Create Bucket.
  3. In the bucket settings, enable Public Access (R2.dev subdomain) or connect a custom domain.
  4. Copy the Public Bucket URL (e.g., https://pub-xxxxxx.r2.dev).

Step 2: Configure CORS

Manually add a CORS policy in the bucket settings to allow uploads from your app's origin (or *). (See content/upload-content.md for the recommended JSON configuration).

Step 3: Create S3 Credentials

  1. In the R2 dashboard, click Manage R2 API Tokens.
  2. Click Create API Token.
  3. Select Object Read & Write permissions (Minimal).
  4. Copy the Account ID, Access Key ID, and Secret Access Key.

Step 4: Configure the App

  1. Open the Upload Modal in the app.
  2. Expand Storage Settings.
  3. Enter the Account ID, Access Key ID, Secret Access Key, and Public Bucket URL.
  4. Click Verify & Save.

The app will attempt to upload a small test file to verify the credentials and public access configuration.

Upload lifecycle

  1. Settings saved locally

    • The Upload modal calls r2Service.saveSettings() to persist Cloudflare credentials. Settings are stored in IndexedDB (bitvidSettings → kv) with a localStorage fallback (bitvid:r2Settings).
  2. Bucket preparation

    • The service relies on the user ensuring the bucket exists and is configured for CORS.
    • It attempts a "best-effort" auto-configuration using the S3 keys (requires broader permissions), but strictly supports the manual "Object Read & Write" flow.
  3. Upload and publish

    • The service builds the R2 object key and public URL.
    • multipartUpload() uploads the file using S3 multipart uploads.
    • On success, publishVideoNote() is called with the hosted URL, so playback can be URL-first with magnet fallback.
  4. UI feedback

    • The Upload modal receives progress, status, and verification errors via r2Service events.

Download / playback flow

There is no direct “download” API for R2 in r2Service. Instead, R2 provides the public URL that gets saved into the video note payload. Playback uses that URL as the primary source and only falls back to magnet/WebTorrent when needed, preserving the URL-first playback promise.

Configuration keys and where they live

Stored locally (IndexedDB / localStorage):

  • accountId: Cloudflare account ID.
  • accessKeyId: R2 S3 access key ID.
  • secretAccessKey: R2 S3 secret access key.
  • baseDomain: The Public Bucket URL (e.g., https://pub-xxxx.r2.dev) used to generate file URLs.
  • buckets: Cached map of npub → { bucket, publicBaseUrl, domainType, lastUpdated }.

Environment / global flags:

  • globalThis.__BITVID_DISABLE_NETWORK_IMPORTS__: disables the AWS SDK network import used by the R2 S3 client loader (js/storage/r2-s3.js). When true, uploads will fail with a load error until the SDK is bundled or otherwise provided.

Config files:

  • There are no R2-specific keys in config/ today; all R2 settings are entered through the UI and stored locally in the browser.

Retry and fallback behavior

  • Bucket/domain setup:
    • The system now relies on explicit user configuration for the Public Bucket URL (baseDomain). If this is missing or incorrect, verification will fail.
  • CORS:
    • The app attempts to set CORS if permissions allow, but largely assumes the user has configured it manually if using minimal tokens.
  • Multipart uploads:
    • There is no custom retry loop inside multipartUpload(). Failures propagate to the UI; the caller can retry by re-initiating the upload.

Security considerations

  • Credential storage: R2 credentials are stored in browser storage. Treat them as sensitive and avoid sharing browser profiles. Clear settings via the Upload modal when done.
  • Public exposure: The generated URLs are public; uploaded assets should be safe for public access. Use signed URLs only if you add a separate access layer.
  • CORS and origins: Ideally, scope CORS rules to the current origin to avoid overly broad exposure, though * is often used for ease of setup.
  • Minimal Permissions: We recommend "Object Read & Write" tokens to limit the scope of the keys to specific buckets and prevent admin actions.

Impact on uploads and playback

  • Uploads: Successful R2 uploads produce a hosted URL stored in the video note payload, enabling URL-first playback. Metadata such as MIME type and the public URL are also stitched into the NIP-71 metadata block during publish.
  • Playback: The hosted URL is the primary stream source. If it fails, playback can still fall back to the magnet/WebTorrent path, so the R2 integration improves reliability without removing P2P fallback.