> ## Documentation Index
> Fetch the complete documentation index at: https://docs.archil.com/llms.txt
> Use this file to discover all available pages before exploring further.

# S3 API

> Read and write Archil disks over an S3-compatible HTTP API — list, download, upload, and delete objects with any S3 client, no mount required.

Archil exposes every disk as an **S3-compatible bucket**. Point `aws s3`, `boto3`, `s5cmd`, or any other S3 client at the Archil endpoint, present per-disk SigV4 credentials, and read or write the disk's contents over HTTPS without installing the [Archil client](/mounting/linux) or running an [`exec`](/compute/serverless-execution).

This S3 view talks directly to the disk, so it's **[strongly read-after-write consistent](/details/consistency)** with every other client. An object you read over the S3 API always reflects the latest write — even one an agent made a moment ago through a [mount](/mounting/linux) or [`exec`](/compute/serverless-execution) — with no propagation delay and no stale cache. You can serve files from a website while an agent is actively editing them and trust that every read is current.

We also recommend using the S3 API to accelerate access to a disk: requests are served from Archil's high-performance, in-region SSD storage rather than going all the way to object storage, making it lower-latency than a general-purpose S3 bucket.

```bash theme={null}
aws s3 ls s3://dsk-abc123/ \
  --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
```

For workloads that need full POSIX semantics (renames, locks, fast random reads), [mount the disk](/mounting/linux) instead — the S3 API isn't optimised for high-throughput streaming.

## Endpoint

The S3 API is served from a per-region endpoint. Use the endpoint that matches the region your disk lives in:

| Region                    | Code              | Endpoint                                          |
| ------------------------- | ----------------- | ------------------------------------------------- |
| AWS US East (N. Virginia) | `aws-us-east-1`   | `https://s3.green.us-east-1.aws.prod.archil.com`  |
| AWS US West (Oregon)      | `aws-us-west-2`   | `https://s3.green.us-west-2.aws.prod.archil.com`  |
| AWS EU West (Ireland)     | `aws-eu-west-1`   | `https://s3.green.eu-west-1.aws.prod.archil.com`  |
| GCP US Central (Iowa)     | `gcp-us-central1` | `https://s3.blue.us-central1.gcp.prod.archil.com` |

The endpoint uses **path-style** addressing (`https://<endpoint>/<bucket>/<key>`); virtual-host style is not supported. Most S3 clients require an explicit flag to force path style:

<CodeGroup>
  ```bash AWS CLI theme={null}
  aws s3 ls s3://dsk-abc123/ \
    --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
  ```

  ```python boto3 theme={null}
  import boto3
  from botocore.config import Config

  s3 = boto3.client(
      "s3",
      endpoint_url="https://s3.green.us-east-1.aws.prod.archil.com",
      aws_access_key_id="<access-key-id>",
      aws_secret_access_key="<secret-key>",
      region_name="us-east-1",
      config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
  )
  ```

  ```bash s5cmd theme={null}
  s5cmd \
    --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com \
    ls s3://dsk-abc123/
  ```
</CodeGroup>

## Authentication

Each disk has its own set of S3 credentials — there are no account-wide S3 keys. To get credentials, add an **S3 user** to the disk:

1. Open the disk's **Details** page in the [Archil console](https://console.archil.com).
2. Under **Authorized Users**, click **Add User** and select **S3 credentials**.
3. The console shows the **Access Key ID** and **Secret Access Key** exactly once. Save them somewhere secure — the secret cannot be retrieved again.

Credentials are scoped to a single disk: presenting them on a request to a different bucket returns `AccessDenied`. To rotate, add a new S3 user and remove the old one. The S3 API uses standard **AWS Signature Version 4** — any S3 SDK signs correctly out of the box.

<Note>
  S3 credentials are distinct from [disk tokens](/concepts/disk-users#disk-token-authorization) (used by the Archil client when mounting) and from [Control Plane API keys](/api-reference/introduction) (account-level, used to manage disks). They cannot be used in place of either one.
</Note>

## Bucket name

The bucket name is the **disk ID** as shown in the console (`dsk-…`).

```bash theme={null}
aws s3 ls s3://dsk-abc123/
```

To address a [branch](/concepts/branches-and-checkpoints) instead of the disk's root, append the branch name to the disk ID with a `.` separator. The same disk credentials authorize every branch.

```bash theme={null}
aws s3 ls s3://dsk-abc123.branch-xyz/
```

## Supported operations

| Operation           | Method                                                                                                                             | Notes                                                                                                             |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `HeadBucket`        | `HEAD /<bucket>`                                                                                                                   | Verifies the bucket exists and the credentials can read it.                                                       |
| `GetBucketLocation` | `GET /<bucket>?location`                                                                                                           | Returns the disk's region.                                                                                        |
| `ListObjectsV2`     | `GET /<bucket>?list-type=2`                                                                                                        | Supports `prefix`, `delimiter`, `max-keys`, and `continuation-token`.                                             |
| `GetObject`         | `GET /<bucket>/<key>`                                                                                                              | Streams the object body. Supports HTTP `Range`.                                                                   |
| `HeadObject`        | `HEAD /<bucket>/<key>`                                                                                                             | Returns size, content type, and last-modified.                                                                    |
| `PutObject`         | `PUT /<bucket>/<key>`                                                                                                              | Creates or overwrites the file at `<key>`. See [Writing](#writing).                                               |
| `DeleteObject`      | `DELETE /<bucket>/<key>`                                                                                                           | Removes the file at `<key>`. Idempotent.                                                                          |
| `DeleteObjects`     | `POST /<bucket>?delete`                                                                                                            | Bulk-deletes up to 1,000 keys in one request; returns per-key results.                                            |
| `AppendObject`      | `PUT /<bucket>/<key>?append=true`                                                                                                  | Appends to an existing file (or creates it). Up to 1 MiB per call — see [Appending](#appending).                  |
| Multipart upload    | `CreateMultipartUpload` / `UploadPart` / `CompleteMultipartUpload` / `AbortMultipartUpload` / `ListParts` / `ListMultipartUploads` | Upload large objects in parts — see [Large uploads](#large-uploads). Each part (except the last) must be ≥ 5 MiB. |

`CopyObject` and `ListBuckets` return `MethodNotAllowed` or `NotImplemented`. For renames or any other POSIX operation, mount the disk as a file system or use [`disk.exec`](/compute/serverless-execution).

### Listing

`ListObjectsV2` accepts the standard query parameters:

```bash theme={null}
aws s3api list-objects-v2 \
  --bucket dsk-abc123 \
  --prefix logs/ \
  --delimiter / \
  --max-keys 100 \
  --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
```

The only delimiter that's accepted is `/`. Any other delimiter value returns a `400 InvalidArgument`. This matches how Archil clients see the underlying directory tree — `delimiter=/` collapses each directory into a single `CommonPrefixes` entry, exactly as a real S3 bucket would.

Pagination uses the standard `NextContinuationToken` — pass it back as `continuation-token` to fetch the next page.

### Range requests

`GetObject` honours the HTTP `Range` header end-to-end and returns `206 Partial Content` with a `Content-Range` header:

```bash theme={null}
# First 1 KiB
curl -H "Range: bytes=0-1023" https://s3.green.us-east-1.aws.prod.archil.com/dsk-abc123/data.bin

# Last 500 bytes
curl -H "Range: bytes=-500" https://s3.green.us-east-1.aws.prod.archil.com/dsk-abc123/data.bin

# Open-ended
curl -H "Range: bytes=1048576-" https://s3.green.us-east-1.aws.prod.archil.com/dsk-abc123/data.bin
```

`Accept-Ranges: bytes` is set on every response so clients can discover range support without an explicit probe.

### Content type

`Content-Type` is derived from the object key's extension when one is recognised; otherwise Archil sniffs the first 512 bytes. `HEAD` and `GET` always agree on the same content type for a given key.

### Writing

`PutObject` creates or overwrites the file at the given key, creating any parent directories as needed. The write commits to the disk before the response returns, so the object is immediately visible to every other client — the same [read-after-write consistency](/details/consistency) as a FUSE mount or `disk.exec`. When the disk is synchronized to a [data source](/concepts/data-sources), the new object is flushed back to the underlying bucket like any other write. `DeleteObject` is idempotent — deleting a key that doesn't exist still returns success — and if you send a `Content-MD5` header on a `PutObject`, Archil validates the body against it and rejects a mismatch. To remove many keys at once, `DeleteObjects` (`POST /<bucket>?delete`) deletes up to 1,000 per request and reports per-key results; `aws s3 rm --recursive` and `s5cmd rm` use it automatically.

```bash theme={null}
# Upload a file
aws s3 cp report.json s3://dsk-abc123/reports/2026-01/data.json \
  --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com

# Delete it
aws s3 rm s3://dsk-abc123/reports/2026-01/data.json \
  --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
```

### Large uploads

Multipart uploads are supported, so most S3 clients work unchanged for large files — the AWS CLI and boto3 automatically switch to multipart above their threshold (8 MB by default), uploading parts in parallel and assembling them server-side. `CreateMultipartUpload`, `UploadPart`, `CompleteMultipartUpload`, `AbortMultipartUpload`, `ListParts`, and `ListMultipartUploads` are all available. Each part except the last must be at least **5 MiB**, and an upload may have at most **10,000 parts**.

```bash theme={null}
# Large files upload via multipart automatically — no special flags needed
aws s3 cp big.tar s3://dsk-abc123/backups/big.tar \
  --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
```

A single `PutObject` body is capped at **5 GB**; larger objects (up to **5 TiB**) must go through multipart.

### Appending

`AppendObject` appends bytes to the end of an existing object, or creates it if the key doesn't exist yet — useful for log-style writes that grow a file over time without rewriting it. It's a `PutObject` with an `?append=true` query parameter:

```http theme={null}
PUT /dsk-abc123/logs/app.log?append=true
```

Each call may append at most **1 MiB**; to grow a file further, append in successive calls. Like every other write, an append commits before the response returns and is immediately visible to other clients. The standard CLI tools don't expose append, so it's easiest to use through the SDKs — `append_object` ([Python](/sdks/python#reading-and-writing-objects)) / `appendObject` ([TypeScript](/sdks/typescript#reading-and-writing-objects)).

## What gets exposed

The S3 API exposes the disk's filesystem as it currently exists — every file written through any client (FUSE mount, `disk.exec`, the Console file browser, or `PutObject` over this API) appears as an object with the key set to its POSIX path relative to the disk root, and objects written here show up identically on a mount. Newly written files are visible to the next S3 request after the write commits, with the same [read-after-write consistency](/details/consistency) as any other Archil client.

Symlinks resolve to the file they point at. Directories appear as `CommonPrefixes` when you list with `delimiter=/`. There is no separate set of "S3 objects" — the bucket is just a different protocol view of the same data.

## Share URLs

For one-off, ad-hoc downloads, Archil can mint **pre-signed share URLs** that don't require any credentials. Generate one from the console's file browser ("Share" in the row menu), or via the [Control Plane API](/api-reference/introduction):

```bash theme={null}
curl -X POST \
  -H "Authorization: ${ARCHIL_API_KEY}" \
  "https://control.green.us-east-1.aws.prod.archil.com/api/disks/dsk-abc123/share/path/to/file.csv?expiresIn=3600"
```

`expiresIn` is in seconds and accepts `3600` (1h), `28800` (8h), `86400` (24h), or `604800` (7d). The default is 24h. The returned URL can be opened in any browser; it streams the file with a sensible `Content-Type` and `Content-Disposition` so files render inline where the browser supports it. Share URLs are signed by Archil's internal KMS key, so they survive control-plane restarts and work across replicas.

## Limitations

* **`PutObject` is capped at 5 GB per request.** Larger objects (up to 5 TiB) must use [multipart](#large-uploads). `CopyObject` is unsupported; for renames, mount the disk.
* **Reserved paths are rejected.** Keys under `.archil/` are managed by Archil and can't be written or deleted.
* **Streaming-signed bodies aren't supported.** Configure your client to use `UNSIGNED-PAYLOAD` or a full-body SHA-256 (the default for `aws s3 cp` and `boto3`). A `STREAMING-AWS4-HMAC-SHA256-PAYLOAD` request returns `NotImplemented`.
* **Active mount conflicts return `409`.** If a file is being actively written through a FUSE mount that holds a write [delegation](/concepts/shared-disks#ownership-rules), a conflicting `PutObject` or `DeleteObject` returns `409 Conflict` rather than racing the mounted client.
* **One bucket per credential.** S3 credentials are disk-scoped; cross-disk access requires a separate set of credentials.
* **`/` is the only supported delimiter** for `ListObjectsV2`.
* **No `ListBuckets`.** With disk-scoped credentials there is at most one bucket to list, so the operation isn't meaningful.
* **No S3 versioning, object lock, ACLs, tags, lifecycle rules, or bucket policies.** A disk is a filesystem exposed over S3, not a full S3 bucket. `PutObject` overwrites the previous contents at that key in place, and prior versions are not retained. The following APIs return `NotImplemented`:

  * Versioning: `GetBucketVersioning`, `PutBucketVersioning`, `ListObjectVersions`
  * Object lock: `Put/GetObjectLockConfiguration`, `Put/GetObjectRetention`, `Put/GetObjectLegalHold`
  * Object ACLs and tagging
  * Lifecycle and bucket policy APIs

  To preserve point-in-time state, use [checkpoints and branches](/concepts/branches-and-checkpoints) instead. A checkpoint captures the entire disk at a moment in time and is immutable once created.
* **Throughput.** The S3 API is optimised for low latency and convenience. For sustained, high-throughput reads or writes use the [Archil client](/mounting/linux) — it talks the native protocol and benefits from local caching.

## Related

* [Branches & Checkpoints](/concepts/branches-and-checkpoints) — fork a disk and read a branch over S3 with `dsk-….<branch>`
* [Disk Users](/concepts/disk-users) — how authorization works for the disk's other access paths
* [Mounting on Linux](/mounting/linux) — the POSIX-protocol path, with full read/write support
* [Serverless Execution](/compute/serverless-execution) — run code directly on the disk instead of pulling data out
