Skip to main content

Overview

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

RoleDescription
AdminsFull access to everything (sinas.*:all)
UsersCreate and manage own resources, chat with any agent, execute own functions
GuestUsersRead and update own profile only

Permission Format

<service>.<resource>[/<path>].<action>:<scope>
Components:
PartDescriptionExamples
ServiceTop-level namespacesinas, or a custom prefix like titan, acme
ResourceResource typeagents, functions, states, users
PathOptional namespace/name path/marketing/send_email, /*
ActionWhat operation is allowedcreate, read, update, delete, execute, chat
ScopeOwnership 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:
PatternMatches
sinas.*:allEverything in Sinas (admin access)
sinas.agents/*/*.chat:allChat with any agent in any namespace
sinas.functions/marketing/*.execute:ownExecute any function in the marketing namespace
sinas.states/*.read:ownRead own states in any namespace
sinas.chats.*:ownAll 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 with specific agent
sinas.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 webhooks
sinas.schedules.read:own                         # Read own schedules
sinas.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:
titan.student_profile.read:own
titan.courses/math/*.enroll:own
acme.billing.invoices.read:all
myapp.*:all
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:
curl -X POST https://yourdomain.com/auth/check-permissions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "permissions": ["titan.student_profile.read:own", "titan.courses.enroll:own"],
    "logic": "AND"
  }'
Response:
{
  "result": true,
  "logic": "AND",
  "checks": [
    {"permission": "titan.student_profile.read:own", "has_permission": true},
    {"permission": "titan.courses.enroll:own", "has_permission": true}
  ]
}
  • 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 role
GET    /api/v1/roles                        # List roles
GET    /api/v1/roles/{name}                 # Get role details
PATCH  /api/v1/roles/{name}                 # Update role
DELETE /api/v1/roles/{name}                 # Delete role
POST   /api/v1/roles/{name}/members         # Add user to role
DELETE /api/v1/roles/{name}/members/{id}    # Remove user from role
POST   /api/v1/roles/{name}/permissions     # Set permission
DELETE /api/v1/roles/{name}/permissions     # Remove permission
GET    /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:
  1. Agent declares the tool — the resource is in the agent’s enabledFunctions, enabledQueries, enabledStores, enabledCollections, or enabledAgents
  2. 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.
ResourceAgent configPermission checked at execution
FunctionsenabledFunctionssinas.functions/{ns}/{name}.execute:own
QueriesenabledQueriessinas.queries/{ns}/{name}.execute:own
CollectionsenabledCollectionssinas.collections/{ns}/{name}.download:own (read) / .upload:own (write)
StoresenabledStoressinas.stores/{ns}/{name}.read_state:own / .write_state:own
Sub-agentsenabledAgentssinas.agents/{ns}/{name}.chat:all
ConnectorsenabledConnectorsAgent-gated only (no standalone execution path)
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.