Skip to content

Role-Based Access Control

TDB Enterprise enforces a three-tier role system on every DB-managed API key. Roles are checked on every request; a denied request returns HTTP 403 with a clear message.


Roles

Role Can query Can register/delete sources Can manage API keys Can verify audit log
read
readwrite
admin

Role sources:

Key type Role
Static env keys (TDB_API_KEYS) Always admin
JWT tokens role claim in the JWT payload (default: admin)
DB-managed keys role column in the registry database

Endpoint access matrix

Endpoint Minimum role
GET /v1/sources read
GET /v1/sources/{id} read
GET /v1/sources/{id}/schema read
POST /v1/query read
POST /v1/mcp read
GET /v1/views read
POST /v1/views/{name}/run read
GET /health None (public)
GET /metrics None (public — restrict via network policy in production)
POST /v1/sources readwrite
DELETE /v1/sources/{id} readwrite
POST /v1/auth/keys admin
GET /v1/auth/keys admin
DELETE /v1/auth/keys/{id} admin
POST /v1/auth/keys/{id}/rotate admin
PUT /v1/auth/keys/{id}/rate-limit admin
PUT /v1/auth/keys/{id}/role admin
PUT /v1/auth/keys/{id}/tools admin
GET /v1/audit/verify admin
POST /v1/audit/export admin

Creating keys with a specific role

# Read-only key for a dashboard or BI tool
curl -X POST http://localhost:8000/v1/auth/keys \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name": "dashboard", "role": "read"}'

# Readwrite key for a pipeline that registers sources
curl -X POST http://localhost:8000/v1/auth/keys \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"name": "etl-pipeline", "role": "readwrite"}'

Changing a key's role

Role changes take effect immediately.

curl -X PUT http://localhost:8000/v1/auth/keys/<KEY_ID>/role \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"role": "readwrite"}'

Valid values: "read", "readwrite", "admin". Invalid values return HTTP 422.


Restricting MCP tool access

Beyond the role system, you can restrict which MCP tools a key can call. This is useful when you want a key that can query data but cannot introspect schema, or cannot run aggregate queries.

# Allow only query_source and schema_source
curl -X PUT http://localhost:8000/v1/auth/keys/<KEY_ID>/tools \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"tools": ["query_source", "schema_source"]}'

# Remove restriction (allow all tools)
curl -X PUT http://localhost:8000/v1/auth/keys/<KEY_ID>/tools \
  -H "Authorization: Bearer <ADMIN_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"tools": null}'

Available MCP tools: query_source, schema_source, preview_source, filter_source, aggregate_source, list_views, run_view.

Note

Tool-level restrictions only apply to DB-managed keys. Static env keys and JWT tokens are never tool-restricted.


Error responses

Scenario HTTP status Detail
No credentials 401 Invalid or missing credentials
Invalid token 401 Invalid or expired token
Valid key, insufficient role 403 Insufficient privileges. Required: 'readwrite', have: 'read'.
Valid key, tool not allowed 200 (tool-error) Tool 'schema_source' is not permitted for this API key.

MCP tool-access denials return HTTP 200 with isError: true in the JSON-RPC result, not a protocol-level error, so MCP clients handle them as a failed tool call rather than a connection failure.


# 1. Bootstrap key — used only to create other keys, then locked away
TDB_API_KEYS=your-strong-bootstrap-key

# 2. Create a read-only key for each integration
curl -X POST .../v1/auth/keys -d '{"name":"grafana","role":"read","expires_in_days":365}'
curl -X POST .../v1/auth/keys -d '{"name":"claude-desktop","role":"read"}'

# 3. Create a readwrite key for the team that registers sources
curl -X POST .../v1/auth/keys -d '{"name":"data-platform-team","role":"readwrite"}'

# 4. Restrict the Claude Desktop key to safe MCP tools only
curl -X PUT .../v1/auth/keys/<claude_key_id>/tools \
  -d '{"tools":["query_source","schema_source","preview_source"]}'