API Reference¶
Arca implements 19 S3 operations in its MVP. All operations follow the standard AWS S3 REST API specification.
Authentication¶
All requests must be signed with AWS Signature V4. Arca supports both the Authorization header and query string authentication methods.
Bucket Operations¶
| Operation | Method | Path | Description |
|---|---|---|---|
| ListBuckets | GET |
/ |
List all buckets |
| CreateBucket | PUT |
/{bucket} |
Create a new bucket |
| HeadBucket | HEAD |
/{bucket} |
Check if a bucket exists |
| DeleteBucket | DELETE |
/{bucket} |
Delete an empty bucket |
| GetBucketLocation | GET |
/{bucket}?location |
Get bucket region |
Object Operations¶
| Operation | Method | Path | Query Params | Description |
|---|---|---|---|---|
| PutObject | PUT |
/{bucket}/{key+} |
— | Upload an object |
| GetObject | GET |
/{bucket}/{key+} |
— | Download an object |
| HeadObject | HEAD |
/{bucket}/{key+} |
— | Get object metadata |
| DeleteObject | DELETE |
/{bucket}/{key+} |
— | Delete an object |
| DeleteObjects | POST |
/{bucket} |
delete |
Delete multiple objects in one request |
| CopyObject | PUT |
/{bucket}/{key+} |
— | Copy an object (uses x-amz-copy-source header) |
Listing Operations¶
| Operation | Method | Path | Query Params | Description |
|---|---|---|---|---|
| ListObjectsV1 | GET |
/{bucket} |
— | List objects in a bucket (legacy) |
| ListObjectsV2 | GET |
/{bucket} |
list-type=2 |
List objects in a bucket |
Multipart Upload Operations¶
| Operation | Method | Path | Query Params | Description |
|---|---|---|---|---|
| CreateMultipartUpload | POST |
/{bucket}/{key+} |
uploads |
Initiate a multipart upload |
| UploadPart | PUT |
/{bucket}/{key+} |
partNumber, uploadId |
Upload a part |
| CompleteMultipartUpload | POST |
/{bucket}/{key+} |
uploadId |
Complete a multipart upload |
| AbortMultipartUpload | DELETE |
/{bucket}/{key+} |
uploadId |
Abort a multipart upload |
Operation Details¶
ListBuckets¶
List all buckets owned by the authenticated user.
Returns ListAllMyBucketsResult XML with bucket names and creation dates.
CreateBucket¶
Create a new bucket.
Bucket names must follow S3 naming rules: 3-63 characters, lowercase letters, numbers, hyphens, no consecutive periods or IP-like names.
HeadBucket¶
Check whether a bucket exists and you have permission to access it.
Returns 200 OK if the bucket exists, 404 Not Found otherwise.
DeleteBucket¶
Delete a bucket. The bucket must be empty.
Returns 204 No Content on success, 409 BucketNotEmpty if the bucket contains objects.
GetBucketLocation¶
Get the region of a bucket.
Returns a LocationConstraint XML element. Arca always returns an empty location constraint (equivalent to us-east-1).
PutObject¶
Upload an object. The request body is streamed directly to storage — never buffered in memory.
ETag is computed as the hex-encoded MD5 of the object content.
The body is verified against Content-MD5, x-amz-checksum-* and a hex x-amz-content-sha256 before the object is stored, see Request body integrity. The checksum (algorithm and value) is recorded on the object and returned as x-amz-checksum-<algo> on this response, and on GetObject / HeadObject when the request carries x-amz-checksum-mode: ENABLED.
GetObject¶
Download an object. Supports Range header for partial content retrieval.
Returns the object body with Content-Type, Content-Length, ETag, and Last-Modified headers.
?partNumber={n} returns the bytes of part n of an object assembled by CompleteMultipartUpload (206, with Content-Range and x-amz-mp-parts-count; the ETag is the object's). With x-amz-checksum-mode: ENABLED the response carries the checksum of that part and the object's x-amz-checksum-type. A part number outside the object's parts, or n > 1 on an object that was not uploaded in parts, answers 400 InvalidPart; n = 1 on such an object returns it whole. partNumber cannot be combined with Range (400 InvalidRequest). A multipart object completed by an older version has no recorded part layout, so ?partNumber answers 400 InvalidPart for it.
HeadObject¶
Retrieve object metadata without downloading the body.
Returns the same headers as GetObject, but with no response body. ?partNumber={n} gives the size (Content-Length) and position (Content-Range) of one part, as above.
DeleteObject¶
Delete an object.
Returns 204 No Content on success. Deleting a non-existent key is not an error.
Directory Markers
S3 uses zero-byte objects with keys ending in / as directory markers (e.g., photos/). Arca preserves trailing slashes in object keys, so you can create, list, and delete directory markers just like any other object. When listing with a delimiter, directory markers whose key matches the listing prefix are excluded from Contents (they appear only as CommonPrefixes).
DeleteObjects¶
Delete multiple objects in a single request.
POST /{bucket}?delete HTTP/1.1
<Delete>
<Quiet>true</Quiet>
<Object><Key>file1.txt</Key></Object>
<Object><Key>file2.txt</Key></Object>
<Object><Key>photos/</Key></Object>
</Delete>
When Quiet is true, the response only includes errors. When false (default), the response includes both successfully deleted keys and errors.
Returns DeleteResult XML. Deleting non-existent keys is not an error.
CopyObject¶
Copy an object within or across buckets. The source is specified via the x-amz-copy-source header.
Returns CopyObjectResult XML with the new ETag and last modified timestamp.
ListObjectsV1¶
List objects in a bucket (legacy API). Prefer ListObjectsV2 for new applications.
| Parameter | Description |
|---|---|
prefix |
Limits results to keys beginning with the specified prefix |
delimiter |
Groups keys that share a common prefix (typically /) |
max-keys |
Maximum number of keys to return (default 1000) |
marker |
Start listing after this key |
Returns ListBucketResult XML with Contents, CommonPrefixes, IsTruncated, and NextMarker.
ListObjectsV2¶
List objects in a bucket with optional prefix and delimiter filtering.
| Parameter | Description |
|---|---|
prefix |
Limits results to keys beginning with the specified prefix |
delimiter |
Groups keys that share a common prefix (typically /) |
max-keys |
Maximum number of keys to return (default 1000) |
continuation-token |
Token from a previous truncated response |
start-after |
Start listing after this key |
Returns ListBucketResult XML with Contents, CommonPrefixes, IsTruncated, and NextContinuationToken.
CreateMultipartUpload¶
Initiate a multipart upload and obtain an upload ID.
Returns InitiateMultipartUploadResult XML containing the UploadId.
x-amz-checksum-algorithm (CRC32, CRC32C, CRC64NVME, SHA1, SHA256) and x-amz-checksum-type (COMPOSITE or FULL_OBJECT) choose the checksum the completed object gets, see Multipart checksums. Both are echoed back as response headers. A type alone, an unknown algorithm or type, and a combination that does not exist (SHA with FULL_OBJECT, CRC64NVME with COMPOSITE) are 400 InvalidRequest; without a type the algorithm's default is used.
UploadPart¶
Upload a part for a multipart upload.
Part numbers range from 1 to 10000. Each part (except the last) must be at least 5 MB.
The part body is verified exactly like a PutObject body (see Request body integrity); a refused part is not stored and does not replace an earlier upload of the same part number. A verified x-amz-checksum-<algo> is returned on the response.
When the upload was created with a checksum algorithm, the part is checksummed with it: Arca computes the checksum when the request declares none, and a checksum declared for another algorithm is 400 InvalidRequest (Checksum Type mismatch occurred, expected checksum Type: ..., actual checksum Type: ...).
CompleteMultipartUpload¶
Complete a multipart upload by assembling previously uploaded parts.
POST /{bucket}/{key+}?uploadId={id} HTTP/1.1
<CompleteMultipartUpload>
<Part>
<PartNumber>1</PartNumber>
<ETag>"etag1"</ETag>
</Part>
<Part>
<PartNumber>2</PartNumber>
<ETag>"etag2"</ETag>
</Part>
</CompleteMultipartUpload>
The resulting ETag is a composite: hex(MD5(binary_MD5(part1) || binary_MD5(part2) || ...))-{count}.
The response carries the object's Checksum<ALGO> and ChecksumType when the upload has a checksum algorithm. See Multipart checksums for what is checked and what the object gets.
AbortMultipartUpload¶
Abort a multipart upload and delete all uploaded parts.
Returns 204 No Content on success.
Conditional writes¶
PutObject, CompleteMultipartUpload, DeleteObject (including version-specific deletes) and DeleteObjects accept preconditions, so that clients can implement optimistic concurrency (compare-and-swap) on an object:
| Operation | Headers / fields | Fails with |
|---|---|---|
| PutObject, CompleteMultipartUpload | If-Match: <etag> (or *), If-None-Match: <etag> (or *) |
412 PreconditionFailed; 404 NoSuchKey for If-Match when the object does not exist |
| DeleteObject | If-Match, x-amz-if-match-last-modified-time, x-amz-if-match-size |
412 PreconditionFailed |
| DeleteObjects | per-key ETag, LastModifiedTime, Size |
a per-key PreconditionFailed entry in the DeleteResult errors |
If-None-Match: * is the "create only if absent" primitive. Deleting a key that does not exist is never a precondition failure (it is a no-op, as for an unconditional delete). A version-specific delete is checked against that exact version, not against the latest one.
Atomicity. The precondition is evaluated inside the same metadata transaction that installs the new version or removes the old one, so two concurrent conditional writes against the same ETag cannot both succeed: exactly one commits, the other gets 412. The check the handler performs before reading the body is only an optimisation that rejects stale requests early. See Architecture › Conditional writes for how each metadata backend guarantees this.
Cost of losing a race. The authoritative check runs at commit time, after the body has been received and stored, so a writer that loses a race has uploaded its whole body for nothing: the blob is deleted and the response is 412. For a refused CompleteMultipartUpload only the assembled object is discarded; the upload and its parts are kept, so the client can retry.
Clusters. In an HA cluster the check is exact per node, not cluster-wide: two conditional writes for the same key served by two different nodes at the same moment can both succeed (TD-025). Route a key's conditional writes to a single node, see High Availability.
Request body integrity¶
PutObject and UploadPart verify the request body against every digest the request declares, as AWS S3 does. A body that fails is not stored (an existing object or part is left untouched) and the request fails with:
| Header | Value | On mismatch | When malformed |
|---|---|---|---|
Content-MD5 |
base64 of the 16-byte MD5 | 400 BadDigest |
400 InvalidDigest |
x-amz-checksum-crc32, -crc32c, -crc64nvme, -sha1, -sha256 |
base64 of the big-endian CRC or the digest | 400 BadDigest (The CRC32 you specified did not match the calculated checksum.) |
400 InvalidRequest |
x-amz-trailer: x-amz-checksum-<algo> with an aws-chunked body (STREAMING-UNSIGNED-PAYLOAD-TRAILER, STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER) |
the same value, sent as a trailing header after the last chunk | 400 BadDigest |
400 InvalidRequest, also when the declared trailer is missing |
x-amz-content-sha256 |
64 hex digits | 400 XAmzContentSHA256Mismatch |
— (UNSIGNED-PAYLOAD and STREAMING-* carry no digest and are not checked) |
At most one checksum algorithm per request: two x-amz-checksum-* values (or a header and a trailer) are rejected with 400 InvalidRequest. Malformed values are refused before the body is read.
This is what makes the default behaviour of current SDKs work end to end: boto3 / botocore 1.36+ send a CRC32 on every upload (a header over HTTP, an aws-chunked trailer over HTTPS) and the AWS CLI a CRC64NVME; MinIO clients send Content-MD5, a hex x-amz-content-sha256 or a CRC32C. The checksum is stored and returned with x-amz-checksum-mode: ENABLED.
The chunks and the trailer of a signed aws-chunked body (STREAMING-AWS4-HMAC-SHA256-PAYLOAD and -PAYLOAD-TRAILER, sent by MinIO clients over plain HTTP) are verified against the request's signature while the body streams: a modified byte, a bad or missing chunk signature, a bad trailer signature and a body cut before its final chunk all answer 403 SignatureDoesNotMatch and store nothing. Signed chunks are not accepted with a presigned URL.
Multipart checksums¶
An upload created with x-amz-checksum-algorithm gives its completed object a checksum, built from the part checksums (no object byte is read at Complete time):
| Type | Value | Algorithms |
|---|---|---|
COMPOSITE |
base64(H(raw part checksums, concatenated))-N, N the number of parts |
CRC32, CRC32C, SHA1, SHA256 |
FULL_OBJECT |
the CRC of the whole object, rebuilt from the part CRCs and sizes | CRC32, CRC32C, CRC64NVME |
CRC64NVME (the AWS CLI default) only has the full-object form, SHA1 and SHA256 only the composite one; CRC32 and CRC32C default to composite.
CompleteMultipartUpload checks, before anything is assembled (a refused request leaves the upload as it was, so the client can fix it and complete again):
x-amz-mp-object-size, when sent, equals the size of the object (400 InvalidRequest);x-amz-checksum-type, when sent, is the upload's type (400 InvalidRequest);- a whole-object
x-amz-checksum-<algo>header equals the checksum computed from the parts (400 BadDigest; one for another algorithm is400 InvalidRequest); - a
Checksum<ALGO>given for a part in the XML equals the one stored for that part (400 BadDigest); a part left without one in the XML is accepted.
The object gets a checksum only when every part has a stored checksum; a part uploaded by an older version may not. The checksum is returned by Complete, by HeadObject and GetObject with x-amz-checksum-mode: ENABLED, and by GetObjectAttributes (Checksum, with ChecksumType).
The layout of the parts (number, size and checksum of each) is recorded with the object for every multipart object, with or without a checksum. It is what ?partNumber and the ObjectParts of GetObjectAttributes read (x-amz-max-parts and x-amz-part-number-marker page it). It is read on demand, so GET, HEAD and listings of a multipart object cost the same whatever its number of parts.
CopyObject keeps a FULL_OBJECT checksum (same bytes) and drops a COMPOSITE one: the copy has no parts, and never has a layout.
Intentional divergences from AWS S3¶
Arca targets drop-in AWS S3 compatibility: what AWS accepts, Arca accepts; what AWS rejects, Arca rejects — with the following deliberate, documented exceptions.
Conditional CompleteMultipartUpload never returns 409¶
AWS can answer a conditional CompleteMultipartUpload with 409 ConditionalRequestConflict when a conflicting operation is in flight. Arca evaluates the precondition once, at commit time, and has no "conflict in flight" state, so every refused precondition is 412 PreconditionFailed. Clients that retry on 409 should treat 412 the same way: re-read the object and decide again.
Multipart uploads without a checksum algorithm¶
AWS now gives an object uploaded without any checksum a CRC64NVME FULL_OBJECT checksum, computed by the service. Arca does not: an upload created without x-amz-checksum-algorithm completes without an object checksum, as before. Clients that ask for a checksum (the AWS CLI and current SDKs do by default) get it.
If-None-Match with a specific ETag¶
AWS documents If-None-Match on writes only with the value *. Arca also accepts a specific ETag (or a list), with standard HTTP semantics: the write proceeds unless the current object's ETag matches. Code that relies on this is not portable to AWS.
Object Lock on existing empty buckets¶
AWS only allows Object Lock to be enabled at bucket creation (x-amz-bucket-object-lock-enabled: true); PutObjectLockConfiguration on a bucket that was not created with Object Lock returns 409 InvalidBucketState.
Arca additionally accepts PutObjectLockConfiguration on an existing bucket that is still empty (auto-enabling versioning, as AWS does at creation). A late enable on an empty bucket cannot retroactively weaken WORM protection on any object, so it is safe; non-empty buckets are still rejected with 409 InvalidBucketState. This is a console-UX convenience: enable Object Lock from a bucket's settings page without recreating the bucket.
Implication: the drop-in direction (AWS-targeted clients running against Arca) is unaffected, but code that relies on this relaxation is not portable to AWS, which would reject it. This divergence is also why the Ceph s3-test test_object_lock_put_obj_lock_invalid_bucket is expected to fail.