Rindle exposes storage capabilities as an explicit adapter contract so adopters can tell which upload and delivery flows are expected to work before wiring a profile to a backend. Unsupported flows fail with tagged tuples instead of falling back silently.
This guide is the canonical capability reference for v1.1. Other guides link here instead of repeating provider matrices inline.
Shipped Capability Vocabulary
Rindle currently knows these capability atoms:
| Capability | Meaning today |
|---|---|
:presigned_put | The adapter can mint a direct-upload URL for a single-object PUT. |
:multipart_upload | The adapter can initiate, sign parts for, complete, and abort an S3-style multipart upload. |
:signed_url | The adapter can mint a time-limited signed delivery URL for private delivery. |
:head | The adapter can read remote object metadata for verification and cleanup. |
:local | The adapter is backed by local filesystem semantics. |
:resumable_upload | The adapter can drive a resumable upload lifecycle instead of a single-shot direct PUT. |
:resumable_upload_session | The adapter can create and continue provider-owned resumable upload sessions. |
These atoms are shipped vocabulary, but adapter-specific. Rindle does not imply that every storage backend supports them.
Unsupported Flow Contract
Rindle uses tagged unsupported tuples when a flow requires a capability the adapter does not advertise:
| Flow family | Tagged error |
|---|---|
| Upload | {:error, {:upload_unsupported, capability}} |
| Delivery | {:error, {:delivery_unsupported, capability}} |
Examples:
Rindle.Storage.Localreturns{:error, {:upload_unsupported, :multipart_upload}}for multipart entrypoints.- Private delivery against an adapter without
:signed_urlreturns{:error, {:delivery_unsupported, :signed_url}}. - A resumable path against an adapter that does not advertise it remains
intentionally explicit:
{:error, {:upload_unsupported, :resumable_upload}}.
These failures are part of the adopter-facing contract. Rindle does not guess, downgrade, or silently swap in another flow.
Provider Matrix
The table below describes the current v1.1 posture for adapters and common provider choices.
| Backend / provider | Runtime seam | Expected capabilities today | Proof posture | Notes |
|---|---|---|---|---|
| Local filesystem | Rindle.Storage.Local | [:local, :presigned_put] | Automated in the default test suite | Presigned PUT is a local-development parity shim, not a remote-object-store claim. Rindle.Storage.Local does not advertise :resumable_upload or :resumable_upload_session. |
| MinIO | Rindle.Storage.S3 | [:presigned_put, :head, :signed_url, :multipart_upload] | Automated in default CI and local integration lanes | This is the always-on real S3-compatible proof for direct PUT, multipart upload, metadata verification, and signed delivery URL generation. Rindle.Storage.S3 does not advertise the resumable capability family. |
| Generic S3-compatible provider | Rindle.Storage.S3 | [:presigned_put, :head, :signed_url, :multipart_upload] | Expected by contract; not proven against every vendor in default CI | Rindle uses the shipped S3 adapter seam. Provider-specific behavior beyond that seam should be validated in adopter-owned environments. |
| Cloudflare R2 | Rindle.Storage.S3 | [:presigned_put, :head, :signed_url, :multipart_upload] when the provider honors the shipped S3-compatible operations | Documented compatibility target; adopters validate vendor behavior in their own environments | Phase 8 does not add a bespoke R2 adapter. The repo only claims the current shipped S3-style operations it can exercise through the existing adapter seam, with MinIO as the automated proof lane. |
| Google Cloud Storage | Rindle.Storage.GCS | [:head, :signed_url, :resumable_upload, :resumable_upload_session] | Live GCS proof exists in the GCS test lanes; adopters still own bucket and browser wiring | Rindle.Storage.GCS is the shipped adapter that honestly advertises the resumable capability family. See storage_gcs.md for runtime wiring, CORS, and session hygiene. |
Proof Boundaries
Rindle separates "documented contract" from "what the repo proves by default":
- MinIO is the default real-provider proof lane in CI for the shipped S3 adapter contract.
- Cloudflare R2 is documented as a compatibility target through the shipped
Rindle.Storage.S3seam. - Default CI proves the shipped S3-style contract against MinIO, not against every vendor-branded backend.
- Generic S3 providers are expected to match the shipped S3 adapter contract, but adopters should still validate vendor-specific behavior in their own environments before rollout.
That distinction matters: Phase 8 improves auditability, not marketing claims. This guide does not imply provider-specific live R2 proof in CI.
Adapter Honesty
Capability claims are adapter-specific, not marketing-wide:
Rindle.Storage.GCSadvertises:resumable_uploadand:resumable_upload_sessionRindle.Storage.S3advertises neither resumable capability todayRindle.Storage.Localadvertises neither resumable capability today- custom adapters may honestly advertise either, both, or neither depending on what they actually implement
Rindle does not silently downgrade resumable requests into presigned PUT.
Cloudflare R2 Boundary
Cloudflare R2 is documented here as an S3-compatible provider path through the
existing Rindle.Storage.S3 adapter. In v1.1, the supported Rindle contract is:
- Direct upload via presigned PUT.
- Metadata verification via
head/2. - Signed delivery URL generation when
:signed_urlis advertised. - S3-style multipart upload when
:multipart_uploadis advertised.
This guide does not claim:
- A bespoke
Rindle.Storage.R2adapter. - HTML form POST uploads as part of the shipped contract.
- Provider-specific live R2 coverage in CI.
- A shipped resumable-upload API through the S3 adapter.
Resumable Boundary
Resumable upload is shipped where the adapter advertises it. Today that means
Rindle.Storage.GCS.
That does not mean:
- every adapter supports resumable upload
- Rindle falls back automatically from resumable upload to presigned PUT
- S3-compatible providers inherit resumable semantics through
Rindle.Storage.S3 - Rindle ships tus or a provider-agnostic resumable abstraction beyond the honest capability contract