> ## 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) or that upload very large objects via multipart, [mount the disk](/mounting/linux) instead — the S3 API supports whole-object `PutObject`/`DeleteObject` but not multipart uploads, and it 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>`. Whole-object upload only — see [Writing](#writing). |
| `DeleteObject`      | `DELETE /<bucket>/<key>`    | Removes the file at `<key>`. Idempotent.                                                       |

`CopyObject`, multipart uploads (`CreateMultipartUpload` and friends), and `ListBuckets` return `MethodNotAllowed` or `NotImplemented`. For renames, large multipart uploads, 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.

```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
```

### Forcing single-part uploads

Archil doesn't support multipart uploads, so every write has to be a single `PutObject`. Most S3 clients automatically switch to multipart above a size threshold — 8 MB in the AWS CLI and boto3 — and those multipart requests are rejected. Two ways to keep uploads single-part:

* **Use the single-part command directly.** `aws s3api put-object` always issues one `PutObject` and never falls back to multipart:

  ```bash theme={null}
  aws s3api put-object \
    --bucket dsk-abc123 \
    --key reports/2026-01/data.json \
    --body ./data.json \
    --endpoint-url https://s3.green.us-east-1.aws.prod.archil.com
  ```

* **Raise the multipart threshold.** For `aws s3 cp`/`sync`, push the threshold up to the 5 GB single-PUT ceiling so any file below it uploads in one request:

  ```bash theme={null}
  aws configure set default.s3.multipart_threshold 5GB
  ```

  In boto3, pass `TransferConfig(multipart_threshold=5 * 1024 ** 3)` to `upload_file`, or just call `client.put_object(...)`, which is always single-part.

A single `PutObject` body is capped at **5 GB**. Files larger than that genuinely require multipart and can't be written over the S3 API — [mount the disk](/mounting/linux) and write through the filesystem instead.

## 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: key-${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

* **No multipart uploads.** `PutObject` is whole-object only, with a 5 GB ceiling per request — see [Forcing single-part uploads](#forcing-single-part-uploads). `CopyObject` is also unsupported. For larger writes or 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.
* **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
