User permissions
Satia enforces a unified Default-Deny / Pure Whitelisting security model across the entire platform. This guarantees that no human user (whether operating in the Web UI, the Satia Mobile App, chatting with Sati, or connecting via a Remote MCP token) can read or modify tables outside their role's granted permissions.
1. Workspace User Profiles (Owner, Admin, and Staff)
Within each Workspace, team members listed in <internal_team_directory> hold one of three roles:
-
Workspace Owner (
Owner) and Administrator (Admin):- The workspace creator (
'owner') or a team member promoted to administrator ('admin'). - Hold universal access (
is_owner = True) across all business tables in the workspace and all operational platform modules. - Important when auditing
user_tables: NeitherOwnernorAdminhas rows in theuser_tablestable. If you queryuser_tablesfor anOwnerorAdminand it returns0rows, that does NOT mean they lack permissions: their role automatically grants full workspace access without requiring rows inuser_tables. - Hold exclusive Read-Only (
SELECT) access touser_tablesin order to audit the permissions assigned to eachStaffmember.
- The workspace creator (
-
Team Member (
Staff):- An invited team member with role
'staff'. - Operates under Default-Deny (Fail-Closed): when a
Staffmember is invited to the workspace, they start with 0 rows inuser_tables, meaning they have zero access to any business table or system module until anOwnerorAdminassigns permissions in Settings > Team (/settings/team,Accesos/ Permissions tab). - When an administrator sets a permission to
"No Access" (none)in the UI, the corresponding row is physically deleted (DELETE) fromuser_tables. Therefore, the absence of a row inuser_tablesfor aStaffmember means No Access. - For any
Staffuser, theuser_tablestable itself is completely invisible and inaccessible ("Table 'user_tables' does not exist in the database.").
- An invited team member with role
2. Structure of the user_tables Table
Granular permissions for each Staff member are stored in user_tables. Each row grants access either to one specific business table or to one platform module (never both in the same row):
| Column | Type | Purpose |
|---|---|---|
id | uuid | Unique permission record ID. |
contact_id | uuid | Team member's UUID (matches the staff member's ID in <internal_team_directory>). |
entity_definition_id | uuid (nullable) | UUID of the business table to which access is granted (e.g., clientes, pedidos, inventario, listed via database__describe_tables()). When set, resource_slug is NULL. |
resource_slug | text (nullable) | System module slug when the permission applies to a platform feature. When set, entity_definition_id is NULL. |
permission | text | Granted access level: 'read' (read-only) or 'write' / 'full' (read and write). |
Business Table Permissions (entity_definition_id):
- When a
Staffmember is granted'read'or'write'on a business table, the permission engine automatically enables the auxiliary tables required to operate that business table at the same access level:tags(record tags for that table).- For order, booking, or appointment tables: also enables
event_items(cart line items),schedules, andschedule_rules(availability calendars).
Complete Catalog of Platform Module Slugs (resource_slug):
When a Staff member is granted 'read' or 'write' on a resource_slug, only the tables and tools associated with that module are enabled:
resource_slug | Enabled Tables & Capabilities | Access Notes |
|---|---|---|
'sati' (or 'agents') | agents, agent_tables, agent_docs | Configuration of transactional assistants, their table permissions, and linked docs. |
'conversations' | conversations, messages, channels | messages is always read-only in SQL. |
'automations' | automation_scripts, agent_tools, integration_connectors, oauth_connections, automation_secrets | integration_connectors and automation_secrets are always read-only in SQL. |
'tasks' | agent_tasks | Human task inbox, scheduled reminders, and escalation tickets. |
'knowledge' (or 'docs') | knowledge_docs, knowledge_folders, agent_docs | Knowledge Base (Satia Docs) and folder hierarchy. |
'custom_views' | custom_views, shared_view_links | Custom Views (Micro-Apps/Dashboards) and shared public links. |
'wallet' (or 'billing') | script_executions, browser_executions + tools billing__get_account_summary and billing__get_consumption_dossier | All consumption and billing tables are strictly read-only. |
'settings' | platform_audit_logs, oauth_connections, automation_secrets | Audit logs, OAuth connections, and secret keys inventory. |
3. Read-Only System Tables & Secret Protection
Even when a user is an Owner or Admin (or a Staff member with 'write' access on the corresponding module), the platform protects system integrity by blocking direct SQL mutations (insert, update, delete) on critical governance and telemetry tables:
| System Table | SQL Read (database__query) | SQL Write (insert/update/delete) | How It Is Managed |
|---|---|---|---|
user_tables | Owner and Admin only (SELECT) | Blocked (Read-only) | Managed exclusively by Owner / Admin in the Web UI at Settings > Team (/settings/team, Permissions / Accesos tab). |
automation_secrets | SELECT (value masked) | Blocked in SQL | Declare in required_secrets of automation__save_script (or via automations__request_secret_key); the user enters the secret value in the Automations (/automations) page. In SQL, value always returns "[CONFIGURED]" or "[EMPTY - PENDING USER INPUT]". |
oauth_connections | SELECT (tokens redacted) | Allowed per module | On any SELECT, access_token and refresh_token are automatically masked as "[REDACTED_SECRET]". |
platform_audit_logs | SELECT | Blocked (Immutable) | The system records every mutation automatically; users can click Restore in the web audit timeline. |
script_executions | SELECT | Blocked (Immutable) | Written automatically by the automation worker (visible in /automations). |
browser_executions | SELECT | Blocked (Immutable) | Written automatically by the headless browser runtime. |
wallet_transactions | Blocked in direct SQL | Blocked (Immutable) | Query exclusively via billing__get_account_summary(transactions_limit=...). |
store_tools / integration_connectors | SELECT | Blocked (Global catalog) | Read-only global platform catalogs. |
messages | SELECT | Blocked in SQL | Messages are sent through messaging tools or active channels. |
Standard Permission Denial Responses:
- Table outside the user's readable scope: Returns
"Table '<name>' does not exist in the database."(stealth mode to avoid leaking resource existence to unauthorized users). - Write attempt on a read-only table or with
'read'permission: Returns"Permission denied: Table '<name>' is read-only under current permissions. Do not retry with other tools."
4. Permission Intersection Model (Effective Scope)
Whenever Sati, the Data Modeler (schema_builder), or a Remote MCP (mcp) connection executes a query or mutation, the security engine computes the Effective Permission Scope as the intersection between the agent role's capabilities and the authenticated human user's RBAC permissions:
Effective Permission = (Agent Role Capability ∩ Human User RBAC Permissions) \ Forbidden System Tables
Real-Time Security Guarantees:
- Strict Context Inheritance (Confused Deputy Protection):
- If a
Staffmember with Read-Only ('read') access onpedidosand no access toautomationsconnects via MCP or asks Sati to modify an order or delegate a schema change toschema_builder, the operation is immediately rejected because Sati,schema_builder, and the MCP server all inherit the exact security context of thatStaffmember.
- If a
- Real-Time SQL Lexical Inspector:
- Every SQL query (
SELECT,WITH,JOIN, subqueries) is inspected before execution to verify that 100% of referenced tables belong to the user's allowed readable scope.
- Every SQL query (
5. Permission Auditing Recipes (For Owner / Admin)
When a Workspace Owner or Administrator asks "What permissions does Juan have?" or "Who has access to the automations module?":
- Identify the team member's
contact_idand role in the<internal_team_directory>block of your context:- If the member has role
OwnerorAdmin, inform the user directly that they hold full access to all business tables and platform modules (they will have 0 rows inuser_tables).
- If the member has role
- If the member is
Staff, queryuser_tableswithdatabase__query:SELECT contact_id, entity_definition_id, resource_slug, permission FROM user_tables WHERE contact_id = 'STAFF_MEMBER_UUID' ORDER BY resource_slug NULLS FIRST - Cross-reference business table IDs with the workspace's business tables:
- Use the business tables catalog (
database__describe_tables()) to translate eachentity_definition_idto the friendly business table name (e.g.,Clientes,Pedidos) and present a clear summary showing which tables/modules have Read (read), which have Read & Write (write), and which are not assigned. - If the user wants to change a staff member's permissions, direct them to Settings > Team (
/settings/team, Permissions /Accesostab) in the web platform.
- Use the business tables catalog (