Skip to content

storage: R2 + GCS + Azure drivers (S-10 from #1873 / #1887 split-out) #1896

@glennmichael123

Description

@glennmichael123

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:

  1. 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)
  2. 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:

  1. R2 first — pair with the ts-cloud endpoint fix; smallest delta from the existing S3 driver
  2. GCS second — most common ask after S3
  3. 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

  • R2: extends `S3StorageAdapter` with custom-endpoint config; `publicUrl()` honors custom domain; ts-cloud upstream lands or local SigV4 client implemented
  • GCS: full `StorageAdapter` implementation including streaming + signed URLs; peer-dep config in `package.json`
  • Azure: full `StorageAdapter` implementation including streaming + SAS-signed URLs; peer-dep config in `package.json`
  • Per-driver setup docs (auth + bucket/container creation)
  • Test coverage per driver (basic CRUD + signed URL minimum)

Referenced from #1887.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions