Policy Management API
Complete API reference for creating, managing, and inspecting authorization policies in the Taruvi Platform using Cerbos.
Introduction
The Taruvi Platform uses Cerbos for fine-grained, policy-based authorization. Cerbos provides a powerful policy engine that separates authorization logic from your application code, making it easier to manage, audit, and update access controls.
What is Cerbos?
Cerbos is an open-source authorization system that uses policies to define who can do what with which resources. It supports:
- Attribute-Based Access Control (ABAC): Make decisions based on user attributes, resource attributes, and context
- Role-Based Access Control (RBAC): Define permissions based on user roles
- Derived Roles: Dynamic roles that change based on context (e.g., "owner" of a resource)
- Policy Conditions: Fine-grained rules using expressions
Learn more: Cerbos Documentation
Taruvi's Enhancements
Taruvi extends Cerbos with several developer-friendly features:
- Automatic Principal Population: No need to manually build principal objects - Taruvi automatically extracts user ID, roles, and attributes from the request context
- Flexible Principal Resolution: Check permissions using username, email, or JWT token - not just principal objects
- Automatic Scope Injection: Multi-tenant isolation is handled automatically - all policies are scoped to
{tenant_id}_{app_slug} - System Policy Lifecycle: Policies are automatically created and deleted when resources (like DataTables) are created/deleted
- AppRole Integration: Roles are extracted from the Taruvi AppRole system (not Django Groups), providing app-scoped role management
- Query Plan Caching: Improved performance with tenant-scoped caching and stampede protection
System Policies
What are System Policies?
System policies are unrestricted policies automatically created by Taruvi when system resources (like DataTables) are created. They establish baseline permissions without custom rules.
System policies use a two-level approach:
-
Default Level (
scope=None): Global fallback policy- Resource policies: DENY all (fail-safe default)
- Role policies: ALLOW all (permissive default)
- Applied when no scoped policy exists
-
Scoped Level (
scope={tenant_id}_{app_slug}): Tenant-specific override- Resource policies: ALLOW all (permissive for tenant)
- Role policies: ALLOW all
- Takes precedence over default level
System Entity Types
The following entity types are protected system entities that have automatic policy lifecycle management:
| Entity Type | Purpose | Auto-Created | Auto-Deleted |
|---|---|---|---|
datatable | Data table resources | ✅ On DataTable creation | ✅ On DataTable deletion |
function | Function resources | ✅ On Function creation | ✅ On Function deletion |
storage | Storage resources | ✅ On Storage creation | ✅ On Storage deletion |
query | Query resources | ✅ On Query creation | ✅ On Query deletion |
Use Cases
System policies are used for:
- Instant resource access: New resources get baseline permissions immediately
- Fail-safe defaults: Global DENY ensures security by default
- Tenant isolation: Scoped policies provide tenant-specific access
- Lifecycle management: Policies are automatically cleaned up when resources are deleted
Learn more about Cerbos policy structure: Cerbos Policy Documentation
Auto-Policy Creation
Taruvi automatically creates and manages policies for system resources. This ensures that new resources have proper access controls from the moment they're created.
Key Features
1. Automatic Principal Population
Taruvi automatically builds the principal (user) object from the request context - no manual construction needed.
What gets auto-populated:
id: User ID from authenticated JWT tokenroles: Extracted from AppRole system (NOT Django Groups) - app-scoped rolesattr.tenant_id: Current tenant schema nameattr.app_slug: Current app slug from URL- User attributes: Merged from
User.attributesJSONField
Learn more: Cerbos Principal Concept
2. Flexible Principal Resolution
Check permissions using 4 different methods - no need to always provide a principal object.
Resolution Priority (first match wins):
- Full principal object - Explicit principal in request body
- Username parameter -
?username=john@example.com(DB lookup) - Email parameter -
?email=john@example.com(DB lookup) - Authenticated user - Use JWT token user (default)
Examples:
# Method 1: Explicit principal object
curl -X POST "/api/apps/crm/check/resources" \
-H "Authorization: Bearer TOKEN" \
-d '{
"principal": {"id": "user_123", "roles": ["admin"]},
"resources": [...]
}'
# Method 2: Username parameter (admin checking other user's permissions)
curl -X POST "/api/apps/crm/check/resources?username=john@example.com" \
-H "Authorization: Bearer ADMIN_TOKEN" \
-d '{"resources": [...]}'
# Method 3: Email parameter
curl -X POST "/api/apps/crm/check/resources?email=john@example.com" \
-H "Authorization: Bearer ADMIN_TOKEN" \
-d '{"resources": [...]}'
# Method 4: Authenticated user (simplest)
curl -X POST "/api/apps/crm/check/resources" \
-H "Authorization: Bearer TOKEN" \
-d '{"resources": [...]}'
Use cases:
- Method 1: Custom principals with specific attributes
- Method 2/3: Admin interfaces checking permissions for other users
- Method 4: Standard permission checks for the authenticated user
3. Automatic Scope Injection
Multi-tenant isolation is handled automatically - you never need to manually manage scopes.
What Taruvi does automatically:
- All policies are scoped to
{tenant_id}_{app_slug}(e.g.,public_crm) - All resources are scoped during authorization checks
- Derived roles are prefixed with scope
Benefits:
- Complete tenant isolation - no cross-tenant data leaks
- No manual scope management
- Derived role prefixing for isolation (e.g.,
public_crm_common_roles)
Learn more: Cerbos Scoped Policies
4. AppRole Integration
Roles are extracted from the Taruvi AppRole system, NOT Django Groups.
Key differences:
| Feature | Django Groups | Taruvi AppRole |
|---|---|---|
| Scope | Global (per user) | App-specific |
| Model | django.contrib.auth.Group | cloud_site.roles.AppRole |
| Relationship | User → Group | User → App → Role |
| Usage | Traditional Django permissions | Cerbos authorization |
5. Query Plan Caching
Taruvi caches query plans for improved performance with tenant-scoped isolation.
Features:
- Tenant-scoped cache keys: Prevents cross-tenant cache poisoning
- Stampede protection: Distributed locks prevent cache stampede
- 5-minute TTL: Configurable cache timeout
- Automatic invalidation: Cache cleared on policy updates
Authentication
All endpoints require JWT authentication:
Authorization: Bearer <your_jwt_token>
Base URLs
Standard (tenant from connection context):
/api/apps/{app_slug}/policies/
Site-scoped (explicit tenant override):
/sites/{schema_name}/api/apps/{app_slug}/policies/
Site-scoped URLs allow you to explicitly specify the tenant context via the URL path. The SiteSwitchingMiddleware handles tenant switching based on the {schema_name} parameter. This is useful for:
- Cross-tenant operations (with proper permissions)
- Admin interfaces managing multiple tenants
- API clients that need explicit tenant control
Policy Management API
1. Create Policy
Create a new authorization policy.
POST /api/apps/{app_slug}/policies/
POST /sites/{schema_name}/api/apps/{app_slug}/policies/
POST endpoint rejects system entity types (datatable, function, storage, query). Use PUT endpoint for system types or wait for auto-creation.
Request Body - Resource Policy
{
"policy_type": "resource",
"name": "sales_invoices",
"entity_type": "invoice",
"import_derived_roles": ["common_roles"],
"rules": [
{
"actions": ["read", "update"],
"effect": "EFFECT_ALLOW",
"roles": ["admin", "manager"]
},
{
"actions": ["read", "update"],
"effect": "EFFECT_ALLOW",
"derived_roles": ["owner"],
"condition": {
"match": {
"expr": "R.attr.status != 'archived'"
}
}
}
],
"metadata": {
"description": "Sales invoices access policy",
"tags": ["finance", "sales-team"]
}
}
Request Body - Derived Role Policy
{
"policy_type": "derived_role",
"name": "common_roles",
"definitions": [
{
"name": "owner",
"parentRoles": ["user"],
"condition": {
"match": {
"expr": "R.attr.owner_id == P.id"
}
}
},
{
"name": "manager",
"parentRoles": ["owner"],
"condition": {
"match": {
"expr": "P.attr.role == 'manager'"
}
}
}
]
}
Derived role names are automatically prefixed with {tenant_id}_{app_slug}_ for tenant isolation. When referencing them in import_derived_roles, use the unprefixed name (e.g., "common_roles" not "public_crm_common_roles").
Learn more: Cerbos Derived Roles
Reserved Metadata Fields
The following metadata fields are automatically injected and cannot be set manually:
| Field | Type | Description |
|---|---|---|
created_by | string | User ID who created the policy |
created_date | string (ISO 8601) | Policy creation timestamp |
modified_by | string | User ID who last modified |
modified_date | string (ISO 8601) | Last modification timestamp |
Success Response (201 Created)
{
"success": true,
"message": "Policy created successfully",
"status_code": 201
}
cURL Example
curl -X POST "http://localhost:8000/api/apps/crm/policies/" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"policy_type": "resource",
"name": "sales_invoices",
"entity_type": "invoice",
"rules": [
{
"actions": ["read", "update"],
"effect": "EFFECT_ALLOW",
"roles": ["admin"]
}
]
}'
2. Update Policy
Update an existing authorization policy. Use this endpoint for system entity types.
PUT /api/apps/{app_slug}/policies/
PUT /sites/{schema_name}/api/apps/{app_slug}/policies/
- POST: Stricter validation - rejects role policies and system entity types
- PUT: Relaxed validation - allows role policies and system entity types
Both are idempotent and call the same underlying service.
cURL Example
curl -X PUT "http://localhost:8000/api/apps/crm/policies/" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"policy_type": "resource",
"entity_type": "datatable",
"name": "users",
"rules": [
{
"actions": ["*"],
"effect": "EFFECT_ALLOW",
"roles": ["admin"]
}
]
}'
3. List Policies
Retrieve all policies for the current app and tenant.
GET /api/apps/{app_slug}/policies/
GET /sites/{schema_name}/api/apps/{app_slug}/policies/
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name_regexp | string | .* | Regex filter for policy names |
scope_regexp | string | Auto-filtered | Regex filter for scope |
version_regexp | string | None | Regex filter for version |
include_disabled | boolean | false | Include disabled policies |
Success Response (200 OK)
{
"success": true,
"message": "Policies retrieved successfully",
"status_code": 200,
"data": [
{
"apiVersion": "api.cerbos.dev/v1",
"resourcePolicy": {
"resource": "invoice:sales_invoices",
"version": "default",
"scope": "public_crm",
"rules": [...]
}
}
],
"total": 1
}
cURL Example
curl -X GET "http://localhost:8000/api/apps/crm/policies/?name_regexp=invoice" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
4. Retrieve Policy
Get a specific policy by its full Cerbos policy ID.
GET /api/apps/{app_slug}/policies/?id={policy_id}
GET /sites/{schema_name}/api/apps/{app_slug}/policies/?id={policy_id}
Policy ID Formats
| Policy Type | Format |
|---|---|
| Resource | resource.{entity_type}_{name}.{version}/{scope} |
| Role | role.{name}/{scope} |
| Principal | principal.{name}.{version}/{scope} |
| Derived Role | derived_roles.{tenant_id}_{app_slug}_{name} |
cURL Example
curl -X GET "http://localhost:8000/api/apps/crm/policies/?id=resource.invoice_sales_invoices.default/public_crm" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
5. Delete Policy
Soft-delete a policy by its full Cerbos policy ID.
DELETE /api/apps/{app_slug}/policies/?id={policy_id}
DELETE /sites/{schema_name}/api/apps/{app_slug}/policies/?id={policy_id}
Cerbos uses soft delete - policies are disabled for audit trail.
cURL Example
curl -X DELETE "http://localhost:8000/api/apps/crm/policies/?id=resource.invoice_sales_invoices.default/public_crm" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
6. List Derived Roles
List derived role policies for the current tenant and app.
GET /api/apps/{app_slug}/policies/derived-roles/
GET /sites/{schema_name}/api/apps/{app_slug}/policies/derived-roles/
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name_regexp | string | .* | Regex filter for role names |
include_disabled | boolean | false | Include disabled policies |
cURL Example
curl -X GET "http://localhost:8000/api/apps/crm/policies/derived-roles/" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
7. Inspect Policies
Retrieve detailed metadata including actions, variables, attributes, derived roles, and constants.
GET /api/apps/{app_slug}/policies/inspect/
GET /sites/{schema_name}/api/apps/{app_slug}/policies/inspect/
Query Parameters
| Parameter | Type | Description |
|---|---|---|
policy_id | string (repeatable) | Specific policy IDs |
name_regexp | string | Regex filter for names |
scope_regexp | string | Regex filter for scope |
version_regexp | string | Regex filter for version |
include_disabled | boolean | Include disabled policies |
Success Response (200 OK)
{
"success": true,
"message": "Policies inspected successfully",
"status_code": 200,
"data": {
"results": {
"resource.invoice_sales_invoices.default/public_crm": {
"policyId": "resource.invoice_sales_invoices.default/public_crm",
"actions": ["read", "create", "update", "delete"],
"derivedRoles": [
{"name": "owner", "kind": "IMPORTED", "source": "common_roles"}
],
"variables": [
{"name": "is_owner", "kind": "LOCAL", "value": "R.attr.owner_id == P.id"}
]
}
}
}
}
cURL Example
curl -X GET "http://localhost:8000/api/apps/crm/policies/inspect/?name_regexp=invoice" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Authorization Check API
1. Check Resources
Check if a principal has permissions to perform actions on resources.
POST /api/apps/{app_slug}/check/resources
POST /sites/{schema_name}/api/apps/{app_slug}/check/resources
Principal Resolution
The check endpoint supports 4 ways to specify the principal (see Flexible Principal Resolution):
- Full principal object - Explicit principal in request body
- Username parameter -
?username=john@example.com - Email parameter -
?email=john@example.com - Authenticated user - JWT token user (default)
Request Body
{
"principal": {
"id": "user_123",
"roles": ["admin"],
"attr": {"department": "sales"}
},
"resources": [
{
"resource": {
"kind": "invoice:sales_invoices",
"id": "inv_001",
"attr": {"owner_id": "user_456"}
},
"actions": ["read", "update", "delete"]
}
]
}
Response
{
"requestId": "test",
"results": [
{
"resource": {
"id": "inv_001",
"kind": "invoice:sales_invoices",
"policyVersion": "default",
"scope": "public_crm"
},
"actions": {
"read": "EFFECT_ALLOW",
"update": "EFFECT_ALLOW",
"delete": "EFFECT_DENY"
},
"meta": {
"effectiveDerivedRoles": ["owner"]
}
}
]
}
cURL Examples
Using authenticated user:
curl -X POST "http://localhost:8000/api/apps/crm/check/resources" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resources": [
{
"resource": {"kind": "invoice:sales_invoices", "id": "inv_001"},
"actions": ["read", "update"]
}
]
}'
Using username parameter:
curl -X POST "http://localhost:8000/api/apps/crm/check/resources?username=john@example.com" \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resources": [
{
"resource": {"kind": "invoice:sales_invoices", "id": "inv_001"},
"actions": ["read", "update"]
}
]
}'
Learn more: Cerbos Check Resources
2. Plan Resources
Get a query plan for row-level filtering. Returns filter conditions that can be applied to database queries.
POST /api/apps/{app_slug}/plan/resources
POST /sites/{schema_name}/api/apps/{app_slug}/plan/resources
Principal Resolution
Supports the same 4 principal resolution methods as Check Resources.
Request Body
{
"principal": {
"id": "user_123",
"roles": ["viewer"],
"attr": {"department": "sales"}
},
"resource": {
"kind": "invoice:sales_invoices",
"policy_version": "default"
},
"action": "read"
}
Response
{
"filter_kind": "CONDITIONAL",
"condition": {
"expression": {
"operator": "eq",
"operands": [
{"variable": "request.resource.attr.owner_id"},
{"value": "user_123"}
]
}
}
}
Filter Kinds:
ALWAYS_ALLOWED: No filtering needed, user has full accessCONDITIONAL: Apply the returned condition to filter resultsALWAYS_DENIED: User has no access
cURL Example
curl -X POST "http://localhost:8000/api/apps/crm/plan/resources" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resource": {"kind": "invoice:sales_invoices"},
"action": "read"
}'
Learn more: Cerbos Query Plans
3. AuthZEN Access Evaluation
Single access evaluation using the AuthZEN standard format.
POST /api/apps/{app_slug}/access/v1/evaluation
POST /sites/{schema_name}/api/apps/{app_slug}/access/v1/evaluation
Request Body
{
"subject": {
"type": "user",
"id": "user_123",
"properties": {
"cerbos.roles": ["admin"],
"department": "sales"
}
},
"resource": {
"type": "invoice:sales_invoices",
"id": "inv_001",
"properties": {
"owner_id": "user_456"
}
},
"action": {
"name": "read"
}
}
Response
{
"decision": true,
"context": {
"id": "01HHENANTHFD5DV3HZGDKB87PJ",
"reason_admin": {
"effectiveDerivedRoles": ["owner"]
}
}
}
Learn more: Cerbos AuthZEN API
4. AuthZEN Batch Evaluation
Batch access evaluation for multiple actions/resources.
POST /api/apps/{app_slug}/access/v1/evaluations
POST /sites/{schema_name}/api/apps/{app_slug}/access/v1/evaluations
Request Body
{
"subject": {
"type": "user",
"id": "user_123",
"properties": {
"cerbos.roles": ["admin"]
}
},
"resource": {
"type": "invoice:sales_invoices",
"id": "inv_001"
},
"evaluations": [
{"action": {"name": "read"}},
{"action": {"name": "update"}},
{
"resource": {"type": "invoice:sales_invoices", "id": "inv_002"},
"action": {"name": "delete"}
}
]
}
Response
{
"evaluations": [
{"decision": true, "context": {...}},
{"decision": true, "context": {...}},
{"decision": false, "context": {...}}
]
}
5. System Actions
Get available actions for system entity types.
GET /api/apps/{app_slug}/authorization/system-action/
Response
{
"success": true,
"data": {
"datatable": ["create", "read", "update", "delete", "materialize"],
"function": ["create", "read", "update", "delete", "execute"],
"storage": ["create", "read", "update", "delete", "upload", "download"],
"query": ["create", "read", "update", "delete", "execute"]
}
}
Error Responses
Standard Error Format
{
"success": false,
"message": "Error description",
"status_code": 400,
"errors": {"detail": "Detailed error message"}
}
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success (GET, DELETE) |
| 201 | Created (POST) |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized (missing/invalid JWT) |
| 403 | Forbidden (permission denied) |
| 404 | Not Found (policy doesn't exist) |
| 500 | Internal Server Error |
Best Practices
1. Use Meaningful Policy Names
{
"name": "sales_invoices",
"entity_type": "invoice"
}
2. Use Specific Actions
{"actions": ["read", "update"]} // ✅ Good
{"actions": ["*"]} // ❌ Avoid in production
3. Leverage Derived Roles
{
"import_derived_roles": ["ownership_roles"],
"rules": [
{"actions": ["read", "update"], "derived_roles": ["owner"]}
]
}
4. Use Conditions for Fine-Grained Control
{
"condition": {
"match": {
"expr": "R.attr.department == P.attr.department && R.attr.status != 'archived'"
}
}
}
5. Use Inspection for Debugging
GET /api/apps/crm/policies/inspect/?name_regexp=invoice
Learn more: Cerbos Best Practices
Authorization Checks (SDK)
The Policy SDK provides methods for checking user permissions on resources. Unlike the REST API endpoints above (which manage policy definitions), these SDK methods evaluate authorization at runtime.
Check Permissions for Resources
- Python
- JavaScript
# Check permissions for multiple resources
result = auth_client.policy.check_resources([
{
"resource": {
"kind": "datatable",
"id": "orders"
},
"actions": ["read", "write"]
},
{
"resource": {
"kind": "datatable",
"id": "invoices"
},
"actions": ["read", "delete"]
}
])
# Check results
for res in result["results"]:
resource_id = res["resource"]["id"]
for action, effect in res["actions"].items():
allowed = effect == "EFFECT_ALLOW"
print(f"{resource_id}.{action}: {allowed}")
# Check if specific action is allowed
can_write_orders = result["results"][0]["actions"]["write"] == "EFFECT_ALLOW"
print(f"Can write orders: {can_write_orders}")
import { Policy } from '@taruvi/sdk'
const policy = new Policy(client)
// Check permissions for multiple resources
const result = await policy.checkResources([
{
resource: {
kind: "datatable",
id: "orders"
},
actions: ["read", "write"]
},
{
resource: {
kind: "datatable",
id: "invoices"
},
actions: ["read", "delete"]
}
])
// Check results
result.results.forEach(res => {
const resourceId = res.resource.id
Object.entries(res.actions).forEach(([action, effect]) => {
const allowed = effect === "EFFECT_ALLOW"
console.log(`${resourceId}.${action}: ${allowed}`)
})
})
// Check if specific action is allowed
const canWriteOrders = result.results[0].actions.write === "EFFECT_ALLOW"
console.log(`Can write orders: ${canWriteOrders}`)
Filter Allowed Resources
- Python
- JavaScript
# Get all datatables
all_tables = [
{'kind': 'datatable', 'id': 'users'},
{'kind': 'datatable', 'id': 'orders'},
{'kind': 'datatable', 'id': 'invoices'},
{'kind': 'datatable', 'id': 'admin_logs'}
]
# Filter to only tables where user can read AND write
allowed_tables = auth_client.policy.filter_allowed(
resources=all_tables,
actions=['read', 'write']
)
print(f"User can read+write {len(allowed_tables)} tables:")
for table in allowed_tables:
print(f" - {table['id']}")
import { Policy } from '@taruvi/sdk'
const policy = new Policy(client)
// Get all datatables
const allTables = [
{ kind: 'datatable', id: 'users' },
{ kind: 'datatable', id: 'orders' },
{ kind: 'datatable', id: 'invoices' },
{ kind: 'datatable', id: 'admin_logs' }
]
// Filter to only tables where user can read AND write
const allowedTables = await policy.filterAllowed({
resources: allTables,
actions: ['read', 'write']
})
console.log(`User can read+write ${allowedTables.length} tables:`)
allowedTables.forEach(table => {
console.log(` - ${table.id}`)
})
Get Allowed Actions
- Python
- JavaScript
# Get allowed actions for a specific resource
allowed_actions = auth_client.policy.get_allowed_actions(
resource={'kind': 'datatable', 'id': 'users'},
actions=['read', 'write', 'create', 'update', 'delete']
)
print(f"Allowed actions on 'users': {allowed_actions}")
# Output: ['read', 'write', 'update']
# Check if user has specific permission
can_delete = 'delete' in allowed_actions
print(f"Can delete users: {can_delete}")
# Use default CRUD actions if not specified
all_allowed = auth_client.policy.get_allowed_actions(
resource={'kind': 'datatable', 'id': 'orders'}
# Defaults to: ['read', 'write', 'create', 'update', 'delete']
)
print(f"Allowed CRUD actions: {all_allowed}")
import { Policy } from '@taruvi/sdk'
const policy = new Policy(client)
// Get allowed actions for a specific resource
const allowedActions = await policy.getAllowedActions({
resource: { kind: 'datatable', id: 'users' },
actions: ['read', 'write', 'create', 'update', 'delete']
})
console.log(`Allowed actions on 'users':`, allowedActions)
// Output: ['read', 'write', 'update']
// Check if user has specific permission
const canDelete = allowedActions.includes('delete')
console.log(`Can delete users: ${canDelete}`)
// Use default CRUD actions if not specified
const allAllowed = await policy.getAllowedActions({
resource: { kind: 'datatable', id: 'orders' }
// Defaults to: ['read', 'write', 'create', 'update', 'delete']
})
console.log(`Allowed CRUD actions:`, allAllowed)
See Also
Additional Resources
- Cerbos Documentation: https://docs.cerbos.dev/
- Cerbos Policy Structure: Policy Concepts
- Cerbos Derived Roles: Derived Roles Guide
- Cerbos Query Plans: Query Plan Recipe
- AuthZEN Specification: AuthZEN API
- Cerbos Conditions: Conditions Guide
- API Overview: Taruvi API Overview