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

# Bring Your Own Key (BYOK)

> Encrypt your tenant-scoped data with a KMS key you own and control

BYOK is available on the **Enterprise plan**. Once configured, gavAI envelope-encrypts every per-tenant data unit under a 256-bit data-encryption key (DEK) that is itself wrapped by your customer-managed key (CMK). If you disable the CMK on your KMS side, the wrapped DEKs become un-unwrappable and your data is unreadable platform-wide within five minutes.

We never see your key material. We see only the wrapped DEK and the reference (ARN, resource path) you give us.

## Supported providers

| Provider                | Status      | Notes                                                 |
| ----------------------- | ----------- | ----------------------------------------------------- |
| AWS KMS                 | First-class | Cross-account IAM role + key ARN.                     |
| GCP Cloud KMS           | Coming next | Service-account + resource path.                      |
| HashiCorp Vault Transit | Coming next | Customer-minted token with `encrypt`/`decrypt` perms. |

## What BYOK covers

* Tenant DocumentDB Serverless cluster data-at-rest (AWS KMS only).
* Tenant R2 bucket data (`gavai-tenant-apps`, `gavai-ctx-artifacts`, `gavai-ctx-audit` namespaces).
* Tenant token-vault rows (`tenant_token_vault`, `tenant_token_audit`).

Transient compute state (Worker memory, Durable Object cache) stays under platform-managed keys. The audit log itself also stays platform-encrypted — it must remain readable for incident response per our DPA.

## What BYOK does not cover

* We do not re-encrypt rows when you rotate the CMK. AWS KMS handles envelope rotation transparently; gavAI does a fresh `KMS Decrypt` on every read and the new key version is picked up automatically.
* We do not provision the CMK for you. You mint the key in your AWS account.
* We do not retain a copy of the key material under a different KEK. The wrapped DEK depends entirely on your CMK being enabled.

## AWS KMS — step by step

### 1. Mint a CMK in your AWS account

In the AWS KMS console (or via `aws kms create-key`):

* **Key type:** Symmetric.
* **Key usage:** Encrypt and decrypt.
* **Key spec:** `SYMMETRIC_DEFAULT` (AES-256-GCM).
* **Region:** the region you want gavAI to call from. We support `us-east-1`, `us-east-2`, `us-west-1`, `us-west-2`, `eu-west-1`, `eu-west-2`, `eu-central-1`, `ap-northeast-1`, `ap-southeast-1`, `ap-southeast-2`.

Note the key ARN; it looks like `arn:aws:kms:us-east-1:123456789012:key/abcd1234-...`.

### 2. Create an IAM role gavAI can assume

In your AWS IAM console, create a role named `GavaiKmsAccess` (or anything — only the ARN matters) with:

**Trust policy** (lets gavAI's proxy role assume this role):

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "AWS": "arn:aws:iam::816253706262:role/gavai-customer-kms-proxy"
    },
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {
        "sts:ExternalId": "your-workspace-uuid-from-console"
      }
    }
  }]
}
```

Your workspace UUID is shown on the **Customer-managed encryption keys** page once you sign in. The external-ID condition prevents the deputy problem (no other AWS account can persuade us to assume your role).

**Permissions policy** — minimal, scoped to the single CMK:

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:DescribeKey",
      "kms:GenerateDataKey"
    ],
    "Resource": "arn:aws:kms:us-east-1:123456789012:key/abcd1234-..."
  }]
}
```

Note the role ARN; it looks like `arn:aws:iam::123456789012:role/GavaiKmsAccess`.

### 3. Configure BYOK in the gavAI console

Sign in as a workspace admin and open **Settings → Customer-managed encryption keys**.

Paste:

* The **IAM role ARN** from step 2.
* The **KMS key ARN** from step 1.
* The **region** of the CMK.

Click **Save AWS configuration**. The console writes the binding and surfaces an `UNVERIFIED` status badge.

### 4. Verify

Click **Verify**. gavAI assumes your IAM role and calls `kms:DescribeKey` on the supplied key ARN. If everything is wired correctly, the badge turns green and the row's `verified_at` timestamp updates.

Common verification errors:

| Code                     | Meaning                                                                                                               | Fix                                             |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `BYOK_KMS_ACCESS_DENIED` | Trust policy doesn't allow gavAI's proxy role to assume yours, or the permission policy is missing `kms:DescribeKey`. | Re-check the trust + permission JSON in step 2. |
| `BYOK_KEY_DISABLED`      | The CMK is in `Disabled` or `PendingDeletion` state.                                                                  | Re-enable the CMK in the AWS KMS console.       |
| `BYOK_KMS_UNAVAILABLE`   | Transient AWS KMS rate-limit or network error.                                                                        | Click **Verify** again.                         |
| `BYOK_KMS_INTERNAL`      | gavAI-side issue.                                                                                                     | Contact support; we'll triage.                  |

### 5. Rotate the CMK (optional)

Use AWS KMS console's **Key rotation** or `aws kms enable-key-rotation`. gavAI doesn't need notification — the next `KMS Decrypt` call automatically picks up the new key version.

### 6. Revoke

To revoke a BYOK binding, click **Revoke** on the console. The row's `revoked_at` timestamp is stamped, all wrap/unwrap calls start returning `BYOK_KEY_DISABLED`, and your data is unreadable platform-wide within five minutes.

You may also revoke by disabling or scheduling deletion of the CMK on the AWS KMS side. Within a couple of minutes, our verification probe flips the badge to red.

Re-enabling BYOK after revocation requires a fresh **Save AWS configuration** — the previous binding is permanently closed.

## Encryption pattern

For each data unit (row, blob, etc.):

```
1. gavAI generates a fresh 32-byte DEK; never persisted in plaintext.
2. Data is encrypted with the DEK using AES-256-GCM.
3. The DEK is wrapped under your CMK via kms:Encrypt → wrapped_dek.
4. We store: { ciphertext, wrapped_dek, key_ref, iv, tag }.
5. On read: kms:Decrypt(wrapped_dek) → DEK. Data decrypted with DEK.
```

We cache the unwrapped DEK in worker memory **for the request lifetime only** — never across requests, never on disk. This bounds the blast radius if a worker isolate is compromised mid-request.

Per-request KMS Decrypt adds ≈10ms (p50) / 50ms (p99) of latency. The Enterprise SLA tolerates this; if you have a sub-50ms p99 read requirement, talk to us before enabling.

## Key rotation behaviour

* **Customer rotates CMK via KMS:** transparent. AWS KMS handles version rotation; gavAI gets the new version on the next decrypt.
* **Customer disables CMK via KMS:** within ≤5 minutes, all reads start failing with HTTP 503 + `BYOK_KEY_DISABLED`.
* **Customer schedules CMK deletion via KMS:** same as disable until the deletion completes; after that the data is permanently unrecoverable.

## Audit events

Every BYOK mutation writes a row to `tenant_audit_log` (visible at `/console/audit`):

| Action                     | When                                                               |
| -------------------------- | ------------------------------------------------------------------ |
| `byok.provider_enabled`    | Save AWS configuration form submitted. Metadata: provider, region. |
| `byok.verified`            | Verify succeeded.                                                  |
| `byok.verification_failed` | Verify returned an error. Metadata: error\_code.                   |
| `byok.revoked`             | Revoke confirmed.                                                  |

## What we never store

* Your CMK material (we can't — only AWS KMS holds it).
* Plaintext DEKs at rest (only wrapped form is persisted).
* IAM role session credentials beyond the cache lifetime (15 minutes max).
