# 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.md`](../README.md) and [`docs/storage-tab-design.md`](./storage-tab-design.md) for 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`](./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`/`.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////.`, 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 **Storage Tab** (in Profile or Upload modal). 2. Add a new connection and select **Cloudflare R2**. 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. **Credential Retrieval:** - When an upload starts, `UploadModal` retrieves the decrypted credentials from `StorageService`. - `StorageService` handles the NIP-44/04 decryption of the Master Key and subsequent decryption of the R2 connection payload. 2. **Upload Execution:** - `r2Service.multipartUpload()` uses the standard AWS SDK commands (`CreateMultipartUpload`, `UploadPart`, etc.) via `js/storage/s3-multipart.js` and the `makeR2Client` adapter. - The adapter automatically constructs the R2 endpoint (`https://.r2.cloudflarestorage.com`) using the stored `accountId`. 3. **Publish:** - On success, `publishVideoNote()` is called with the hosted URL, enabling URL-first playback. ## 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_r2` - `accountId`: 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 in `js/storage/s3-multipart.js` and 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.