Files
bitvid/docs/storage-r2.md
T

99 lines
5.7 KiB
Markdown

# 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/<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. **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.
4. In the bucket settings, enable **Public Access** (R2.dev subdomain) or connect a custom domain.
5. 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
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://<accountId>.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.