The S3 API served by mantad, the Triton Cloud object store, on port 7443.
For the concepts behind workspaces and keys, see
How object storage works.
Endpoint and addressing
| Property | Value |
|---|
| Endpoint | https://s3.<DOMAIN>:7443 when the cloud has an ACME-enabled DNS zone. Otherwise http://<IP>:7443 on a private address. Your operator provides it. |
| Addressing | Path-style only: /<BUCKET>/<KEY>. A bucket in the Host header is not parsed. |
| Region | Any region string signs successfully. GetBucketLocation returns the configured region, default us-east-1. |
| Server header | Server: mantad/<VERSION>. Every response carries x-amz-request-id and x-amz-id-2. |
Authentication
| Method | Behavior |
|---|
Signature Version 4, Authorization header | Supported. x-amz-date and x-amz-content-sha256 are required. The scope's service is s3 or sts. |
| Signature Version 4, presigned query | Supported. X-Amz-Expires is 1 to 604800 seconds. Outside that range: 403 AccessDenied. An expired URL: 403 AccessDenied, Request has expired. |
Signature Version 2, Authorization header | Refused. 403 InvalidDigest, or 400 InvalidArgument when the header has no :. |
| Session credentials | GetSessionToken, AssumeRole, and GetCallerIdentity are served as STS form posts to /. Session keys start with MSIA and need x-amz-security-token. Duration is 900 to 129600 seconds, default 43200. AssumeRoleWithWebIdentity returns 501. |
| Anonymous | Unsigned requests are evaluated against bucket ACLs, bucket policy, and public access block. Anonymous CreateBucket returns 403. |
Clock skew tolerance is 15 minutes. Beyond it: 403 RequestTimeTooSkewed.
Payload signing
x-amz-content-sha256 | Behavior |
|---|
| SHA-256 hex digest | Accepted. The body is checked against the digest. |
UNSIGNED-PAYLOAD | Accepted with a Content-Length. |
STREAMING-AWS4-HMAC-SHA256-PAYLOAD, STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER | Accepted. Each chunk signature is checked. A bad chunk: 403 SignatureDoesNotMatch. |
STREAMING-UNSIGNED-PAYLOAD-TRAILER | Refused with 403 AccessDenied unless the operator set MANTAD_ALLOW_STREAMING_UNSIGNED_PAYLOAD=true. |
| ECDSA streaming | 501 NotImplemented. |
Operations
| Group | Implemented operations |
|---|
| Service | ListBuckets |
| Bucket | CreateBucket, HeadBucket, DeleteBucket, GetBucketLocation, GetBucketVersioning, PutBucketVersioning |
| Object | PutObject, GetObject, HeadObject, DeleteObject, DeleteObjects, CopyObject, PostObject (browser form upload) |
| Listing | ListObjects, ListObjectsV2, ListObjectVersions |
| Multipart | CreateMultipartUpload, UploadPart, UploadPartCopy, CompleteMultipartUpload, AbortMultipartUpload, ListParts, ListMultipartUploads |
| Lifecycle | GetBucketLifecycleConfiguration, PutBucketLifecycleConfiguration, DeleteBucketLifecycle |
| ACL | GetBucketAcl, PutBucketAcl, GetObjectAcl, PutObjectAcl |
| CORS | GetBucketCors, PutBucketCors, DeleteBucketCors, and OPTIONS preflight |
| Tagging | GetBucketTagging, PutBucketTagging, DeleteBucketTagging, GetObjectTagging, PutObjectTagging, DeleteObjectTagging |
| Bucket policy | GetBucketPolicy, PutBucketPolicy, DeleteBucketPolicy, GetBucketPolicyStatus |
| Object Lock | GetObjectLockConfiguration, PutObjectLockConfiguration, GetObjectRetention, PutObjectRetention, GetObjectLegalHold, PutObjectLegalHold |
| Public access block | GetPublicAccessBlock, PutPublicAccessBlock, DeletePublicAccessBlock |
GetObjectTorrent returns 404 NoSuchKey. GetBucketOwnershipControls
returns 404 OwnershipControlsNotFoundError.
Every other S3 operation returns 501 NotImplemented. That includes bucket
encryption, website, logging, notification, replication, accelerate, request
payment, analytics, inventory, metrics, intelligent-tiering,
PutBucketOwnershipControls, DeleteBucketOwnershipControls,
GetObjectAttributes, RestoreObject, SelectObjectContent, and
CreateSession.
Operation details
| Feature | Behavior |
|---|
| Server-side encryption headers | 501 NotImplemented, server-side encryption is not supported (<HEADER>); reissue the request without SSE headers. Conflicting SSE headers: 400 InvalidArgument. |
Range on GetObject | Supported. Unsatisfiable: 416 InvalidRange. |
If-Match, If-Unmodified-Since on GET, HEAD | 412 PreconditionFailed on mismatch. |
If-None-Match, If-Modified-Since on GET, HEAD | 304 NotModified on match. |
If-Match, If-None-Match on PutObject and CompleteMultipartUpload | 412 PreconditionFailed on mismatch. |
If-Match, x-amz-if-match-size, x-amz-if-match-last-modified-time on DeleteObject | Checked. |
partNumber on GetObject | 1 is accepted for a single-PUT object. For a multipart object the whole object is returned. Below 1, or above 1 on a single-PUT object: InvalidPart. |
Content-MD5 | Checked on PutObject and UploadPart. Malformed: 400 InvalidDigest. Mismatch: 400 BadDigest. |
ListObjectVersions | Ignores delimiter. |
| Lifecycle rules | Supports id, status, a prefix filter, expiration.days, abort_incomplete_multipart_upload.days_after_initiation, and noncurrent_version_expiration.noncurrent_days. Transitions, expired_object_delete_marker, and and, tag, or size filters: 501 NotImplemented. |
Object Lock at CreateBucket | Turns versioning on. |
CopyObject from an access point or Outposts ARN | 501 NotImplemented. |
Bucket names
CreateBucket enforces these rules. A violation returns 400 InvalidBucketName.
| Rule | Value |
|---|
| Length | 3 to 63 characters |
| Characters | a-z, 0-9, -, . |
| First and last character | A lowercase letter or digit |
| Forbidden sequences | .., -., .- |
| IPv4 form | Four dot-separated numbers of 0 to 255 are refused |
| Prefix | xn-- is refused |
Every request path is also checked against the length, character, ..,
IP-address, and xn-- rules.
A new bucket gets a lifecycle rule, default-abort-incomplete-mpu-7d, that
aborts multipart uploads incomplete after 7 days, and a public access block
with all four settings on. CreateBucket with a LocationConstraint other
than empty or the configured region returns 400 InvalidLocationConstraint.
Re-creating a bucket you own, with no ACL headers and a default ACL, returns
200. Any other re-create returns 409 BucketAlreadyExists.
Limits
| Limit | Value | Error |
|---|
| Object key length | 1024 bytes | 400 KeyTooLongError |
Single PutObject body | 5 GiB, operator-tunable with MANTAD_MAX_BODY_BYTES | 400 EntityTooLarge |
UploadPart body | Same as PutObject | 400 EntityTooLarge |
| Minimum part size | 5 MiB, except the last part, checked at CompleteMultipartUpload | 400 EntityTooSmall |
Parts in CompleteMultipartUpload | 10,000 | 400 InvalidRequest |
Part numbering in CompleteMultipartUpload | Contiguous from 1, after duplicates collapse to the last entry | 400 InvalidPart |
Body length on PutObject, UploadPart | A SHA-256 or UNSIGNED-PAYLOAD request needs Content-Length or a body of known length. A streaming signed request needs x-amz-decoded-content-length. Transfer-Encoding: chunked alone does not waive either. | 411 MissingContentLength |
Keys in DeleteObjects | 1000 | 400 MalformedXML |
List page size, ListParts, ListMultipartUploads | 1000 | Clamped |
| Lifecycle rules per bucket | 1000 | 400 InvalidArgument |
| Object tags | 10 per object, 50 per bucket, key 128 characters, value 256 characters | 400 InvalidTag |
| XML request body | 20 MB | 400 MaxMessageLengthExceeded |
| Workspace quota | Set by the operator | 507 InsufficientStorage |
Request rate per workspace, per mantad process | 1000 per second, burst 200 | 503 SlowDown |
Request rate across all workspaces, per mantad process | 20,000 per second, burst 4000 | 503 SlowDown |
| Body idle timeout | 120 seconds | |
| Object | ETag |
|---|
Single PUT, and each part | "<MD5_HEX>" of the body |
| Multipart | "<MD5_HEX>-<PART_COUNT>", the MD5 of the concatenated binary part MD5s |
Error codes
| Status | Codes |
|---|
304 | NotModified |
400 | BadDigest, EntityTooLarge, EntityTooSmall, IncompleteBody, InvalidArgument, InvalidBucketName, InvalidDigest, InvalidLocationConstraint, InvalidPart, InvalidRequest, InvalidRetentionPeriod, InvalidTag, InvalidURI, KeyTooLongError, MalformedPOSTRequest, MalformedPolicy, MalformedXML, MaxMessageLengthExceeded, UnresolvableGrantByEmailAddress |
403 | AccessDenied, InvalidAccessKeyId, InvalidDigest (Signature Version 2 header), RequestTimeTooSkewed, SignatureDoesNotMatch |
404 | NoSuchBucket, NoSuchBucketPolicy, NoSuchCORSConfiguration, NoSuchKey, NoSuchLifecycleConfiguration, NoSuchObjectLockConfiguration, NoSuchPublicAccessBlockConfiguration, NoSuchTagSet, NoSuchUpload, ObjectLockConfigurationNotFoundError, OwnershipControlsNotFoundError |
405 | MethodNotAllowed |
409 | BucketAlreadyExists, BucketAlreadyOwnedByYou, BucketNotEmpty, InvalidBucketState |
411 | MissingContentLength |
412 | PreconditionFailed |
416 | InvalidRange |
500 | InternalError |
501 | NotImplemented |
503 | ServiceUnavailable, SlowDown |
507 | InsufficientStorage |
Error bodies are S3 XML with a <RequestId> element.
See also