5.7 KiB
Cloudflare R2 integration (r2Service)
Purpose and scope
js/services/r2Service.js is the specialized provider handler for Cloudflare R2 uploads. It implements one of the supported S3 Upload Modes (specifically Mode 1: Browser-held keys).
Security Note: This integration requires storing encrypted credentials in the browser. See
README.mdanddocs/storage-tab-design.mdfor security warnings and alternative upload modes for untrusted environments.
The Storage Tab (powered by StorageService) is the single supported configuration path for R2 credentials. The deprecated quick-upload panel (quickR2Section / js/r2-quick.js) has been removed to avoid conflicting credential sources. r2Service is responsible for:
- Uploading the selected video/HLS asset via multipart S3 uploads.
- Verifying public access via the configured Public Bucket URL.
- Publishing the resulting public URL into the video note payload.
Architecture Note: For the high-level design of credential storage, encryption, and the provider adapter pattern, please refer to
docs/storage-tab-design.md.
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.
- Use the exact bucket name derived by the app. bitvid derives the bucket name from your npub (sanitized), and the bucket configured in Cloudflare must exactly match what the app expects. If the app logs show
bucketName: 'npub1qxduthz4p8v5zsu-ftrlaz', create/configure that exact bucket name in Cloudflare and paste it into the dashboard before moving on. - 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 *).
(CORS must be configured on the S3 API endpoint bucket, not the public URL domain.)
(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 Storage Tab (in Profile or Upload modal).
- Add a new connection and select Cloudflare R2.
- 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
-
Credential Retrieval:
- When an upload starts,
UploadModalretrieves the decrypted credentials fromStorageService. StorageServicehandles the NIP-44/04 decryption of the Master Key and subsequent decryption of the R2 connection payload.
- When an upload starts,
-
Upload Execution:
r2Service.multipartUpload()uses the standard AWS SDK commands (CreateMultipartUpload,UploadPart, etc.) viajs/storage/s3-multipart.jsand themakeR2Clientadapter.- The adapter automatically constructs the R2 endpoint (
https://<accountId>.r2.cloudflarestorage.com) using the storedaccountId.
-
Publish:
- On success,
publishVideoNote()is called with the hosted URL, enabling URL-first playback.
- On success,
Configuration keys
R2 credentials are no longer stored in plain bitvidSettings. They are encapsulated within the StorageService data model. Legacy r2Settings are migrated into StorageService on first unlock and then cleared.
R2 Connection Profile (Encrypted):
provider:cloudflare_r2accountId: Cloudflare account ID.accessKeyId: S3 access key ID.secretAccessKey: S3 secret access key.bucket: The target bucket name.
Metadata (Plaintext):
baseDomain: The Public Bucket URL (e.g.,https://pub-xxxx.r2.dev).defaultForUploads: Boolean flag indicating if this is the active upload target.
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. Multipart helpers live injs/storage/s3-multipart.jsand rely on the same SDK import.
Security considerations
- Encrypted Storage: All sensitive keys (
accessKeyId,secretAccessKey) are encrypted at rest using the user's Nostr signer. - Session Security: Decrypted keys exist in memory only during the active session.
- Public Exposure: The generated playback URLs are public. Ensure your bucket permissions match your intent.
- Least Privilege: Always use "Object Read & Write" tokens rather than Admin tokens.