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

# Managing Encryption Keys

CyborgDB encrypts every index under a 256-bit AES key (the **index key**, also called the KEK in the service code). Without that key it is impossible to read the index — you cannot query, list, upsert, or even safely delete its contents.

v0.17 supports two completely different ways to provision and hold that key. Pick one per index:

| Mode             | What you set on `create_index` | Who holds the plaintext key                    | Best for                                                                                     |
| ---------------- | ------------------------------ | ---------------------------------------------- | -------------------------------------------------------------------------------------------- |
| **SDK-supplied** | `index_key` (32 bytes)         | Your SDK / application                         | Development, evaluation, single-tenant deployments where you already have a secure key store |
| **KMS-backed**   | `kms_name` (registry slot)     | The service's KMS (AWS KMS or Secrets Manager) | Production, multi-tenant, BYOK                                                               |

This page covers the **SDK-supplied** path. For production, prefer the KMS-backed path — see [Per-Index KMS & BYOK](./kms-byok) for the end-to-end setup.

<Warning>
  **Do not commit, log, or check in `index_key` material**, and do not store it in plaintext on shared filesystems. Lost keys cannot be recovered — every index wrapped under a lost key becomes permanently unreadable.
</Warning>

## Generating a key for development

For local development and evaluation, generate a 256-bit key locally and pass it as `index_key` (or the SDK's typed equivalent).

<CodeGroup>
  ```bash OpenSSL icon="rectangle-terminal" theme={null}
  # 32 random bytes, hex-encoded (64 hex chars)
  openssl rand -hex 32
  ```

  ```python Python SDK icon="python" theme={null}
  from cyborgdb import Client

  client = Client(base_url='http://localhost:8000', api_key='your-api-key')

  # Generate a 32-byte key; `save=True` writes it to ~/.cyborgdb/index_key
  index_key = client.generate_key(save=False)
  ```

  ```typescript TypeScript SDK icon="code" theme={null}
  import { Client } from 'cyborgdb';

  const client = new Client({ baseUrl: 'http://localhost:8000', apiKey: 'your-api-key' });

  // Returns a fresh 32-byte Uint8Array
  const indexKey: Uint8Array = client.generateKey();
  ```

  ```go Go SDK icon="golang" theme={null}
  import "github.com/cyborginc/cyborgdb-go"

  // Returns ([]byte, error)
  indexKey, err := cyborgdb.GenerateKey()
  if err != nil {
      log.Fatal(err)
  }
  ```
</CodeGroup>

## Using the key

The same `index_key` you passed to `create_index` must be re-supplied on **every** subsequent request for that index — `load_index`, `upsert`, `query`, `get`, `delete`, `delete_index`. The service stores only a hash to verify the key on each request; it never persists the plaintext.

<CodeGroup>
  ```python Python SDK icon="python" theme={null}
  # Create
  index = client.create_index('my_index', index_key=index_key, dimension=384)

  # Subsequent calls — same key
  index = client.load_index('my_index', index_key=index_key)
  index.upsert([{'id': 'a', 'vector': [0.1] * 384}])
  results = index.query(query_vectors=[0.1] * 384, top_k=5)
  ```

  ```typescript TypeScript SDK icon="code" theme={null}
  // Create
  const index = await client.createIndex({
      indexName: 'my_index',
      indexKey,
      dimension: 384,
  });

  // Subsequent calls — same key
  const loaded = await client.loadIndex({ indexName: 'my_index', indexKey });
  await loaded.upsert({ items: [{ id: 'a', vector: new Array(384).fill(0.1) }] });
  const results = await loaded.query({ queryVectors: new Array(384).fill(0.1), topK: 5 });
  ```

  ```go Go SDK icon="golang" theme={null}
  dim := int32(384)

  // Create
  index, _ := client.CreateIndex(context.Background(), &cyborgdb.CreateIndexParams{
      IndexName: "my_index",
      IndexKey:  indexKey,
      Dimension: &dim,
  })

  // Subsequent calls — same key
  loaded, _ := client.LoadIndex(context.Background(), "my_index", indexKey)
  _ = loaded.Upsert(context.Background(), cyborgdb.VectorItems{
      {Id: "a", Vector: []float32{ /* 384 values */ }},
  })
  ```

  ```bash cURL icon="rectangle-terminal" theme={null}
  INDEX_KEY=$(openssl rand -hex 32)

  # Create
  curl -X POST "http://localhost:8000/v1/indexes/create" \
       -H "X-API-Key: cyborg_your_api_key_here" \
       -H "Content-Type: application/json" \
       -d "{
         \"index_name\": \"my_index\",
         \"index_key\": \"$INDEX_KEY\",
         \"dimension\": 384
       }"

  # Subsequent calls — same hex key
  curl -X POST "http://localhost:8000/v1/indexes/describe" \
       -H "X-API-Key: cyborg_your_api_key_here" \
       -H "Content-Type: application/json" \
       -d "{
         \"index_name\": \"my_index\",
         \"index_key\": \"$INDEX_KEY\"
       }"
  ```
</CodeGroup>

## Persisting the key

The service never persists the plaintext key, so your application owns key storage. For development, the Python SDK's `generate_key(save=True)` will cache the generated key in `~/.cyborgdb/index_key` — useful for evaluation, **not for production**.

For production:

* Store the key in your platform's secret store (Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, k8s Secret) and fetch it at runtime.
* Or **switch to the KMS-backed path** so the SDK never holds a long-term key: see [Per-Index KMS & BYOK](./kms-byok).

## Migrating from SDK-supplied to KMS-backed

You cannot rotate an index *in place* from `provider: none` to a real KMS provider — the wrapping model changes. The migration path is:

1. Create a new index with `kms_name` set instead of `index_key`.
2. Replay your data (`upsert`) into the new index.
3. Delete the old index.

This is intentional: the persisted envelope for `provider: none` records no wrap key, so there is nothing for the KMS path to unwrap. Bulk re-encryption against a fresh DEK happens on the upsert.

## See also

* [Per-Index KMS & BYOK](./kms-byok) — production setup with AWS KMS or Secrets Manager (preferred for production).
* [Environment Variables](./env-vars) — `CYBORGDB_API_KEY` and other service-level configuration.
* [Create an Encrypted Index](../encrypted-indexes/create-index) — full `create_index` reference, including `kms_name`.
