Split out from #1887 — the per-tenant scoping piece (S-11) shipped in `a150b8971`. The three driver implementations are tracked here because each blocks on a different prerequisite.
R2 (Cloudflare)
R2 is S3-API-compatible. Implementation is mostly a thin extension of `S3StorageAdapter` with:
- Custom endpoint: `https://.r2.cloudflarestorage.com`
- `region: 'auto'` (R2 has no real regions)
- Custom-domain support for `publicUrl()` (R2-mapped domain)
- R2-specific signed URL rules (some headers differ from S3 standard)
Blocker: ts-cloud's `S3Client(region?: string, profile?: string)` constructor doesn't accept a custom endpoint. Either:
- Upstream fix: file ts-cloud to add `endpoint?: string` to the constructor — minimal change, unlocks R2 plus any other S3-compatible store (MinIO, DigitalOcean Spaces, Backblaze B2)
- Local fix: implement R2 as a fully-standalone SigV4 client without ts-cloud (~150 lines of HMAC signing)
Option 1 is cleaner.
GCS (Google Cloud Storage)
Needs `@google-cloud/storage` as a peer dep. Different API entirely from S3:
- Auth: service-account credentials JSON OR `GOOGLE_APPLICATION_CREDENTIALS` env var OR explicit `projectId + keyFilename`
- Signed URLs: V4 signing via the SDK's `getSignedUrl()`
- Resumable uploads (similar to S3 multipart but different protocol)
- Custom-domain public URLs need bucket-level config
Blocker: peer dep install + the testing story (Google's official SDK is heavy; the local-dev story is awkward without real GCS access).
Azure Blob Storage
Needs `@azure/storage-blob` as a peer dep. Auth options:
- Connection string
- Account name + account key
- `DefaultAzureCredential` (managed identity / env-based)
SAS tokens for signed URLs; `BlockBlobClient` for upload (with multipart-equivalent for >256MB).
Blocker: peer dep install + Azure's SDK has its own auth-flow surprises (storage account types, soft-delete defaults, RBAC permissions).
Scope for the follow-up
Each driver is independently shippable. Phasing suggestion:
- R2 first — pair with the ts-cloud endpoint fix; smallest delta from the existing S3 driver
- GCS second — most common ask after S3
- Azure last — smallest user base; can land standalone
All three plug into the existing `ScopedStorageAdapter` from `a150b8971` without further changes — multi-tenant scoping works the moment they ship.
Acceptance
Referenced from #1887.
Split out from #1887 — the per-tenant scoping piece (S-11) shipped in `a150b8971`. The three driver implementations are tracked here because each blocks on a different prerequisite.
R2 (Cloudflare)
R2 is S3-API-compatible. Implementation is mostly a thin extension of `S3StorageAdapter` with:
Blocker: ts-cloud's `S3Client(region?: string, profile?: string)` constructor doesn't accept a custom endpoint. Either:
Option 1 is cleaner.
GCS (Google Cloud Storage)
Needs `@google-cloud/storage` as a peer dep. Different API entirely from S3:
Blocker: peer dep install + the testing story (Google's official SDK is heavy; the local-dev story is awkward without real GCS access).
Azure Blob Storage
Needs `@azure/storage-blob` as a peer dep. Auth options:
SAS tokens for signed URLs; `BlockBlobClient` for upload (with multipart-equivalent for >256MB).
Blocker: peer dep install + Azure's SDK has its own auth-flow surprises (storage account types, soft-delete defaults, RBAC permissions).
Scope for the follow-up
Each driver is independently shippable. Phasing suggestion:
All three plug into the existing `ScopedStorageAdapter` from `a150b8971` without further changes — multi-tenant scoping works the moment they ship.
Acceptance
Referenced from #1887.