> ## 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.

# Access Control (RBAC)

CyborgDB supports per-user access control (RBAC) on an encrypted index. Instead of sharing the single root index key with every caller, the holder of the **root** index KEK can mint per-user keys that grant scoped `read` and/or `write` access. Each user then loads the index with their own per-user key and is restricted to the permissions they were granted.

<Note>**Key model.** Each index is encrypted under a 32-byte Key-Encryption-Key (KEK) — the `index_key`. The root KEK holder mints per-user wrapped keys; **the set of wraps that exist *is* the permission set** — there is no separate permission flag. A read-only user simply has no write wrap. Managing users (create / delete / list) is always gated on the root KEK.</Note>

***

## Minting User Keys

The root holder calls `create_user_keys`, supplying the new user's 16-byte identifier, their 32-byte per-user KEK, the permissions to grant, and the **root** index KEK as the admin gate.

<CodeGroup>
  ```python Python icon="python" theme={null}
  import cyborgdb_core as cyborgdb
  import secrets

  client = cyborgdb.Client(storage_config=cyborgdb.StorageConfig.disk("/tmp/cyborgdb"))

  # Root index KEK — the admin gate for user management
  index_key = secrets.token_bytes(32)
  index = client.create_index("shared_index", index_key)

  # Mint a read-only user and a read/write user
  reader_id = secrets.token_bytes(16)
  reader_kek = secrets.token_bytes(32)
  index.create_user_keys(reader_id, reader_kek, permissions=["read"], index_key=index_key)

  writer_id = secrets.token_bytes(16)
  writer_kek = secrets.token_bytes(32)
  index.create_user_keys(writer_id, writer_kek, permissions=["read", "write"], index_key=index_key)
  ```
</CodeGroup>

You must securely deliver each user's `user_id` and `user_kek` to that user out-of-band — together they are that user's credentials for the index.

***

## Loading the Index as a User

A user loads the index with their own KEK as the `index_key` and passes their `user_id`. From then on, every operation is gated to the permissions they hold — a read-only user's `upsert`/`delete` is rejected, raising `RuntimeError`.

<CodeGroup>
  ```python Python icon="python" theme={null}
  # The read-only user loads the index with their own key
  user_index = client.load_index("shared_index", reader_kek, user_id=reader_id)

  # Reads are allowed (user's KEK + user_id gate every data op)
  results = user_index.query(
      query_vectors=[0.1, 0.2, 0.3, 0.4],
      top_k=5,
      index_key=reader_kek,
      user_id=reader_id,
  )

  # Writes are rejected for a read-only user
  # user_index.upsert([...], index_key=reader_kek, user_id=reader_id)  # raises RuntimeError — no write wrap
  ```
</CodeGroup>

<Note>Every data operation requires the `index_key=` keyword argument; an RBAC user passes their KEK as `index_key=` together with `user_id=`.</Note>

***

## Listing and Revoking Users

User management is always gated on the **root** KEK. `list_user_keys` returns each user's id and whether they have read/write; `delete_user_keys` revokes a user (idempotent).

<CodeGroup>
  ```python Python icon="python" theme={null}
  # List all users (root-gated)
  for u in index.list_user_keys(index_key=index_key):
      print(u["user_id"], u["has_read"], u["has_write"])

  # Revoke a user (idempotent, root-gated)
  index.delete_user_keys(reader_id, index_key=index_key)
  ```
</CodeGroup>

***

## API Reference

For full signatures and exceptions, refer to the API Reference:

<CardGroup cols={2}>
  <Card title="Python API Reference" href="../../python/encrypted-index/manage-users" icon="python">
    API reference for `create_user_keys` / `list_user_keys` / `delete_user_keys` in Python
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.