oxistore-blob-s3 — S3-compatible BlobStore backend (Pure Rust)
oxistore-blob-s3 implements the oxistore-blob::BlobStore trait against any S3-compatible endpoint — AWS S3, MinIO, Ceph RGW, DigitalOcean Spaces, and similar. Once constructed, an S3BlobStore is a drop-in BlobStore, so it inherits all the trait's default helpers (CAS, copy/rename, prefix delete, paginated metadata listing) for free.
The backend is Pure Rust and ring-free. Requests are signed with AWS Signature Version 4 using the aws-sigv4 crate's pure signing closure (no AWS C SDK, no ring, no aws-lc-rs on normal dependency edges — verified 2026-05-27). HTTP transport runs over oxihttp-client (a hyper-based client with connection pooling and Pure-Rust TLS), so a single client instance handles both http:// (MinIO / mocks) and https:// (real AWS) endpoints. XML responses are parsed with quick-xml. The crate forbids unsafe code.
Installation
[]
= "0.1.0"
= "0.1.0" # for the BlobStore trait + BlobError
This crate has no Cargo features; all functionality is always available.
Quick Start
use ;
use BlobStore;
use Bytes;
# async
Presigned URLs
Generated offline (no HTTP round-trip); anyone with the URL can perform the operation without AWS credentials.
# use S3BlobStore;
# use Duration;
#
Multipart upload
# use S3BlobStore;
# use Bytes;
# async
API Overview
S3BlobStore
S3-compatible blob store implementing BlobStore. Clone; implements Debug (credentials redacted). Created via S3BlobStoreBuilder.
| Method | Description |
|---|---|
S3BlobStore::new(config) |
Construct directly from a complete S3Config |
(trait) put / get / delete / head / list |
Standard BlobStore operations (ListObjectsV2 is auto-paginated) |
copy(src, dst) |
Server-side copy via x-amz-copy-source (no data through the client) |
presign_get(key, ttl) |
Generate a presigned GET URL valid for ttl |
presign_put(key, ttl, content_type) |
Generate a presigned PUT URL, optionally binding Content-Type |
create_multipart_upload(key) |
Initiate a multipart upload; returns S3MultipartUpload<'_> |
S3MultipartUpload<'a>
Handle for an in-progress multipart upload.
| Method | Description |
|---|---|
upload_part(part_number, body) |
Upload a part (number 1–10000); ETag recorded internally |
complete(self) |
Commit the upload (parts sorted ascending per AWS requirement) |
abort(self) |
Discard the upload, freeing server-side parts |
S3Credentials
AWS credentials for SigV4 signing. Clone; implements Debug with the secret key and session token redacted.
| Field / Method | Description |
|---|---|
access_key_id: String |
AWS Access Key ID |
secret_access_key: String |
AWS Secret Access Key |
session_token: Option<String> |
Optional STS session token |
S3Credentials::new(id, secret, session_token) |
Construct from explicit values |
S3Credentials::from_env() |
Load from AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
S3Credentials::from_env_with(getter) |
Load via a custom env getter (for testing) |
S3Config
Full configuration (Debug, Clone).
| Field | Description |
|---|---|
endpoint: String |
Endpoint URL (e.g. https://s3.us-east-1.amazonaws.com or http://localhost:9000) |
region: String |
AWS region |
bucket: String |
Bucket name |
credentials: S3Credentials |
Signing credentials |
path_style: bool |
true = endpoint/bucket/key; false = bucket.host/key |
timeout_secs: u64 |
Per-operation timeout in seconds |
retry_config: RetryConfig |
Retry / backoff configuration |
S3BlobStoreBuilder
Default. Builder for S3Config and the resulting store.
| Method | Description |
|---|---|
S3BlobStoreBuilder::new() |
Defaults: path-style, 30 s timeout, default retry |
endpoint(e) / region(r) / bucket(b) |
Set required fields |
credentials(c) |
Set the credentials |
path_style(bool) |
Toggle path-style URLs (default true) |
timeout_secs(secs) |
Per-operation timeout (default 30) |
retry_config(cfg) |
Override retry/backoff |
build() |
Construct the S3BlobStore (errors if endpoint/region/bucket/credentials are unset) |
RetryConfig (retry module)
Debug, Clone, PartialEq, Eq, Default (3 attempts, 100 ms base, 5000 ms cap). Truncated binary exponential backoff with deterministic per-attempt variance; HTTP 429/503 and transport errors are retried.
| Field / Function | Description |
|---|---|
max_attempts: u32 |
Total attempts (1 = no retry) |
base_delay_ms: u64 |
Base delay before the first retry |
max_delay_ms: u64 |
Delay cap |
retry::backoff_delay(config, attempt) |
Compute the Duration for a 0-indexed retry attempt |
retry::should_retry_status(status) |
true for 429 / 503 |
Error handling (error module)
| Item | Description |
|---|---|
S3ErrorResponse |
Parsed S3 XML error (code, message); S3ErrorResponse::parse(xml) -> Option<Self> |
http_error_to_blob_error(status, body, key) |
Map an HTTP status + body to a BlobError (404 NoSuchKey/NoSuchBucket → NotFound) |
Errors are surfaced as oxistore_blob::BlobError; transport/HTTP failures that exhaust retries become BlobError::RetryExhausted.
SigV4 signing (sigv4 module)
| Function | Description |
|---|---|
sigv4::sign_request(method, uri, headers, body, credentials, region) |
Sign a request; returns the extra headers to inject (x-amz-date, authorization, and optionally x-amz-security-token) |
Feature Flags
This crate defines no Cargo features.
Cross-references
oxistore-blob— theBlobStoretrait,BlobMeta,Digest, andBlobErrorthis crate builds on.oxistore-blob-gcs/oxistore-blob-azure— sibling cloud backends for Google Cloud Storage and Azure Blob Storage.oxistore-core/oxistore— the trait crate and top-level facade.
License
Apache-2.0 — COOLJAPAN OU (Team Kitasan)