multistore
Runtime-agnostic core library for the S3 proxy gateway. This crate contains all business logic — S3 request parsing, SigV4 signing/verification, authorization, configuration retrieval, and the proxy handler — without depending on any async runtime.
Why This Crate Exists Separately
The proxy needs to run on fundamentally different runtimes: Tokio/Hyper in containers and Cloudflare Workers on the edge. These runtimes have incompatible stream types, HTTP primitives, and threading models (multi-threaded vs single-threaded WASM). By keeping the core free of runtime dependencies, it compiles cleanly to both x86_64-unknown-linux-gnu and wasm32-unknown-unknown.
Key Abstractions
The core defines three trait boundaries that runtime crates implement:
ProxyBackend — Provides three capabilities: create_paginated_store() returns a PaginatedListStore for LIST, create_signer() returns a Signer for presigned URL generation (GET/HEAD/PUT/DELETE), and send_raw() sends signed HTTP requests for multipart operations. Both runtimes delegate to build_signer() which uses object_store's built-in signer for authenticated backends and UnsignedUrlSigner for anonymous backends (avoiding Instant::now() which panics on WASM). For create_paginated_store(), the server runtime uses default connectors + reqwest; the worker runtime uses a custom FetchConnector.
BucketRegistry — Identity-aware bucket resolution and listing. Given a bucket name, identity, and S3 operation, get_bucket() returns a ResolvedBucket (config + optional list rewrite) or an authorization error. list_buckets() returns the buckets visible to a given identity. See multistore-static-config for a file-based implementation.
CredentialRegistry — Credential and role lookup for authentication infrastructure. Provides get_credential() for SigV4 verification and get_role() for STS role assumption. See multistore-static-config for a file-based implementation.
Any provider implementing these traits can be wrapped with CachedProvider for in-memory TTL caching of credential/role lookups (bucket resolution is always delegated directly since it involves authorization).
The core also defines the Middleware trait for composable post-auth request processing. Middleware runs after identity resolution and authorization, wrapping the backend dispatch call. Each middleware receives a DispatchContext and a Next handle to continue the chain. The oidc-provider crate provides AwsBackendAuth as a middleware that resolves backend credentials via OIDC token exchange.
Module Overview
src/
├── api/
│ ├── request.rs Parse incoming HTTP → S3Operation enum
│ ├── response.rs Serialize S3 XML responses
│ └── list_rewrite.rs Rewrite <Key>/<Prefix> values in list response XML
├── auth/
│ ├── mod.rs Authorization (scope-based access control)
│ ├── identity.rs SigV4 verification, identity resolution
│ └── tests.rs Auth test helpers
├── backend/
│ └── mod.rs ProxyBackend trait, Signer/StoreBuilder, S3RequestSigner (multipart)
├── registry/
│ ├── mod.rs Re-exports
│ ├── bucket.rs BucketRegistry trait, ResolvedBucket, DEFAULT_BUCKET_OWNER
│ └── credential.rs CredentialRegistry trait
├── error.rs ProxyError with S3-compatible error codes
├── middleware.rs Middleware trait, DispatchContext, Next
├── proxy.rs ProxyGateway — the main request handler
├── route_handler.rs RouteHandler trait, ProxyResponseBody
└── types.rs BucketConfig, RoleConfig, StoredCredential, etc.
Usage
This crate is not used directly. Runtime crates depend on it and provide concrete ProxyBackend implementations. If you're building a custom runtime integration, depend on this crate and implement ProxyBackend, BucketRegistry, and/or CredentialRegistry.
Standard usage
use ProxyGateway;
use StaticProvider;
let backend = new;
let config = from_file?;
let gateway = new;
// Optional: enable session token verification for STS temporary credentials.
// let gateway = gateway.with_credential_resolver(token_key);
// Optional: register route handlers for STS, OIDC discovery, etc.
// let gateway = gateway.with_route_handler(sts_handler);
// Optional: register middleware (e.g., OIDC-based backend credential resolution).
// let gateway = gateway.with_middleware(oidc_auth);
// In your HTTP handler, use handle_request for a two-variant match:
let req_info = RequestInfo ;
match gateway.handle_request.await
Custom BucketRegistry
For non-standard bucket resolution, authorization, or multi-tenant routing, implement BucketRegistry directly:
use ;
use ProxyError;
Temporary Credential Resolution
The core defines a TemporaryCredentialResolver trait for resolving session tokens (from x-amz-security-token) into TemporaryCredentials. The core proxy calls this during identity resolution without knowing the token format.
The multistore-sts crate provides TokenKey, a sealed-token implementation using AES-256-GCM. Register it via ProxyGateway::with_credential_resolver(). See the sealed tokens documentation for details.
Feature Flags
All optional — the default build has zero network dependencies:
azure— enables Azure Blob Storage backend supportgcp— enables Google Cloud Storage backend support