Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

S3 data-plane compatibility

Maskura’s zero-config Docker image is a single-node S3-compatible endpoint intended as a local MinIO replacement. This page lists the S3 data-plane operations it implements and points at the test that covers each one.

It intentionally covers data-plane verbs only. Bucket subresources — versioning, ACLs, policies, tagging, lifecycle, notifications, object lock, and the rest — are out of scope and handled fail-closed; see Limits and ADR 0019.

Every Supported and Local appliance only row links to a test in this repository, and the How to run the evidence section lists the exact commands. Treat this table as derived output: if the dispatch in crates/gateway/src/server.rs or a cited test changes, update this page in the same change.

Status legend

StatusMeaning
SupportedImplemented on the local appliance.
Local appliance onlyImplemented for the durable local FileStore; on a configured workspace or backend the request returns 403 AccessDenied.
Not implementedRecognized and rejected before dispatch with 501 NotImplemented, without touching state.
Not routedNo route exists for the request; it does not reach a handler.

Operations

OperationRequestStatusEvidenceNotes
ListBucketsGET /Supporteds3_frontdoor_test::list_buckets_at_rootBrowsers get the dashboard on the same path; S3 clients get the XML.
CreateBucketPUT /{bucket}Local appliance onlys3_frontdoor_test::filestore_conformance_bucket_object_lifecycle_survives_restart, s3_frontdoor_test::create_bucket_is_rejectedExisting bucket → BucketAlreadyExists; a configured backend → 403 AccessDenied.
DeleteBucketDELETE /{bucket}Local appliance onlys3_frontdoor_test::filestore_conformance_bucket_object_lifecycle_survives_restart, file_store::delete_and_bucket_lifecycle_require_empty_bucketEmpty bucket → 204; non-empty → 409 BucketNotEmpty; missing → NoSuchBucket.
HeadBucketHEAD /{bucket}Local appliance onlys3_frontdoor_test::filestore_list_and_head_on_missing_bucket_return_no_such_bucketServed by the GET /{bucket} route (body stripped). Missing bucket → 404 NoSuchBucket, existing → 200.
ListObjects (v1)GET /{bucket}Supporteds3_frontdoor_test::list_objects_returns_keys_and_prefixes, s3_frontdoor_test::filestore_list_and_head_on_missing_bucket_return_no_such_bucketPrefix, delimiter, and pagination. Missing bucket → NoSuchBucket.
ListObjectsV2GET /{bucket}?list-type=2Supporteds3_frontdoor_test::memory_list_continuations_are_opaque_bound_and_tamper_resistant, s3_frontdoor_test::filestore_list_and_head_on_missing_bucket_return_no_such_bucketOpaque, bound continuation tokens; continuation-token requires list-type=2. Missing bucket → NoSuchBucket.
PutObjectPUT /{bucket}/{key}Supporteds3_frontdoor_test::put_get_roundtrip_filters_pii, s3_frontdoor_test::unmodified_rust_sdk_default_put_is_acceptedWith no plugins configured, bytes are stored unchanged.
GetObjectGET /{bucket}/{key}Supporteds3_frontdoor_test::streaming_memory_get_preserves_range_and_head_metadata, s3_frontdoor_test::conditional_get_and_head_preserve_object_identity_without_a_bodySupports Range and conditional request headers.
HeadObjectHEAD /{bucket}/{key}Supporteds3_frontdoor_test::head_and_delete_remain_availableMetadata only; no body.
DeleteObjectDELETE /{bucket}/{key}Supporteds3_frontdoor_test::head_and_delete_remain_availableMissing key → 404 NoSuchKey.
CreateMultipartUploadPOST /{bucket}/{key}?uploadsSupporteds3_multipart_conformance::sdk_multipart_lifecycle_completes_over_real_tcpStaged and durable; requires the local staged multipart store (enabled on the appliance).
UploadPartPUT /{bucket}/{key}?partNumber&uploadIdSupporteds3_multipart_conformance::sdk_multipart_lifecycle_completes_over_real_tcp, local_filesystem_multipart::local_filesystem_multipart_lifecycle_over_httpParts persist across restart.
CompleteMultipartUploadPOST /{bucket}/{key}?uploadIdSupporteds3_multipart_conformance::http_conformance_completion_errors_pagination_helpers_and_replayCompletion replaces any existing single-PUT object atomically.
AbortMultipartUploadDELETE /{bucket}/{key}?uploadIdSupporteds3_multipart_conformance::sdk_multipart_abort_list_uploads_and_no_such_uploadUnknown upload → NoSuchUpload.
ListPartsGET /{bucket}/{key}?uploadIdSupportedlocal_filesystem_multipart::local_filesystem_multipart_lifecycle_over_httpFull part listing with pagination fields.
ListMultipartUploadsGET /{bucket}?uploadsSupporteds3_multipart_conformance::sdk_multipart_abort_list_uploads_and_no_such_uploadIn-progress uploads for the bucket.
CopyObjectPUT /{bucket}/{key} + x-amz-copy-sourceNot implementeds3_protocol::s3_not_implemented_returns_501, crates/gateway/src/server.rs (x-amz-copy-source pre-dispatch gate)Rejected before the body is polled.
UploadPartCopyPUT ?partNumber&uploadId + x-amz-copy-sourceNot implementedcrates/gateway/src/server.rs (s3_upload_part copy-source check)Rejected before any part is stored.
DeleteObjects (batch)POST /{bucket}?deleteNot routedcrates/gateway/src/server.rs (no POST /{bucket} route; delete is a gated subresource)Returns 405 Method Not Allowed.
SelectObjectContentPOST /{bucket}/{key}?selectNot implementeds3_protocol::s3_not_implemented_returns_501, crates/gateway/src/server.rs (select subresource gate)Rejected before dispatch.

How to run the evidence

The Rust suites that back the table:

cargo test -p maskura-gateway --test s3_frontdoor_test
cargo test -p maskura-gateway --test s3_multipart_conformance
cargo test -p maskura-gateway --test local_filesystem_multipart
cargo test -p maskura-gateway --test s3_protocol
cargo test -p maskura-gateway file_store::

Unmodified AWS CLI and boto3 interoperability (requires aws and boto3 on PATH):

just interop

The published container is proven black-box (including the AWS CLI round trip) by just proof; see Run the claims.

Limits

The appliance is a single-node, byte-preserving local store, not a claim of full AWS S3 or historical MinIO feature parity. With no plugins configured (the default), uploaded bytes are stored and returned unchanged, including arbitrary binary objects. The Wasm transform pipeline (PII redaction, encryption) remains opt-in and applies to record-oriented formats (text, JSON, JSONL, CSV, TSV).

Bucket subresources that are not data-plane verbs — versioning, ACLs, bucket and object tagging, policies, lifecycle, encryption config, website, CORS, replication, object-lock, legal-hold, notifications, logging, request-payment — are recognized and rejected with 501 NotImplemented before method/path dispatch, so they can never be misrouted into a destructive operation. See ADR 0019.