Object storage
@cleverbrush/storage defines a provider-neutral Node.js contract. @cleverbrush/storage-s3 implements it for self-hosted and hosted S3-compatible services. Change the endpoint, region, bucket and credentials to choose a provider.
Configure an adapter
import { S3Storage } from '@cleverbrush/storage-s3';
await using storage = new S3Storage({
endpoint: process.env.STORAGE_ENDPOINT!,
region: process.env.STORAGE_REGION!,
bucket: process.env.STORAGE_BUCKET!,
credentials: {
accessKeyId: process.env.STORAGE_ACCESS_KEY_ID!,
secretAccessKey: process.env.STORAGE_SECRET_ACCESS_KEY!
},
forcePathStyle: true,
keyPrefix: 'assets',
publicBaseUrl: 'https://assets.example.com'
});
await storage.put('images/logo.png', imageBytes, {
contentType: 'image/png'
});
const url = storage.publicUrl('images/logo.png');
// Leaving this scope automatically awaits storage.close().The public base URL is configured independently of the API endpoint. It maps keys to stable public bucket or proxy URLs; access policies remain deployment configuration. The key prefix is appended once. Hetzner can use its regional endpoint with virtual-hosted addressing by setting forcePathStyle: false.
Streaming and lifecycle
Use put, get, stat, copy and delete with an AbortSignal. Writes accept buffers or binary Node streams and use bounded multipart uploads. Consume or destroy every returned read stream. Use await using for storage owned by the current scope: normal exit and exceptions both await active work and multipart cleanup before releasing connections. Explicit close() is also supported and is idempotent. Keep application-wide instances alive until shutdown; handlers borrowing injected storage must not dispose them.
Metadata survives writes and copies. Errors have portable codes and omit raw provider requests and credentials. Copy stays within the configured bucket. ETags are opaque values, not guaranteed content checksums.
Garage is covered by the CI contract suite. Other endpoints, including Hetzner, can run the same suite against a designated test bucket. Production provider selection is independent of the Garage test fixture.
Storage contract and DI · S3 configuration, uploads and streaming examples