Skip to content

Commit 33a2bcb

Browse files
committed
Reconcile S3 compatibility matrix
1 parent 5f79d4f commit 33a2bcb

6 files changed

Lines changed: 30 additions & 7 deletions

File tree

docs/s3-compatibility-matrix.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This document compares feature-level S3 coverage across:
99
- `MinIO`: whether MinIO documents support for the equivalent S3 feature/API.
1010
- `s3ite`: whether this repository currently implements and tests the feature.
1111

12-
The matrix was last reconciled on 2026-06-08. It uses colored status markers in
12+
The matrix was last reconciled on 2026-06-12. It uses colored status markers in
1313
the feature columns:
1414

1515
- ✅ means covered/supported.
@@ -38,9 +38,9 @@ compatibility-limited, the note says so.
3838
[Amazon S3 Multi-Region Access Points](https://docs.aws.amazon.com/en_en/AmazonS3/latest/userguide/MultiRegionAccessPoints.html),
3939
[Amazon S3 Object Lambda availability change](https://docs.aws.amazon.com/AmazonS3/latest/userguide/amazons3-ol-change.html),
4040
and [S3 Express One Zone](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-express-getting-started.html).
41-
- MinIO surface: [MinIO S3 API Compatibility](https://minio.community/community/minio-object-store/reference/s3-api-compatibility.html),
41+
- MinIO surface: [MinIO S3 API Compatibility](https://docs.min.io/aistor/developers/s3-api-compatibility/),
4242
[MinIO server-side encryption](https://docs.min.io/aistor/installation/linux/server-side-encryption/),
43-
[MinIO Object Lambda transforms](https://docs.min.io/enterprise/aistor-object-store/developers/transforms-with-object-lambda/),
43+
[MinIO Object Lambda transforms](https://docs.min.io/aistor/developers/transforms-with-object-lambda/),
4444
and [MinIO S3 compatibility product overview](https://www.min.io/product/s3-compatibility).
4545
- s3ite surface: [README.md](../README.md), [src/s3](../src/s3),
4646
[src/s3/sse.rs](../src/s3/sse.rs), and [tests](../tests).
@@ -118,7 +118,7 @@ compatibility-limited, the note says so.
118118
| Multipart | UploadPart ||||| Includes size and part-number validation. |
119119
| Multipart | CompleteMultipartUpload ||||| Includes ordering, ETag, and missing/incorrect part validation. |
120120
| Multipart | AbortMultipartUpload ||||| Includes missing upload behavior. |
121-
| Multipart | ListMultipartUploads ||||| s3ite supports prefix, delimiter, pagination, and owner fields. |
121+
| Multipart | ListMultipartUploads ||||| s3ite supports prefix, delimiter, pagination, and owner fields. MinIO documents an exact-prefix limitation. |
122122
| Multipart | ListParts ||||| s3ite supports pagination and ETags. |
123123
| Multipart | UploadPartCopy ||||| Includes ranges, conditions, special names, and versions. |
124124
| Multipart | Multipart idempotent complete retry ||||| s3ite records completion results for retries. |
@@ -165,7 +165,7 @@ compatibility-limited, the note says so.
165165
| Lifecycle | Storage-class transitions ||||| Out of scope for s3ite. |
166166
| Lifecycle | RestoreObject from archive/tier ||||| MinIO lists `RestoreObject`; s3ite has no restore/tiering. |
167167
| Lifecycle | Restore status in list outputs ||||| Out of scope for s3ite. |
168-
| CORS | Bucket CORS API ||| || s3ite stores per-bucket CORS rules in each bucket DB; MinIO documents bucket CORS APIs as unsupported because CORS is enabled by default. |
168+
| CORS | Bucket CORS API ||| || s3ite stores per-bucket CORS rules in each bucket DB; current MinIO docs list `PutBucketCors` and `DeleteBucketCors`. |
169169
| CORS | CORS preflight behavior ||||| s3ite applies bucket CORS first and falls back to configurable server-level CORS when no bucket config exists. |
170170
| POST | POST Object form uploads ||||| Browser form uploads reuse the normal object write path. |
171171
| POST | POST Object policy validation ||||| SigV4 POST policy validation is handled by `s3s`. |
@@ -184,7 +184,7 @@ compatibility-limited, the note says so.
184184
| Logging | S3 server access logging ||||| MinIO documents bucket logging APIs as unsupported; s3ite has application logs only. |
185185
| Website | Static website bucket configuration ||||| MinIO documents website APIs as unsupported. |
186186
| Inventory | Bucket inventory configuration ||||| MinIO documents inventory APIs as unsupported. |
187-
| Analytics | Bucket analytics configuration ||||| MinIO documents analytics APIs as unsupported. |
187+
| Analytics | Bucket analytics configuration ||||| MinIO documents some analytics operations but still lists `GetBucketAnalyticsConfiguration` as unsupported, so full configuration parity is not present. |
188188
| Metrics | Bucket metrics configuration ||||| MinIO documents metrics APIs as unsupported. |
189189
| Accelerate | Bucket accelerate configuration ||||| MinIO documents accelerate APIs as unsupported. |
190190
| Storage class | STANDARD storage-class reporting ||||| s3ite reports `STANDARD` in list/object-attribute shapes. |

src/s3/get.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ fn response_checksum(
234234
}
235235
}
236236

237+
/// Formats lifecycle expiration metadata for object read response headers.
237238
fn lifecycle_expiration_response_header(
238239
expiration: Option<LifecycleExpirationHeader>,
239240
) -> S3Result<Option<String>> {
@@ -242,6 +243,7 @@ fn lifecycle_expiration_response_header(
242243
.transpose()
243244
}
244245

246+
/// Loads the current or exact-version body reference for a `GetObject` read.
245247
fn load_object_body_reference(
246248
transaction: &rusqlite::Transaction<'_>,
247249
key: &str,
@@ -256,6 +258,7 @@ fn load_object_body_reference(
256258
})
257259
}
258260

261+
/// Resolves range, part, and lifecycle metadata for a found object body.
259262
fn load_object_body_read_found(
260263
transaction: &rusqlite::Transaction<'_>,
261264
key: &str,
@@ -292,6 +295,7 @@ fn load_object_body_read_found(
292295
})
293296
}
294297

298+
/// Decrypts encrypted object chunks when a body read needs plaintext slicing.
295299
fn decrypt_object_body_if_needed(
296300
transaction: &rusqlite::Transaction<'_>,
297301
object: &ObjectBodyReference,

src/s3/multipart.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ async fn upload_part_copy_cross_bucket(
701701
.map_err(s3_error_from_sqlite)
702702
}
703703

704+
/// Maps one storage multipart upload summary into the S3 listing DTO.
704705
fn multipart_upload_from_summary(
705706
summary: crate::sqlite::MultipartUploadSummary,
706707
encode_keys: bool,
@@ -719,6 +720,7 @@ fn multipart_upload_from_summary(
719720
}
720721
}
721722

723+
/// Splits storage multipart listing entries into upload and common-prefix DTOs.
722724
fn multipart_upload_listing_entries(
723725
entries: Vec<MultipartUploadListEntry>,
724726
encode_keys: bool,
@@ -837,7 +839,7 @@ pub(super) async fn list_multipart_uploads(
837839
Ok(S3Response::new(output))
838840
}
839841

840-
/// Creates multipart upload state for a future object.
842+
/// Validates that create-multipart ACL inputs are owner-equivalent no-ops.
841843
fn validate_create_multipart_upload_acl(input: &CreateMultipartUploadInput) -> S3Result<()> {
842844
validate_object_acl(&ObjectAclRequest {
843845
acl: input.acl.as_ref(),
@@ -854,6 +856,7 @@ fn validate_create_multipart_upload_acl(input: &CreateMultipartUploadInput) -> S
854856
})
855857
}
856858

859+
/// Creates multipart upload state for a future object.
857860
pub(super) async fn create_multipart_upload(
858861
sqlite: &Sqlite,
859862
req: S3Request<CreateMultipartUploadInput>,
@@ -960,6 +963,7 @@ pub(super) async fn create_multipart_upload(
960963
Ok(S3Response::new(output))
961964
}
962965

966+
/// Reads, encrypts, and stores one multipart upload part as encoded chunks.
963967
async fn upload_encrypted_part(
964968
sqlite: &Sqlite,
965969
context: UploadPartWriteContext,
@@ -1032,6 +1036,7 @@ async fn upload_encrypted_part(
10321036
)))
10331037
}
10341038

1039+
/// Reads and stores one plain multipart upload part.
10351040
async fn upload_plain_part(
10361041
sqlite: &Sqlite,
10371042
context: UploadPartWriteContext,
@@ -1099,6 +1104,7 @@ async fn upload_plain_part(
10991104
)))
11001105
}
11011106

1107+
/// Loads active multipart upload state before an `UploadPart` body is consumed.
11021108
async fn load_upload_part_state(
11031109
sqlite: &Sqlite,
11041110
bucket: &str,
@@ -1524,6 +1530,7 @@ async fn precompute_encrypted_full_object_checksum(
15241530
.map_err(s3_error_from_sqlite)
15251531
}
15261532

1533+
/// Normalizes and validates the S3 complete-multipart request shape.
15271534
fn normalize_multipart_completion_request(
15281535
input: CompleteMultipartUploadInput,
15291536
access_key: Option<String>,
@@ -1577,6 +1584,7 @@ fn normalize_multipart_completion_request(
15771584
})
15781585
}
15791586

1587+
/// Precomputes completion checksums that require reading encrypted part bodies.
15801588
async fn precompute_multipart_completion_checksum(
15811589
sqlite: &Sqlite,
15821590
request: &MultipartCompletionRequest,
@@ -1596,6 +1604,7 @@ async fn precompute_multipart_completion_checksum(
15961604
.await
15971605
}
15981606

1607+
/// Prepares multipart completion against a read snapshot before the write.
15991608
async fn prepare_multipart_completion(
16001609
sqlite: &Sqlite,
16011610
request: MultipartCompletionRequest,
@@ -1614,6 +1623,7 @@ async fn prepare_multipart_completion(
16141623
.map_err(s3_error_from_sqlite)
16151624
}
16161625

1626+
/// Writes a prepared multipart completion or returns an idempotent retry result.
16171627
async fn write_multipart_completion(
16181628
sqlite: &Sqlite,
16191629
bucket: &str,
@@ -1637,6 +1647,7 @@ async fn write_multipart_completion(
16371647
}
16381648
}
16391649

1650+
/// Builds the S3 completion response from stored completion metadata.
16401651
fn complete_multipart_upload_output(
16411652
bucket: String,
16421653
key: String,

src/s3/put.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ fn validate_put_object_acl_input(input: &PutObjectInput) -> S3Result<()> {
125125
})
126126
}
127127

128+
/// Returns whether the request explicitly asks for SSE-S3 encryption.
128129
fn requested_put_object_sse_s3(input: &PutObjectInput) -> S3Result<bool> {
129130
requested_sse_s3(
130131
input.server_side_encryption.as_ref(),
@@ -158,6 +159,7 @@ fn put_object_output(
158159
}
159160
}
160161

162+
/// Reads, encrypts, and writes a `PutObject` body as encoded encrypted chunks.
161163
async fn put_encrypted_object(
162164
sqlite: &Sqlite,
163165
body: StreamingBlob,
@@ -238,6 +240,7 @@ async fn put_encrypted_object(
238240
))
239241
}
240242

243+
/// Reads and writes a plain buffered `PutObject` body.
241244
async fn put_plain_object(
242245
sqlite: &Sqlite,
243246
body: StreamingBlob,
@@ -312,6 +315,7 @@ async fn put_plain_object(
312315
))
313316
}
314317

318+
/// Dispatches a normalized object write to the encrypted or plain storage path.
315319
async fn write_put_object(
316320
sqlite: &Sqlite,
317321
body: StreamingBlob,

src/sqlite/list.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const OBJECT_ENTRY_KIND: i64 = 0;
1111
const COMMON_PREFIX_ENTRY_KIND: i64 = 1;
1212

1313
impl ObjectVersionListEntry {
14+
/// Returns the key/version pair used as the next listing marker.
1415
fn marker(&self) -> (&str, &str) {
1516
match self {
1617
Self::Object(summary) => (&summary.key, &summary.version_id),
@@ -89,6 +90,7 @@ fn object_version_marker_state(
8990
Ok((marker_kind, after_sequence))
9091
}
9192

93+
/// Decodes one emitted object-version listing row from the storage query.
9294
fn object_version_entry_from_row(
9395
row: &rusqlite::Row<'_>,
9496
) -> rusqlite::Result<ObjectVersionListEntry> {

src/sqlite/multipart.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl<'a> From<&'a MultipartMetadata> for CompletionPartMetadata<'a> {
5454
}
5555

5656
impl MultipartUploadListEntry {
57+
/// Returns the key/upload pair used as the next multipart listing marker.
5758
fn marker(&self) -> (&str, Option<Uuid>) {
5859
match self {
5960
Self::Upload(summary) => (&summary.key, Some(summary.upload_id)),
@@ -101,6 +102,7 @@ fn multipart_upload_list_page(
101102
}
102103
}
103104

105+
/// Decodes one emitted multipart-upload listing row from the storage query.
104106
fn multipart_upload_entry_from_row(
105107
row: &rusqlite::Row<'_>,
106108
) -> rusqlite::Result<MultipartUploadListEntry> {

0 commit comments

Comments
 (0)