Casbin Authorization Policies Documentation
10 min read
This page explains how authorization policies work on the Unique platform: how a policy rule is structured, what every field means, which resources and actions exist, and how to create custom policies via the API.
Authorization on the platform is handled by the Gatekeeper service, which uses Casbin as its policy engine. Every permission check and every policy you create ultimately maps to a Casbin rule.
Authorization model
Every authorization check answers one question:
May subject (a user, role, or group) from company perform action on resource, where the object belongs to object company and is owned by object owner?
The formal Casbin model (from next/services/gatekeeper/src/casbin/model.conf) defines six request fields and seven policy fields:
[request_definition]
r = sub, dom, obj, obj_company, obj_owner, act
[policy_definition]
p = sub, dom, obj, obj_company, obj_owner, act, eft
[role_definition]
g = _, _, _
g2 = _, _, _
[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))Two properties of this model are important to understand before writing policies:
Allow unless denied. A request is granted when at least one matching policy has effect
allowand no matching policy has effectdeny. A singledenyrule overrides any number ofallowrules. This makesdenyrules a powerful tool for carving exceptions out of broad grants and a dangerous one if applied too widely.Role inheritance. A policy whose subject is a role (e.g.
Chat_User) applies to every user assigned to that role via agrule, including transitively through role-to-role and group-to-group inheritance.
Evaluation flow for a single request:
eft is Casbin's abbreviation for effect the outcome a policy rule produces when it matches a request. In this model it's the last field of a p rule (stored in column v6), and it takes one of two values:
allowthe rule grants the access it describesdenythe rule blocks the access it describes
The policy effect line in the model config defines how effects from all matching rules combine:
How policies are stored: ptype and v0–v6
Policies are persisted in the casbin_rule database table. Casbin uses a generic column layout ptype plus positional columns v0 through v6 and the meaning of each column depends on the ptype of the row.
ptype values
| Name | Purpose | Who writes it |
|---|---|---|---|
| Permission policy | Grants or denies an action on a resource to a subject | Platform (role defaults), admins via API |
| Grouping policy | Assigns a member to a role or group: user → role, user → group, group → parent group, group → role | Platform (IdP sync, group sync), admins via API |
| Same-group relationship | Records that two users share a group; used only to evaluate | System-managed only never create these manually |
Field reference for p rules (permission policies)
For a p row, the columns map to the policy definition p = sub, dom, obj, obj_company, obj_owner, act, eft:
Column | Casbin field | Meaning | Example values |
|---|---|---|---|
|
| Subject who the policy applies to: a user ID, a role ID, or a group ( |
|
|
| Subject's company (domain) the tenant in which the policy applies |
|
|
| Resource what the policy is about (see Resources); may be instance-scoped |
|
|
| Object's company which tenant the accessed object must belong to |
|
|
| Object owner who must own the accessed object |
|
|
| Action the operation being permitted or denied (see Actions) |
|
|
| Effect |
|
The distinction between v1 (subject's company) and v3 (object's company) exists so cross-tenant access can be expressed explicitly. In practice, almost all customer policies use company:own for v3, meaning "the object must belong to the same company as the requesting user".
Field reference for g rules (role and group assignments)
For a g row, the columns map to g = member, role_or_group, company:
Column | Meaning | Example values |
|---|---|---|
| Member the user or group being assigned |
|
| Role or parent group what the member is assigned to |
|
| Company (domain) the tenant in which the assignment applies |
|
A g rule means: within v2, subject v0 inherits everything granted to v1. Role assignments are transitive if a user is in a group, the group inherits a role, and that role inherits another role, the user gets all of it.
g2 rules (system-managed)
g2 rows record that two users share at least one group (v0 = requesting user, v1 = object owner, v2 = company). They exist solely so that policies with objectOwner = ownGroup can be evaluated, are generated on demand by the platform, and are invalidated when group membership changes. Never create or delete g2 rows yourself.
Value prefixes
Values in the casbin_rule table carry a URN-style prefix identifying what kind of value they are:
Prefix | Used in | Example |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
When using the API you do not write these prefixes. The API accepts plain values (subject: "usr_abc123", resource: "chat", action: "read") and Gatekeeper adds/strips the prefixes when reading from or writing to the database. The prefixes only matter when you inspect raw policy rows (e.g. via adminPaginatedPolicies or getAllPolicies).
Special values
Several sentinel values are resolved dynamically at enforcement time instead of being compared literally:
Value | Valid in | Meaning |
|---|---|---|
|
| The object must belong to the same company as the requesting user. This is the standard value for tenant-scoped policies. |
|
| The object must be owned by the requesting user themselves. Use this to grant users access to their own data only (e.g. their own chats). |
|
| The object must be owned by a user who shares at least one group with the requesting user. Evaluated via system-managed |
| any field | Wildcard matches anything. |
Instance-scoped resources. The resource field may target a single object instance using the format {resource}:{id}, e.g. assistant:asst_xyz789. The platform uses this itself: when access to a specific assistant is granted, Gatekeeper writes p rules with resource:assistant:{assistantId}. Because resource matching uses Casbin's keyMatch, a policy on the bare resource (resource:assistant) also covers all instance-scoped requests for that resource type.
Actions
The possible values for the action field (v5):
Action | Meaning |
|---|---|
| View a single object and its details |
| Enumerate objects of this type (e.g. list users, list memberships) |
| Create a new object |
| Create or modify objects |
| Delete objects |
| Administrative control configure, share, grant access, change settings |
| Interact with an assistant in a conversation (specific to |
Not every action is meaningful for every resource: chat only applies to assistants, and some resources only ever get checked with a subset of actions. When in doubt, check which actions the built-in roles grant on a resource (see next/services/gatekeeper/src/roles/roles-definition/).
Resources
The resource field (v2) accepts the following values (defined in GatekeeperResources, next/packages/backend/gatekeeper-utils/src/types/resources.ts). The descriptions indicate what each resource governs on the platform.
Identity and tenancy
Resource | Description |
|---|---|
| Platform user accounts |
| The tenant (company) entity itself and its settings |
| A user's membership in a company |
| User groups (typically synced from the identity provider) |
| API keys used for SCIM user provisioning |
Chat
Resource | Description |
|---|---|
| Chat conversations |
| Individual messages within chats |
| Automated assessments attached to messages (e.g. hallucination checks) |
| Execution state of an assistant run for a message |
| Diagnostic logs attached to messages |
| User-saved prompts (personal prompt library) |
| Translation functionality within chat |
| Briefings (scheduled/automated digest reports) |
Knowledge base
Resource | Description |
|---|---|
| Documents and files in the knowledge base |
| Folders organizing knowledge base content |
| Chunked and embedded segments of ingested documents |
| Knowledge base scopes containers that group content for access control |
| Access grants on scopes (who may use a scope) |
| The mapping between scopes and folders |
| Generic access grants on shared resources (e.g. shared chats or content) |
| The content ingestion pipeline (upload and processing of documents) |
| Metrics about ingestion runs |
| Company-level metadata used by ingestion |
Assistants and spaces
Resource | Description |
|---|---|
| AI assistants (spaces). Supports instance scoping: |
| The catalog of assistant types |
| The catalog of module types available to assistants |
| Reusable assistant templates |
| Modules configured within an assistant |
| Reusable module templates |
| System prompts configured on assistants |
| Tools available to assistants |
| Language model (LLM) configurations |
| Direct access to the platform's OpenAI-compatible completion endpoints |
Apps and integrations
Resource | Description |
|---|---|
| Registered applications (programmatic platform access) |
| API keys belonging to apps |
| Webhook endpoints registered by apps |
| Event subscriptions (which events an app receives) |
| App installations |
| Request logs of app/webhook traffic |
| MCP (Model Context Protocol) server integrations |
| Configuration of tools exposed via MCP connectors |
| Virtual MCP servers in the MCP Hub |
| Access grants on MCP Hub virtual servers |
| OAuth provider configurations for the MCP Hub |
| The ServiceNow integration |
Analytics and quality
Resource | Description |
|---|---|
| Analytics data (general) |
| Aggregated chat interaction analytics |
| Detailed per-interaction analytics |
| Statistics on knowledge base references in answers |
| Export of user chat data |
| Active-user analytics |
| Scheduled analytics report configuration |
| Insights dashboards |
| Usage statistics |
| Client-side metrics reporting |
| LLM usage (token consumption) data |
| Benchmarking runs for evaluating assistant quality |
| User feedback on assistant answers |
| NPS survey responses |
| Application logs |
Workflow features
Resource | Description |
|---|---|
| Due diligence questionnaire workflows |
| Agentic (magic) table workspaces |
| Stock market index data (finance features) |
Platform configuration
Resource | Description |
|---|---|
| UI theme configuration |
| Custom fonts for theming |
| User notifications |
| Admin-managed notification banners |
| Terms and conditions shown to users |
| Feature flag configuration |
System roles
The platform ships 23 built-in roles. Policies whose subject is a role ID apply to every user holding that role (via g rules). Roles can inherit other roles for example, Company_Admin inherits User_Management_Admin, KB_Admin, Space_Admin, Chat_Admin, Benchmarking_Admin, Feedback_Admin, Analytics_Admin, Apps_Admin, and Module_Template_Admin.
Role ID | Description |
|---|---|
| System administrator across a specific company (inherits most other admin roles) |
| Can create, change and delete users |
| Uses the chat can only access their own data |
| Chat user with debug read access to chunks |
| Administers chat functionality company-wide |
| Full administration of the knowledge base |
| Can read and write knowledge base folders and content |
| Can read and access knowledge base content |
| Knowledge base viewer with debug access |
| Can edit metadata of knowledge base content |
| Can create and configure spaces, grant access and delegate |
| Can create spaces |
| Can manage assistants via MANAGE access |
| Can create, update and delete module templates |
| Administers benchmarking |
| Can download benchmarking sheets and results |
| Can read, add and delete feedback |
| Can read feedback |
| Can request, configure and download analytics |
| Can create and delete apps |
| Can read app information |
| Can manage connectors |
| Can view connectors |
Custom roles created via the API get an ID prefixed with custom__ and behave like system roles: users are assigned via g rules, and the role's permissions are ordinary company-scoped p rules with the custom role ID as subject.
Worked examples
The examples below show policies as stored rows. Remember that when creating them via the API you omit the prefixes.
Role default: chat users may read their own chats
ptype: p
v0: subject:Chat_User (subject: the Chat_User role)
v1: company:* (applies in every tenant platform default)
v2: resource:chat (resource: chats)
v3: company:own (object must belong to the user's own company)
v4: subject:own (object must be owned by the requesting user)
v5: action:read (action: read)
v6: allowAny user holding Chat_User (directly or via inheritance) may read chats but only chats they own, within their own company.
Custom grant: one user may manage a specific assistant
ptype: p
v0: subject:usr_abc123 (a specific user)
v1: company:cmp_xyz789 (their company)
v2: resource:assistant:asst_def456 (one specific assistant)
v3: company:cmp_xyz789 (object company)
v4: subject:* (regardless of who owns the assistant)
v5: action:manage
v6: allowThis is the instance-scoped pattern: the resource targets a single assistant by ID rather than all assistants.
Deny exception: block a group from deleting content
ptype: p
v0: subject:group_interns (a group)
v1: company:cmp_xyz789
v2: resource:content
v3: company:own
v4: subject:*
v5: action:delete
v6: denyEven if members of group_interns hold a role that allows deleting content, this rule blocks it deny always wins over allow.
Role assignment (g rule)
ptype: g
v0: subject:usr_abc123 (the user)
v1: subject:Chat_User (the role)
v2: company:cmp_xyz789 (within this company)This makes example 1 apply to usr_abc123 in company cmp_xyz789.
Managing policies via the API
Policies are managed through the Gatekeeper GraphQL admin API. All admin operations require an authenticated user with the user-management admin permission, and they operate within the caller's own company.
The policy input maps one-to-one onto the p rule fields with plain values, no prefixes:
input PolicyParamsInput {
subject: String! # v0 user ID, role ID, or group_{id}
subjectCompany: String! # v1 company ID or "*"
resource: String! # v2 resource, optionally instance-scoped ("assistant:asst_xyz")
objectCompany: String! # v3 company ID, "own", or "*"
objectOwner: String! # v4 user ID, "own", "ownGroup", or "*"
action: String! # v5 see Actions
effect: PolicyEffect # v6 ALLOW (default) or DENY
}Key operations:
Operation | Type | Purpose |
|---|---|---|
| Mutation | Create one / multiple |
| Mutation | Delete one / multiple |
| Mutation | Replace an existing |
| Mutation | Create / delete |
| Mutation | Manage custom roles (a named bundle of |
| Query | Browse and filter existing policy rows |
| Query | Policy counts for the company |
| Query | List system and custom roles |
| Query | Inspect a user's effective permissions |
Grouping policy input for g rules:
input GroupingPolicyCreateParamsInput {
subject: String! # v0 the member (user ID or group_{id})
group: String! # v1 the role ID or parent group
company: String! # v2 company ID
}Practical guidance
Prefer roles over per-user policies. Create a custom role with the required
prules and assign users to it viagrules, instead of duplicatingprules per user.Scope tightly. Use
objectCompany: "own"unless you have a specific reason not to, and preferobjectOwner: "own"or"ownGroup"over"*"when the resource has meaningful ownership.Use
denysparingly. Adenyrule overrides everyallowthat matches the same request. Broad deny rules (wildcards) can lock users out in surprising ways.Removal requires an exact match.
adminRemovePolicydeletes the rule whose fields all match the input exactly it does not delete "all rules matching a pattern".Never touch
g2. Same-group relationships are maintained automatically by the platform.