Files
bitvid/docs/storage-r2.md
T
google-labs-jules[bot] 3a86ad5b20 docs: Add detailed R2 setup guide and clarify API token requirement
- 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.
2026-01-09 02:44:25 +00:00

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/.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 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

  1. Log in to the Cloudflare Dashboard.
  2. Navigate to R2 in the sidebar.
  3. Copy the Account ID from the right-hand sidebar.

Step 2: Create an S3 Access Key (for uploads)

  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.

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.

The app is now ready to upload. It will automatically configure your bucket on the first upload.

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) 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.
  3. 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.
  4. UI feedback

    • The Upload modal receives progress, status, and managed-domain fallback warnings via r2Service events.

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 of npub → { 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, 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.
  • 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.

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 zoneId and 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.