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.
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/.mpdand 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
- Log in to the Cloudflare Dashboard.
- Navigate to R2 -> Create Bucket.
- In the bucket settings, enable Public Access (R2.dev subdomain) or connect a custom domain.
- 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
- In the R2 dashboard, click Manage R2 API Tokens.
- Click Create API Token.
- Select Object Read & Write permissions (Minimal).
- Copy the Account ID, Access Key ID, and Secret Access Key.
Step 4: Configure the App
- Open the Upload Modal in the app.
- Expand Storage Settings.
- Enter the Account ID, Access Key ID, Secret Access Key, and Public Bucket URL.
- Click Verify & Save.
The app will attempt to upload a small test file to verify the credentials and public access configuration.
Upload lifecycle
-
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).
- The Upload modal calls
-
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.
-
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.
-
UI feedback
- The Upload modal receives progress, status, and verification errors via
r2Serviceevents.
- The Upload modal receives progress, status, and verification errors via
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 ofnpub→{ 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.
- The system now relies on explicit user configuration for the Public Bucket URL (
- 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.
- There is no custom retry loop inside
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.