Users are assigned to roles, and roles define permissions. A user's effective permissions are the union of all permissions from all their roles (OR logic). Permissions are loaded from the database on every request — changes take effect immediately.
Default Roles
Role
Description
Admins
Full access to everything (sinas.*:all)
Users
Create and manage own resources, chat with any agent, execute own functions
GuestUsers
Read and update own profile only
Permission Format
<service>.<resource>[/<path>].<action>:<scope>
<service>.<resource>[/<path>].<action>:<scope>
<service>.<resource>[/<path>].<action>:<scope>
Components:
Part
Description
Examples
Service
Top-level namespace
sinas, or a custom prefix like titan, acme
Resource
Resource type
agents, functions, states, users
Path
Optional namespace/name path
/marketing/send_email, /*
Action
What operation is allowed
create, read, update, delete, execute, chat
Scope
Ownership scope
:own (user's resources), :all (all resources)
Permission Matching Rules
Scope hierarchy::all automatically grants :own. A user with sinas.agents.read:all passes any check for sinas.agents.read:own.
Wildcards can be used at any level:
Pattern
Matches
sinas.*:all
Everything in Sinas (admin access)
sinas.agents/*/*.chat:all
Chat with any agent in any namespace
sinas.functions/marketing/*.execute:own
Execute any function in the marketing namespace
sinas.states/*.read:own
Read own states in any namespace
sinas.chats.*:own
All chat actions (read, update, delete) on own chats
Namespaced resource permissions use slashes in the resource path:
sinas.agents/support/ticket-bot.chat:own #Chat withspecific agentsinas.functions/*/send_email.execute:own # Execute send_email in any namespace
sinas.states/api_keys.read:all # Read all shared states in api_keys namespace
sinas.agents/support/ticket-bot.chat:own #Chat withspecific agentsinas.functions/*/send_email.execute:own # Execute send_email in any namespace
sinas.states/api_keys.read:all # Read all shared states in api_keys namespace
sinas.agents/support/ticket-bot.chat:own #Chat withspecific agentsinas.functions/*/send_email.execute:own # Execute send_email in any namespace
sinas.states/api_keys.read:all # Read all shared states in api_keys namespace
Non-namespaced resource permissions use simple dot notation:
sinas.webhooks.create:own #Create webhookssinas.schedules.read:own #Read own schedulessinas.users.update:own #Update own profile
sinas.webhooks.create:own #Create webhookssinas.schedules.read:own #Read own schedulessinas.users.update:own #Update own profile
sinas.webhooks.create:own #Create webhookssinas.schedules.read:own #Read own schedulessinas.users.update:own #Update own profile
Custom Permissions
The permission system is not limited to sinas.*. You can define permissions with any service prefix for your own applications:
These work identically to built-in permissions — same wildcard matching, same scope hierarchy. This lets you use Sinas as the authorization backend for external applications.
Checking Permissions from External Services
Use the POST /auth/check-permissions endpoint to verify whether the current user (identified by their Bearer token or API key) has specific permissions:
logic: "AND" — User must have ALL listed permissions (default)
logic: "OR" — User must have AT LEAST ONE of the listed permissions
This makes Sinas usable as a centralized authorization service for any number of external applications.
Managing Roles
POST /api/v1/roles #Create roleGET /api/v1/roles #List rolesGET /api/v1/roles/{name} # Get role detailsPATCH /api/v1/roles/{name} # Update roleDELETE /api/v1/roles/{name} # Delete rolePOST /api/v1/roles/{name}/members #Add user to roleDELETE /api/v1/roles/{name}/members/{id} # Remove user from rolePOST /api/v1/roles/{name}/permissions #Set permissionDELETE /api/v1/roles/{name}/permissions #Remove permissionGET /api/v1/permissions/reference #List all known permissions
POST /api/v1/roles #Create roleGET /api/v1/roles #List rolesGET /api/v1/roles/{name} # Get role detailsPATCH /api/v1/roles/{name} # Update roleDELETE /api/v1/roles/{name} # Delete rolePOST /api/v1/roles/{name}/members #Add user to roleDELETE /api/v1/roles/{name}/members/{id} # Remove user from rolePOST /api/v1/roles/{name}/permissions #Set permissionDELETE /api/v1/roles/{name}/permissions #Remove permissionGET /api/v1/permissions/reference #List all known permissions
POST /api/v1/roles #Create roleGET /api/v1/roles #List rolesGET /api/v1/roles/{name} # Get role detailsPATCH /api/v1/roles/{name} # Update roleDELETE /api/v1/roles/{name} # Delete rolePOST /api/v1/roles/{name}/members #Add user to roleDELETE /api/v1/roles/{name}/members/{id} # Remove user from rolePOST /api/v1/roles/{name}/permissions #Set permissionDELETE /api/v1/roles/{name}/permissions #Remove permissionGET /api/v1/permissions/reference #List all known permissions
Agent Execution & Permissions
Agents define which tools are available (via enabled_* fields), but the user's role permissions are checked at execution time. Both conditions must be met:
Agent declares the tool — the resource is in the agent's enabledFunctions, enabledQueries, enabledStores, enabledCollections, or enabledAgents
User has the permission — checked when the tool is actually called
If the user lacks permission, the tool call returns an error that the LLM can explain to the user. Tools are still visible to the LLM regardless — the check happens at execution, not discovery.
Connectors are the exception — they have no independent API and can only be accessed through an agent, so the agent's enabledConnectors (with per-operation filtering) is the sole access control.
Namespace wildcards make this manageable. Grant sinas.functions/sales/*.execute:own once on a role, and all functions in the sales/ namespace are accessible.