Skip to main content
Modules sometimes need to put bytes somewhere: an uploaded document, a rendered invoice PDF, a media asset. Voyant handles that with the @voyant-travel/storage package, a small provider abstraction that gives portable code one contract and lets the deployment choose the backend. This page covers the platform storage provider interface and its built-in backends. It is the in-process abstraction you wire into a self-hosted deployment. It is distinct from the managed Cloud storage service, which Voyant runs for you. Cross-link below.

One provider contract

Everything in the package targets a single interface, StorageProvider, from @voyant-travel/storage/types:
The contract is deliberately narrow: upload bytes, delete by key, mint a time-limited URL, fetch bytes back. StorageUploadBody accepts an ArrayBuffer, a Uint8Array, or a Blob. upload returns a StorageObject carrying the object key and a public url (an empty string when the object is private and only reachable through signedUrl). get returns null when the key is absent. Portable module code targets this interface. The backend is a deployment choice, not a fork of the module.

The service wrapper

Most deployments use exactly one storage backend, so createStorageService(provider) from @voyant-travel/storage/service wraps a single provider as a named StorageService:
The service exposes the same upload / delete / signedUrl / get surface as the provider, plus a provider reference. It is a thin convenience, not an orchestration layer.

Built-in backends

The package ships three providers. Each is created by a factory and satisfies the same StorageProvider contract.

Local

createLocalStorageProvider() keeps objects in an in-memory Map held in a closure. It is the right backend for unit tests and for running workflows locally without touching remote storage. Data does not survive the process.

R2

createS3CompatibleStorageProvider({ bucket }) binds to a Cloudflare R2 bucket binding (for example env.MEDIA_BUCKET). The binding handles authentication at the Worker runtime boundary, so no credentials live in this layer. R2 bindings do not mint signed URLs by themselves. The R2 provider adds a publicUrl(key) method for permanent public URLs (requires publicBaseUrl, a public custom domain or a Worker route that proxies the bucket). For time-limited access, configure a signer.
Calling signedUrl on the R2 provider without a configured signer throws. This is intentional: falling back to ${publicBaseUrl}${key} would return a permanent, unauthenticated URL while the caller believes it expires after expiresIn seconds. Use publicUrl(key) when a permanent URL is what you actually want.

S3 and S3-compatible

createS3CompatibleStorageProvider({ region, bucket, accessKeyId, secretAccessKey }) signs every request with AWS SigV4 using Web Crypto, so it needs no AWS SDK. It accepts an optional sessionToken for temporary credentials. For S3-compatible services (MinIO, Backblaze B2, DigitalOcean Spaces, Wasabi, or R2’s S3 API), set a custom endpoint. forcePathStyle defaults to true for the widest compatibility; set it to false for virtual-hosted-style URLs. signedUrl presigns a GET URL with SigV4. The SigV4 signing primitives, signRequest and presignUrl, are also exported directly from @voyant-travel/storage/lib/sigv4 for advanced use. They are verified against the AWS canonical test vectors.

Package exports

Framework storage versus Cloud storage

This package is the in-process abstraction you assemble into a deployment yourself: you pick a provider, supply the bucket or credentials, and own the operational surface. Cloud storage is the managed alternative. Voyant runs the backend, provisioning, and access for you, so you do not wire up an R2 binding or carry S3 credentials. Reach for the platform package when you self-host and want to choose and control the backend. Reach for Cloud storage when you want it managed.

Next steps

Cloud storage

The managed storage service, the counterpart to this in-process abstraction.

Configuration

How a deployment chooses and wires its storage backend.

Services

Where module code calls into a storage provider.

Glossary

The shared vocabulary behind the storage model.