The Rust client
tessaridb-s3-client: an async SDK built for moving files — streamed from and to disk, large ones in resumable parts, checksums end to end, presigned links.
cargo add tessaridb-s3-client
tessaridb-s3-client is published on crates.io under Apache-2.0. It speaks the
S3 API with AWS Signature Version 4, so it works with TessariDB S3 and with any other
S3-compatible store, and it depends on the protocol, never on the server's code.
use tessaridb_s3_client::sign::Credentials;
use tessaridb_s3_client::{BucketName, ClientBuilder, ListObjects, ObjectKey};
let client = ClientBuilder::new("https://s3.example.com", Credentials::new("ACCESS_KEY", "SECRET_KEY"))
.region("us-east-1")
.build()?;
let bucket = BucketName::new("photos")?;
let key = ObjectKey::new("2026/cat.jpg")?;
client.put_object(&bucket, &key, b"...".to_vec(), Some("image/jpeg")).await?;
let page = client.list_objects(&bucket, &ListObjects::default()).await?;
What it does for you
- Names are checked when they are made.
BucketName::newandObjectKey::newrefuse what S3 would refuse, and a key with a.or..path segment, which S3 accepts but an HTTP URL silently collapses. - Files stream.
upload_filereads the file once to hash it, then sends it in 1 MiB chunks signed with that hash.download_filewrites into a hidden.partfile and renames it into place only when the whole object arrived. - Large files go in parts. Over 16 MiB, a file goes up in parts of 8 MiB or more,
four at a time. If parts keep failing on a transient error,
upload_filereturnsUploadInterruptedwith the upload ID;resume_uploadsends only the parts the server does not already hold intact, andabort_uploaddiscards it. - Downloads continue where they stopped, with
RangeandIf-Matchon the ETag, so a changed object is refused rather than spliced. - Integrity end to end. Uploads declare their CRC64NVME; downloads are checked
against every whole-object checksum the server keeps, retried on a mismatch, and
ChecksumMismatchis reported if it keeps failing. - Presigned links:
presign_getandpresign_put, from 1 second to 7 days. APUTlink does not sign the body, so share it only with whoever should write that key.
Errors and transport
- One
Errortype with a category to branch on (NotFound,Conflict,Unauthorized,Unavailableand the rest), the S3 code, status and request ID kept. Branch on the category, never on the message. - Transient failures (a timeout, a lost connection,
SlowDown, a 5xx) are retried up to three times with a randomised backoff, then reported asRetriesExhausted. - TLS uses the platform's trust store, with no switch to skip certificate checks. A
plain
http://endpoint is accepted for a server on the same machine or a private network; the secret key is never sent, but object data travels unencrypted. - Buckets are addressed path-style. Redirects are not followed: a signed request sent to another host would be a different request.