> ## 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 Object Storage

> Configure S3-compatible storage providers as data sources for your Archil disk

Before Archil can read or write to your bucket, the bucket needs to grant Archil access. This is a one-time setup per bucket, regardless of how you use the disk afterwards (mount, `disk exec`, anything else).

The Archil console walks you through this when you create a disk in the UI. This page documents what the console is doing, for setting things up by hand or scripting it.

## Amazon S3

[<Icon icon="aws" size={20} /> Amazon S3](https://aws.amazon.com/s3/) supports two ways to authorize Archil:

* **Bucket resource policy** — recommended. Archil's IAM role assumes access to your bucket via a resource policy you attach to the bucket. No long-lived credentials change hands.
* **Static IAM credentials** — paste an access key and secret key from an IAM user that has read/write access to the bucket. Simpler to set up, but you're responsible for rotating the credentials.

### Bucket resource policy

In the [AWS S3 console](https://console.aws.amazon.com/console/), select your bucket, open the **Permissions** tab, and edit the **Bucket policy**. Add the following statement, substituting `YOUR-BUCKET-NAME` and your disk's session ID for `YOUR-DISK-SESSION-ID` (shown in the [Archil console](https://console.archil.com), e.g. `asi_us-east-1_tl02r28ami6phda70d1pv`):

```json theme={null}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowArchilAccess",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789:role/archil-s3.prod.us-east-1"
            },
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR-BUCKET-NAME",
                "arn:aws:s3:::YOUR-BUCKET-NAME/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalTag/archilSessionId": "YOUR-DISK-SESSION-ID"
                }
            }
        }
    ]
}
```

The `archilSessionId` condition scopes the grant to a single Archil disk, so the bucket is only reachable by that one disk — every other disk in your organization is denied. This is the right default if you point one disk at the bucket.

The action list above is the minimum Archil needs: `s3:ListBucket` for bucket existence checks and listings, `s3:GetObject` for reads, `s3:PutObject` for writes (including multipart uploads), and `s3:DeleteObject` for object removal.

<Note>
  Archil doesn't call `AbortMultipartUpload` directly, so it's not required in the policy. However, on rare failure paths a multipart upload can be left incomplete, and the parts continue to incur storage cost until they're cleaned up. We recommend either adding `s3:AbortMultipartUpload` to the action list, or attaching an [S3 lifecycle rule](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpu-abort-incomplete-mpu-lifecycle-config.html) that aborts incomplete multipart uploads after a few days.
</Note>

The exact `Principal` ARN varies by Archil region — the [Archil console](https://console.archil.com) displays the precise policy to paste, including the right principal for your region.

#### Authorizing every disk in your organization

If you point many disks at the same bucket — for example, one disk per tenant or per workspace — swap the condition to match on `archilAccountId` instead. That's your Archil organization ID (shown in the [Archil console](https://console.archil.com), e.g. `org_3CjfHc6i0vXj24vu0Wpl8twU3nk`) and it's the same for every disk you create:

```json theme={null}
"Condition": {
    "StringEquals": {
        "aws:PrincipalTag/archilAccountId": "YOUR-ARCHIL-ACCOUNT-ID"
    }
}
```

With this condition every current and future disk in your organization can use the bucket without you having to edit the policy again. The trade-off is that any disk in your organization can reach this bucket, so use it when you control all the disks under that organization.

### Server-side encryption with KMS (SSE-KMS)

If your bucket is encrypted with **SSE-S3** (the default `AES256` option), nothing extra is needed — the bucket policy above is sufficient.

If your bucket is encrypted with **SSE-KMS** using a customer-managed KMS key, you also need to grant Archil's IAM role permission to use that key. Open the key in the [AWS KMS console](https://console.aws.amazon.com/kms/) (the key's ARN is shown at the top of its detail page), edit the **Key policy**, and add the following statement — substituting `YOUR-KMS-KEY-ARN`:

```json theme={null}
{
    "Sid": "AllowArchilToUseKey",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::123456789:role/archil-s3.prod.us-east-1"
    },
    "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey"
    ],
    "Resource": "YOUR-KMS-KEY-ARN"
}
```

`kms:Decrypt` lets Archil read objects, `kms:GenerateDataKey` lets it write new objects under envelope encryption, and `kms:DescribeKey` is occasionally used during client setup.

<Note>
  SSE-KMS with the AWS-managed `aws/s3` key does **not** work for cross-account access. AWS-managed keys can't be shared outside the account that owns them, so a bucket encrypted with `aws/s3` is unreachable from Archil's role. To use SSE-KMS with Archil, the bucket must use a [customer-managed KMS key](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#customer-cmk).
</Note>

Archil never sets `ServerSideEncryption` headers on its requests, so whatever default encryption you configure on the bucket is what objects land with. You don't need to tell Archil which key you're using.

### Static credentials

If you'd rather not edit the bucket policy, create an IAM user with `s3:*` permissions on the bucket and pass its access key ID and secret to Archil when you create the disk:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import * as archil from "disk";

  await archil.createDisk({
    name: "my-disk",
    mounts: [{
      type: "s3",
      bucketName: "my-bucket",
      accessKeyId: "AKIA...",
      secretAccessKey: "...",
    }],
  });
  ```

  ```python Python theme={null}
  import archil
  from archil import S3Mount

  archil.create_disk(
      name="my-disk",
      mounts=[S3Mount(
          bucket_name="my-bucket",
          access_key_id="AKIA...",
          secret_access_key="...",
      )],
  )
  ```
</CodeGroup>

## Google Cloud Storage

[<Icon icon="google" size={20} /> Google Cloud Storage](https://cloud.google.com/storage) authorizes Archil through static AWS-compatible HMAC credentials.

1. Open the [Google Cloud Storage console](https://console.cloud.google.com/storage/).
2. Click **Settings**, then **Interoperability**.
3. Under **Service account HMAC**, click **Create a key for another service account**.
4. Grant the **Cloud Storage** → **Storage Object Admin** role to the new service account.
5. Record the **Access key** and **Secret** for the new service account HMAC key.

Pass them when you create the disk:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await archil.createDisk({
    name: "my-disk",
    mounts: [{
      type: "gcs",
      bucketName: "my-bucket",
      accessKeyId: "GOOG1EABCD1234567890",
      secretAccessKey: "1234567890abcdef1234567890ABCD/EF",
    }],
  });
  ```

  ```python Python theme={null}
  from archil import GCSMount

  archil.create_disk(
      name="my-disk",
      mounts=[GCSMount(
          bucket_name="my-bucket",
          access_key_id="GOOG1EABCD1234567890",
          secret_access_key="1234567890abcdef1234567890ABCD/EF",
      )],
  )
  ```
</CodeGroup>

## Cloudflare R2

[<Icon icon="cloudflare" size={20} /> Cloudflare R2](https://www.cloudflare.com/r2/) authorizes Archil through static AWS-compatible credentials.

1. Open the [Cloudflare console](https://dash.cloudflare.com/login).
2. Browse to **R2** and click **Manage R2 API Tokens**.
3. Create a new token with **Object Read & Write** permissions.
4. Record the **Access Key ID**, **Secret Access Key**, and the default **endpoint** (looks like `https://<account>.r2.cloudflarestorage.com`).

Pass them when you create the disk:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await archil.createDisk({
    name: "my-disk",
    mounts: [{
      type: "r2",
      bucketName: "my-bucket",
      accessKeyId: "R2_ACCESS_KEY_ID",
      secretAccessKey: "R2_SECRET_ACCESS_KEY",
      endpoint: "https://<account>.r2.cloudflarestorage.com",
    }],
  });
  ```

  ```python Python theme={null}
  from archil import R2Mount

  archil.create_disk(
      name="my-disk",
      mounts=[R2Mount(
          bucket_name="my-bucket",
          access_key_id="R2_ACCESS_KEY_ID",
          secret_access_key="R2_SECRET_ACCESS_KEY",
          bucket_endpoint="https://<account>.r2.cloudflarestorage.com",
      )],
  )
  ```
</CodeGroup>

## Generic S3-compatible storage

Many providers expose S3-compatible APIs and work with Archil out of the box. To configure one:

1. Get the S3-compatible API endpoint from your provider.
2. Create or obtain access credentials (Access Key ID and Secret Access Key) with read/write/list/delete permissions on the bucket.
3. When creating your disk, select **Generic S3 Compatible** as the data source type and provide the endpoint plus credentials.

Tested with:

* [<Icon icon="database" size={16} /> MinIO](https://min.io/)
* [<Icon icon="database" size={16} /> DigitalOcean Spaces](https://www.digitalocean.com/products/spaces)
* [<Icon icon="database" size={16} /> Wasabi](https://wasabi.com/)
* [<Icon icon="database" size={16} /> Backblaze B2](https://www.backblaze.com/b2/) (with S3-compatible API)
* And many others
