diff --git a/content/upload-content.md b/content/upload-content.md index 01d3cfcd..3d469920 100644 --- a/content/upload-content.md +++ b/content/upload-content.md @@ -9,36 +9,61 @@ bitvid is a decentralized platform, but for larger video files, we currently sup ## Step-by-Step Guide -### 1. Create an API Token - -To let bitvid upload files to your bucket, you need an API Token with permission to edit R2 storage. +### 1. Create a Bucket & Enable Public Access 1. Log in to your [Cloudflare Dashboard](https://dash.cloudflare.com/). 2. Navigate to **R2** in the sidebar. -3. On the right side of the R2 Overview page, look for **"Manage R2 API Tokens"** and click it. -4. Click the **"Create API Token"** button. -5. **Configure the token:** - - **Token name**: Enter something recognizable, e.g., "bitvid-upload". - - **Permissions**: Select **"Admin Read & Write"** (easiest) or select the specific **"Workers R2 Storage"** permission with **"Edit"** access. - - **Specific Bucket(s)**: You can grant access to "All buckets" or a specific one if you already created it. - - **TTL**: Set this to "Forever" or a duration of your choice. -6. Click **"Create API Token"**. +3. Click **"Create Bucket"**. +4. Give it a name (e.g., `my-bitvid-videos`) and click **"Create Bucket"**. +5. Go to the **Settings** tab of your new bucket. +6. Scroll down to **"Public Access"** and click **"Enable"** under "R2.dev subdomain". +7. **Copy the Public Bucket URL** (it looks like `https://pub-xxxxxx.r2.dev`). You will need this later. -### 2. Copy Your Credentials +### 2. Configure CORS -Once created, you will see your credentials. **Do not close this page yet!** You cannot see the Secret Access Key again. +To allow your browser to upload files directly to Cloudflare, you must allow Cross-Origin Resource Sharing (CORS). -Copy the following values: +1. Still in your bucket's **Settings** tab, scroll down to **"CORS Policy"**. +2. Click **"Add CORS Policy"** (or Edit). +3. Paste the following JSON configuration: -- **Access Key ID**: Usually starts with a random string. -- **Secret Access Key**: A long string of characters (this is your password). -- **Account ID**: This is found in the URL of your dashboard or on the R2 Overview page (e.g., `https://dash.cloudflare.com//r2`). +```json +[ + { + "AllowedOrigins": ["*"], + "AllowedMethods": ["GET", "PUT", "DELETE"], + "AllowedHeaders": ["*"], + "ExposeHeaders": ["ETag", "Content-Length", "Content-Range", "Accept-Ranges"], + "MaxAgeSeconds": 3600 + } +] +``` +> **Note:** You can replace `"*"` in `AllowedOrigins` with your specific domain (e.g., `["https://bitvid.network"]`) for tighter security, but `"*"` is easiest for getting started. -### 3. Configure bitvid +4. Click **"Save"**. -1. Return to the **Upload Video** modal in bitvid. -2. Click **"Configure R2 Storage"**. -3. Enter your **Account ID**, **Access Key ID**, and **Secret Access Key**. -4. Click **"Save Credentials"**. +### 3. Create an API Token -bitvid will automatically attempt to create a private bucket for your videos if one doesn't exist, or you can use the "Advanced Options" to specify a custom bucket or domain. +1. Go back to the **R2 Overview** page. +2. Click **"Manage R2 API Tokens"** (right sidebar). +3. Click **"Create API Token"**. +4. **Configure the token:** + - **Token name**: e.g., "bitvid-upload". + - **Permissions**: Select **"Object Read & Write"**. This is the minimal permission needed to upload and delete files. + - **Specific Bucket(s)**: Select the bucket you created in Step 1. + - **TTL**: "Forever". +5. Click **"Create API Token"**. + +### 4. Configure bitvid + +1. Copy the **Access Key ID**, **Secret Access Key**, and your **Account ID** (found on the R2 Overview page). +2. Return to the **Upload Video** modal in bitvid. +3. Click **"Configure R2 Storage"**. +4. Enter your: + - **Account ID** + - **Access Key ID** + - **Secret Access Key** + - **Public Bucket URL** (from Step 1) +5. Click **"Verify & Save"**. + +bitvid will verify your credentials by uploading a small test file. Once verified, you are ready to upload! diff --git a/docs/storage-r2.md b/docs/storage-r2.md index 27d237c0..1d3e629f 100644 --- a/docs/storage-r2.md +++ b/docs/storage-r2.md @@ -4,9 +4,9 @@ `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. -- Ensuring the target bucket exists, has CORS rules, and has a public domain (custom or managed). - 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. @@ -25,56 +25,40 @@ The object key format is generated by `buildR2Key()` in `js/r2.js` and looks lik ## Setup Guide: Getting your Cloudflare Credentials -To enable R2 uploads, you must provide a Cloudflare API Token. This allows the app to automatically create buckets, configure CORS, and enable the public `r2.dev` domain for your videos. +To enable R2 uploads, you must provide Cloudflare S3-compatible credentials and configure your bucket for public access and CORS. -### Step 1: Get your Account ID +### Step 1: Create Bucket & Enable Public Access 1. Log in to the **Cloudflare Dashboard**. -2. Navigate to **R2** in the sidebar. -3. Copy the **Account ID** from the right-hand sidebar. +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: Create an S3 Access Key (for uploads) +### 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 **Admin Read & Write** permissions. -4. Click **Create API Token**. -5. Copy the **Access Key ID** and **Secret Access Key**. - -### Step 3: Create a Cloudflare API Token (for automation) -1. Go to **My Profile** > **API Tokens**. -2. Click **Create Token** -> **Custom Token** -> **Get started**. -3. **Token name:** e.g., "BitVid R2 Automation". -4. **Permissions:** - * **Account** -> **Workers R2 Storage** -> **Edit** -5. **Account Resources:** Select your account. -6. Click **Continue to summary** -> **Create Token**. -7. Copy the token string. +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. Paste the **Account ID**. -4. Paste the **Access Key ID** and **Secret Access Key** (from Step 2). -5. Paste the **Cloudflare API Token** (from Step 3). -6. Click **Save**. +3. Enter the **Account ID**, **Access Key ID**, **Secret Access Key**, and **Public Bucket URL**. +4. Click **Verify & Save**. -The app is now ready to upload. It will automatically configure your bucket on the first upload. +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 and - optional domain configuration. Settings are stored in IndexedDB (`bitvidSettings` → `kv`) + - 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 and domain preparation** (`ensureBucketConfigForNpub`) - - If an API token is provided, the service: - - Creates the bucket if it does not exist. - - Applies CORS rules for the current origin. - - Attempts to attach a custom domain using the configured `zoneId` and `baseDomain`. - - If the custom domain cannot be activated or already exists, it falls back to the managed - `*.r2.dev` domain. - - If no API token is available, it: - - Uses the last-known bucket/domain (or builds a managed `*.r2.dev` URL), - - Tries to set CORS via S3 keys when possible. +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. @@ -83,8 +67,7 @@ The app is now ready to upload. It will automatically configure your bucket on t with magnet fallback. 4. **UI feedback** - - The Upload modal receives progress, status, and managed-domain fallback warnings via - `r2Service` events. + - 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 @@ -96,9 +79,7 @@ back to magnet/WebTorrent when needed, preserving the URL-first playback promise - `accountId`: Cloudflare account ID. - `accessKeyId`: R2 S3 access key ID. - `secretAccessKey`: R2 S3 secret access key. -- `apiToken`: Cloudflare API token used for bucket/domain management. -- `zoneId`: Cloudflare zone ID (required for custom domain attachment). -- `baseDomain`: Base domain used to derive per-user subdomains (e.g., `videos.example.com`). +- `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:** @@ -112,24 +93,20 @@ back to magnet/WebTorrent when needed, preserving the URL-first playback promise ## Retry and fallback behavior - **Bucket/domain setup:** - - Custom domain attachment is attempted when `apiToken`, `zoneId`, and `baseDomain` are present. - - If custom domain attachment fails or is unavailable, the service falls back to the managed - `*.r2.dev` domain and surfaces a warning to the user. + - The system now relies on explicit user configuration for the Public Bucket URL (`baseDomain`). If this is missing or incorrect, verification will fail. - **CORS:** - - CORS setup is attempted via the API token or (secondarily) via S3 credentials. Failures are - logged and do not block the upload, but missing CORS may prevent playback from browsers. + - 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 and API tokens are stored in browser storage. Treat them as +- **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:** CORS rules are scoped to the current origin to avoid overly broad exposure. -- **Domain ownership:** Custom domain attachment requires `zoneId` and a Cloudflare API token with - the necessary R2 and DNS permissions. Keep tokens scoped to least privilege. +- **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