- Add new "Setup Guide" section to `docs/storage-r2.md` detailing the dual-credential requirement (S3 Keys + Cloudflare API Token) for full automation. - Remove unsupported "manual/S3-only" workflow documentation to prevent user confusion. - Update `js/services/r2Service.js` error message to explicitly prompt users for an API Token when the public domain is missing, instead of suggesting manual configuration.
7.0 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.
- 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.
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 a Cloudflare API Token. This allows the app to automatically create buckets, configure CORS, and enable the public r2.dev domain for your videos.
Step 1: Get your Account ID
- Log in to the Cloudflare Dashboard.
- Navigate to R2 in the sidebar.
- Copy the Account ID from the right-hand sidebar.
Step 2: Create an S3 Access Key (for uploads)
- In the R2 dashboard, click Manage R2 API Tokens.
- Click Create API token.
- Select Admin Read & Write permissions.
- Click Create API Token.
- Copy the Access Key ID and Secret Access Key.
Step 3: Create a Cloudflare API Token (for automation)
- Go to My Profile > API Tokens.
- Click Create Token -> Custom Token -> Get started.
- Token name: e.g., "BitVid R2 Automation".
- Permissions:
- Account -> Workers R2 Storage -> Edit
- Account Resources: Select your account.
- Click Continue to summary -> Create Token.
- Copy the token string.
Step 4: Configure the App
- Open the Upload Modal in the app.
- Expand Storage Settings.
- Paste the Account ID.
- Paste the Access Key ID and Secret Access Key (from Step 2).
- Paste the Cloudflare API Token (from Step 3).
- Click Save.
The app is now ready to upload. It will automatically configure your bucket on the first upload.
Upload lifecycle
-
Settings saved locally
- The Upload modal calls
r2Service.saveSettings()to persist Cloudflare credentials and optional domain configuration. Settings are stored in IndexedDB (bitvidSettings→kv) with a localStorage fallback (bitvid:r2Settings).
- The Upload modal calls
-
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
zoneIdandbaseDomain.- If the custom domain cannot be activated or already exists, it falls back to the managed
*.r2.devdomain.
- If the custom domain cannot be activated or already exists, it falls back to the managed
- If no API token is available, it:
- Uses the last-known bucket/domain (or builds a managed
*.r2.devURL), - Tries to set CORS via S3 keys when possible.
- Uses the last-known bucket/domain (or builds a managed
- If an API token is provided, the service:
-
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 managed-domain fallback warnings via
r2Serviceevents.
- The Upload modal receives progress, status, and managed-domain fallback warnings 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.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).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:
- Custom domain attachment is attempted when
apiToken,zoneId, andbaseDomainare present. - If custom domain attachment fails or is unavailable, the service falls back to the managed
*.r2.devdomain and surfaces a warning to the user.
- Custom domain attachment is attempted when
- 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.
- 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 and API tokens 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
zoneIdand a Cloudflare API token with the necessary R2 and DNS permissions. Keep tokens scoped to least privilege.
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.