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

# Storage Configuration

> Configure where the Enclave appliance stores encrypted file data — S3-compatible object storage, NFS, or local disk.

<Info>
  This page is for platform administrators of the **Private Virtual Appliance** deployment. Tenant users connecting their own S3 bucket (BYOS) should see [Storage](/enclave/storage) instead.
</Info>

## Overview

The Enclave appliance needs a storage backend to persist encrypted file data. All data written to storage is **ciphertext only** — the appliance encrypts every file before it leaves the application layer.

| Backend                    | Best for                                                               |
| -------------------------- | ---------------------------------------------------------------------- |
| **AWS S3 / S3-compatible** | Cloud-adjacent deployments with internet access; high durability       |
| **NFS**                    | On-premise or air-gapped environments with existing NAS infrastructure |
| **Local Disk**             | Single-node lab or evaluation deployments only                         |

Configuration is done at **Admin → Platform Infra → Storage**.

***

## AWS S3 / S3-compatible

Supports standard AWS S3, and any S3-compatible object store (MinIO, Ceph Object Gateway, Wasabi, Backblaze B2).

### Prerequisites

* An S3 bucket (or compatible endpoint) reachable from the appliance over HTTPS
* Because the appliance runs on VMware (not EC2), there is no instance profile — use **Assume Role** or **Static Credentials**
* For S3-compatible stores: the endpoint URL and a bucket created in advance

### Step 0 — Create an S3 bucket

1. Sign in to the **AWS Console** → navigate to **S3 → Create bucket**
2. Enter a bucket name and choose an AWS region
3. Keep **Block all public access** enabled (default)
4. Click **Create bucket** — note the bucket name and region for later

### Auth methods

<Tabs>
  <Tab title="Assume Role (recommended for AWS S3)">
    The appliance authenticates using a dedicated IAM user and then assumes a role that holds the S3 permissions. Long-lived credentials are not stored for S3 access.

    <Note>
      If you already created an `enclave-appliance` IAM user during KMS setup, skip Step 1 and reuse that user — just add the new AssumeRole permission in Step 3b.
    </Note>

    **Step 1 — Create a dedicated IAM user for the appliance**

    1. In the AWS Console, go to **IAM → Users → Create user**
    2. User name: `enclave-appliance` (or any name you prefer)
    3. Do not attach any permissions — click **Next** → **Create user**
    4. Open the user → **Security credentials** tab → **Create access key**
    5. Choose **"Application running outside AWS"** → **Create access key**
    6. **Save the Access Key ID and Secret Access Key**

    **Step 2 — Create an IAM role for S3 access**

    1. Go to **IAM → Roles → Create role**
    2. Trusted entity type: **AWS account** → **This account**
    3. Do not attach permissions yet → click **Next**
    4. **Role name**: `enclave-s3-role` (or any name you prefer) → **Create role**

    **Step 3 — Set the trust policy**

    1. Open the `enclave-s3-role` role → **Trust relationships** tab → **Edit trust policy**
    2. Replace the content with:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Sid": "AllowEnclaveApplianceToAssume",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::YOUR_ACCOUNT_ID:user/enclave-appliance"
        },
        "Action": "sts:AssumeRole"
      }]
    }
    ```

    Replace `YOUR_ACCOUNT_ID` with your 12-digit AWS account ID. Using an IAM user as the principal avoids the "Invalid principal in policy" error — the user exists before the role.

    3. Click **Update policy**

    **Step 3b — Allow the user to call AssumeRole**

    1. Go to the `enclave-appliance` user → **Permissions** tab → **Add permissions** → **Create inline policy**
    2. JSON:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/enclave-s3-role"
      }]
    }
    ```

    3. Name it `enclave-assume-s3-role` → **Create policy**

    **Step 3c — Attach S3 permissions to the role**

    1. Go back to `enclave-s3-role` → **Permissions** tab → **Add permissions** → **Create inline policy**
    2. JSON:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Sid": "EnclavePlatformS3Access",
        "Effect": "Allow",
        "Action": [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "s3:ListBucket",
          "s3:GetBucketLocation",
          "s3:HeadBucket"
        ],
        "Resource": [
          "arn:aws:s3:::YOUR_BUCKET_NAME",
          "arn:aws:s3:::YOUR_BUCKET_NAME/*"
        ]
      }]
    }
    ```

    3. Name it `enclave-s3-permissions` → **Create policy**
    4. Copy the **Role ARN** from the role Summary page

    **Step 4 — Configure in Enclave**

    Go to **Admin → Platform Infra → Storage** → **Add Storage**:

    | Field             | Value                                               |
    | ----------------- | --------------------------------------------------- |
    | Storage type      | S3                                                  |
    | Auth method       | Assume Role                                         |
    | Access Key ID     | `enclave-appliance` user access key                 |
    | Secret Access Key | `enclave-appliance` user secret key                 |
    | Role ARN          | `arn:aws:iam::YOUR_ACCOUNT_ID:role/enclave-s3-role` |
    | Bucket            | Bucket name from Step 0                             |
    | Region            | AWS region of the bucket                            |
    | Endpoint          | Leave blank for standard AWS S3                     |
    | Path Prefix       | Optional subdirectory                               |

    Click **Test Connection**, then **Save** and **Activate**.
  </Tab>

  <Tab title="Static Credentials">
    Use an IAM user's access key and secret directly. Simpler to set up; suitable for S3-compatible stores that do not support role assumption.

    <Warning>
      Static credentials are stored encrypted in the appliance database. Rotate them regularly and restrict permissions to the specific bucket.
    </Warning>

    **Step 1 — Create an IAM user with S3 permissions**

    In your AWS account (or S3-compatible admin console), create a user with the following permissions on your bucket:

    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Action": [
          "s3:GetObject",
          "s3:PutObject",
          "s3:DeleteObject",
          "s3:ListBucket",
          "s3:GetBucketLocation",
          "s3:HeadBucket"
        ],
        "Resource": [
          "arn:aws:s3:::YOUR_BUCKET_NAME",
          "arn:aws:s3:::YOUR_BUCKET_NAME/*"
        ]
      }]
    }
    ```

    Generate an access key for the user.

    **Step 2 — Configure in Enclave**

    Go to **Admin → Platform Infra → Storage** → **Add Storage**:

    | Field             | Value                                                                                                 |
    | ----------------- | ----------------------------------------------------------------------------------------------------- |
    | Storage type      | S3                                                                                                    |
    | Auth method       | Static Credentials                                                                                    |
    | Access Key ID     | Access key                                                                                            |
    | Secret Access Key | Secret key                                                                                            |
    | Bucket            | Your bucket name                                                                                      |
    | Region            | Region (use `us-east-1` or closest for S3-compatible stores)                                          |
    | Endpoint          | Leave blank for AWS S3; enter API URL for S3-compatible stores (e.g. `https://minio.yourcompany.com`) |
    | Path Prefix       | Optional subdirectory                                                                                 |

    Click **Test Connection**, then **Save** and **Activate**.
  </Tab>
</Tabs>

***

## NFS

NFS is the recommended option for fully on-premise or air-gapped deployments where no cloud object storage is available.

### Prerequisites

* An NFS v3 or v4 share exported and mountable from the appliance VM
* The share must be mounted on the appliance at a stable path before configuring storage (the appliance does not mount NFS shares itself — it reads/writes to an already-mounted path)
* Read/write permissions for the `enclave` process user on the mount

### Mounting the NFS share on the appliance

SSH into the appliance VM and mount the share:

```bash theme={null}
sudo mount -t nfs your-nfs-server:/export/enclave /mnt/enclave-storage
```

To make the mount persistent across reboots, add it to `/etc/fstab`:

```
your-nfs-server:/export/enclave  /mnt/enclave-storage  nfs  defaults,_netdev  0  0
```

### Configure in Enclave

Go to **Admin → Platform Infra → Storage** → **Add Storage**, select **NFS**:

| Field       | Value                                        |
| ----------- | -------------------------------------------- |
| Mount Point | `/mnt/enclave-storage` (or your chosen path) |

Click **Test Connection**, then **Save** and **Activate**. The appliance verifies the path is accessible and writable.

<Note>
  Enclave writes files under `<mount_point>/tenants/<tenant_id>/` and creates subdirectories automatically. Ensure the NFS share has sufficient capacity and monitor it externally — Enclave does not track NFS utilisation.
</Note>

***

## Local Disk

Local disk writes encrypted file data directly to the appliance VM's disk. Suitable for evaluation or single-node lab deployments only.

<Warning>
  Local disk storage is not replicated. If the VM disk is lost, all file data is lost. Do not use local disk for production deployments.
</Warning>

Go to **Admin → Platform Infra → Storage** → **Add Storage**, select **Local Disk**:

| Field       | Value                                                                |
| ----------- | -------------------------------------------------------------------- |
| Mount Point | An absolute path on the appliance disk (e.g. `/var/enclave/storage`) |

The path must exist and be writable. Enclave creates subdirectories automatically.

***

## Activating the storage backend

After saving, the backend must be **activated** before the appliance can accept file uploads:

1. In **Admin → Platform Infra → Storage**, locate the saved backend
2. Click **Activate**
3. The appliance immediately begins routing all new file uploads to this backend

<Warning>
  Only one platform storage backend can be active at a time. Activating a new backend does not migrate existing files — files stored under the previous backend remain there. Contact [support@kvelden.com](mailto:support@kvelden.com) if you need to migrate data between backends.
</Warning>

***

## Testing a saved backend

To re-run the connectivity test at any time, hover over the backend row in **Admin → Platform Infra → Storage** and click **Test**. The appliance performs a live head-check and reports success or the specific error. No data is written during the test.
