Skip to main content
CyborgDB Service v0.17 ships with an operator-side multi-tenancy model built on cryptographic isolation: each index is wrapped by its own key, and per-user API keys hold their own wrapped data-encryption keys (DEKs). Permissions aren’t stored as a policy blob — the set of wrapped DEKs a user holds is their permission set, enforced by the encrypted-index engine on every request. This page is the operator playbook: how to enable RBAC, mint and revoke users, and the constraints to be aware of.

Modes

You enable RBAC by setting both keys at startup:
Or via YAML:
cyborgdb.yaml
Pick fresh, distinct values for the two keys. The root key has admin privileges; treat it like a master credential. Rotate it independently of CYBORGDB_API_KEY.

Key kinds

When RBAC is on, the service classifies every X-API-Key request header into one of three kinds: If CYBORGDB_SERVICE_ROOT_KEY is unset, the legacy single key keeps its full-access behavior — RBAC is strictly opt-in.

Provisioning users

A user is scoped to exactly one index, with a non-empty subset of {"read", "write"} permissions. Mint a user with the root key:
The api_key is returned exactly once and is never stored by the service. If you lose it, you must revoke the user and mint a new one. Hand it to the user securely (out-of-band, encrypted channel, secret store).
The user authenticates by passing cdbk_… as api_key to their Client — they need no index key of their own (the service resolves the index KEK server-side).

Listing and revoking users

Revocation is cryptographic, not policy-based: the service erases that user’s wrapped DEK(s) for the index. On the next request, the service has nothing to unwrap for them — even a captured cdbk_… token becomes useless. There is no propagation lag.

KMS-backed constraint

Per-user keys work only against KMS-backed indexes — i.e., indexes created with kms_name=… rather than index_key=…. The reason is mechanical: a user does not hold the index key, so the service must be able to resolve it server-side from a kms.registry slot. For SDK-supplied indexes (provider: none), there is no server-side key to resolve, so per-user provisioning is not supported. If you want multi-tenant access to an index that today uses an SDK-supplied key, migrate it to KMS-backed first (see Managing Encryption Keys → Migrating).
Mint-time provisioning still requires the index KEK to bootstrap the new user’s wrapped DEK. For KMS-backed indexes this happens server-side; for SDK-supplied indexes you’d need to pass the index key on the create_user call, but the resulting user still wouldn’t have a path to authenticate (the service can’t resolve the KEK for their later requests). This is why the constraint exists end-to-end.

End-to-end example

A typical operator workflow for onboarding a new tenant:
1

Provision a KMS slot

Add a registry entry to your service YAML — see KMS & BYOK.
cyborgdb.yaml
Restart the service. Look for KMS registry loaded in the logs.
2

Create the tenant's index (root)

3

Mint a user key for the tenant (root)

Hand the returned cdbk_… to the tenant out-of-band.
4

Tenant uses the index

The tenant configures their SDK client with api_key = "cdbk_…" and operates only on acme-documents. They cannot list, describe, or touch any other tenant’s index.
5

Revoke when access ends

Revocation is immediate — the tenant’s next request fails with 401.

Operational notes

  • Read vs write semantics. read covers query, get, list_ids, describe. write covers upsert, delete. Index lifecycle operations (create, train, delete_index) and user management are root-only — there’s no “delegated admin” permission.
  • KEK cache. Plaintext index KEKs live in an in-process cache for INDEX_KEK_CACHE_TTL_SECONDS (default 60s). Revoking a user is immediate and cryptographic; revoking KMS-level access to the wrap key propagates within the cache TTL.
  • Restarting the service. User keys survive restart — the wrapped DEKs are persisted alongside the index envelope. The root key is read from env/YAML on every start.
  • Auditing. The service logs the kind of key (root / user / legacy / none) on each request at INFO. Pair with structured logging to attribute index activity to users.

See also