Core Object Operations
PUT, GET, DELETE, HeadObject, CopyObject, and multipart upload — the read/write path every S3 client depends on. All are real, DB-and-storage-backed handlers on the XNS S3 Gateway, not stubs.
PUT, GET, DELETE, HeadObject, CopyObject, and multipart upload — the read/write path every S3 client depends on. All are real, DB-and-storage-backed handlers on the XNS S3 Gateway, not stubs.
What this area is
Put an object in, get it back out, copy it, delete it, or split a large upload into parts. Every S3-compatible client — the AWS CLI, boto3, rclone, restic — ultimately calls down to these operations: PutObject, GetObject, HeadObject, DeleteObject/DeleteObjects, CopyObject, GetObjectAttributes, and the six-verb multipart family (CreateMultipartUpload, UploadPart, UploadPartCopy, CompleteMultipartUpload, AbortMultipartUpload, ListParts). This is the surface a compatibility claim is judged on first.
How XNS implements it
Object CRUD lives in read_handlers.go and write_handlers.go (~952 lines) in the S3 Gateway codebase. Multipart lives in multipart_handlers.go (~500 lines); server-side copy is header-matched (PUT + x-amz-copy-source) in copy_handlers.go.
Bytes land on an erasure-coded storage layer, not a single disk. The corp deployment convention is 80 data shards + 40 parity shards = 120 hosts per object — Reed-Solomon DataNum/ParityNum are per-cost-center configuration set via Muse, not a hardcoded constant, so a given deployment can run a different split. Requests land on the S3 data plane, port 9000 plain HTTP always, with additive TLS on 9443 when a certificate is configured; every request is SigV4-signed and cost_center-scoped — the gateway is multi-tenant, and object storage resolves per-tenant, not per single account.
write_handlers.go.read_handlers.go.object_versions row that serves the bytes (BUG-239 fix) — under concurrent overwrite the served ETag always matches the served body.delete_sectorref); batch delete runs one gateway call per request, budgeted ≤30s per 1,000 keys.multipart_reaper.go cleans abandoned uploads automatically.How we conform to the S3 protocol
Object operations are verified against the same public conformance suite the rest of the industry is measured by — object CRUD, multipart, copy, and range/conditional requests, test by test. We publish the full per-test result, including what still fails, on the compatibility matrix rather than summarising it here.
Where we deliberately differ. An anonymous or cross-principal caller probing a deleted or foreign object gets a uniform 403 — not the AWS-standard 404-if-you-can’t-list / 403-if-you-can distinction. That difference is a choice, not a gap: it refuses to confirm whether an object exists to a caller who has no business knowing.
The reasoning behind that posture is set out under “What we chose to be different about” on the compatibility matrix, alongside every measured figure and its provenance.
How we compare
Source: the same competitor cells published on the S3 compatibility matrix — “Core object operations” section — researched from each vendor’s own documentation, 2026-06-14.
| Capability | XNS | AWS S3 | Ceph | MinIO | Wasabi | B2 | Storj |
|---|---|---|---|---|---|---|---|
| PUT / GET / DELETE object | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Multipart upload | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Server-side CopyObject | ✓ | ✓ | ✓ | ✓ | ✓ | ◐ x-amz-tagging rejected on copy | ✓ |
| Range GET | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Conditional requests (If-Match / If-None-Match) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ◐ undocumented |
| Max object size | No RAM cap | 48.8 TiB | ~5 TB | 5 TiB | 5 TB | 10 TB | No cap |
How applications use it
Addressing: both path-style (https://endpoint/bucket/key) and virtual-hosted-style (https://bucket.endpoint/key) work — vhost is the boto3/AWS CLI default and is enabled when the gateway’s domain is configured. Trailing-slash bucket paths are normalized for mc, restic, and kopia.
# Point at your gateway, not AWS
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://relayer.example.com", # or http://localhost:9000 for local/dev
aws_access_key_id="AKIA...",
aws_secret_access_key="...",
region_name="us-east-1", # GetBucketLocation always returns us-east-1
)
# PUT
s3.put_object(Bucket="my-bucket", Key="reports/q3.parquet", Body=data)
# Conditional PUT — first-write-wins, e.g. Terraform-style state locking
s3.put_object(Bucket="my-bucket", Key="state.tfstate", Body=data, IfNoneMatch="*")
# Byte-range GET — e.g. DuckDB/Polars reading a Parquet footer
resp = s3.get_object(Bucket="my-bucket", Key="reports/q3.parquet", Range="bytes=0-1048575")
# Multipart upload (boto3 switches automatically above its threshold)
s3.upload_file("large-backup.img", "my-bucket", "backups/large-backup.img")
# Server-side copy
s3.copy_object(Bucket="my-bucket", CopySource={"Bucket": "my-bucket", "Key": "reports/q3.parquet"},
Key="archive/q3.parquet")AWS CLI
aws s3api put-object --bucket my-bucket --key reports/q3.parquet --body q3.parquet \
--endpoint-url https://relayer.example.com
aws s3api get-object --bucket my-bucket --key reports/q3.parquet --range bytes=0-1048575 out.parquet \
--endpoint-url https://relayer.example.com
aws s3 cp large-backup.img s3://my-bucket/backups/large-backup.img \
--endpoint-url https://relayer.example.com
Use cases
Five patterns account for most of the traffic on top of these operations: media and video delivery, ML training-data staging, backup and restore, analytical file reads, and state-locking for infrastructure tooling. Each one leans on a specific verb below — ranged GET, multipart upload, or conditional PUT — not the API surface as a whole.
Served with byte-range GET so a player can seek without downloading the whole file.
Uploaded via multipart so a multi-GB shard survives a flaky connection without a full restart.
Written with multipart upload and restored with ranged GET for partial recovery.
Read with ranged GET against footer/metadata sections instead of full downloads.
Uses conditional PUT (If-None-Match: *) to guarantee first-write-wins.
Applications that lean on it heavily
The tools below aren’t hypothetical integration targets. DuckDB, Polars, Terraform’s S3 backend, restic, kopia, and rclone all depend on these operations behaving correctly today — not just being present in a compatibility list.
Ranged GET against Parquet footers is how these engines avoid downloading multi-GB files to read a schema or a column chunk.
State-file locking depends on conditional PUT (If-None-Match) behaving correctly; a backend that silently allows a duplicate write corrupts state.
Chunked backup tools that depend on multipart upload plus trailing-slash path normalization, both confirmed working (see the compatibility matrix's Tool compatibility table).
Mirrors and syncs large trees using multipart upload and server-side CopyObject to avoid re-transferring unchanged data.
What’s out of scope here
S3 Select (SelectObjectContent) is not exposed on this route table — a request gets a 404, not a 501. It's tracked as an integration-and-tooling boundary, not an object-CRUD one; see the integration & tooling resource page. Region is hardcoded to us-east-1 — GetBucketLocation always returns it, and region-specific endpoint hostnames aren't supported (single-region in this release).
FAQ
Is there a maximum object size on the XNS S3 Gateway?
No fixed RAM cap. PutObject streams to storage at constant memory above 40 MB (write_handlers.go, ~952 lines), unlike a buffer-everything-in-RAM implementation.
Does the XNS S3 Gateway support conditional requests?
Yes. If-Match and If-None-Match are supported on PUT and GET, the same mechanism Terraform’s S3 backend uses for state-file locking (IfNoneMatch: “*” on first write).
Is multipart upload required for large files?
Not required, but recommended above roughly 64 MB. CreateMultipartUpload / UploadPart / UploadPartCopy / CompleteMultipartUpload / AbortMultipartUpload / ListParts are all real handlers in multipart_handlers.go (~500 lines); boto3 and the AWS CLI switch to multipart automatically.
The object-operations rows above are one section of the full S3 compatibility matrix — buckets, versioning, lifecycle, encryption, access control, replication, and integration all get the same treatment.
Explore the platform
Everything you need to go from evaluation to production.