Modes
You enable RBAC by setting both keys at startup:
cyborgdb.yaml
Key kinds
When RBAC is on, the service classifies everyX-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:
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
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 withkms_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.Restart the service. Look for
cyborgdb.yaml
KMS registry loaded in the logs.2
Create the tenant's index (root)
3
Mint a user key for the tenant (root)
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
Operational notes
- Read vs write semantics.
readcoversquery,get,list_ids,describe.writecoversupsert,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 atINFO. Pair with structured logging to attribute index activity to users.
See also
- Per-Index KMS & BYOK — required for per-user keys.
- Environment Variables —
CYBORGDB_SERVICE_ROOT_KEY,INDEX_KEK_CACHE_TTL_SECONDS. - REST API: Create User, List Users, Delete User.