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:

  1. 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: Neither Owner nor Admin has rows in the user_tables table. If you query user_tables for an Owner or Admin and it returns 0 rows, that does NOT mean they lack permissions: their role automatically grants full workspace access without requiring rows in user_tables.
    • Hold exclusive Read-Only (SELECT) access to user_tables in order to audit the permissions assigned to each Staff member.
  2. Team Member (Staff):

    • An invited team member with role 'staff'.
    • Operates under Default-Deny (Fail-Closed): when a Staff member is invited to the workspace, they start with 0 rows in user_tables, meaning they have zero access to any business table or system module until an Owner or Admin assigns 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) from user_tables. Therefore, the absence of a row in user_tables for a Staff member means No Access.
    • For any Staff user, the user_tables table itself is completely invisible and inaccessible ("Table 'user_tables' does not exist in the database.").

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):

ColumnTypePurpose
iduuidUnique permission record ID.
contact_iduuidTeam member's UUID (matches the staff member's ID in <internal_team_directory>).
entity_definition_iduuid (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_slugtext (nullable)System module slug when the permission applies to a platform feature. When set, entity_definition_id is NULL.
permissiontextGranted access level: 'read' (read-only) or 'write' / 'full' (read and write).

Business Table Permissions (entity_definition_id):

  • When a Staff member 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, and schedule_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_slugEnabled Tables & CapabilitiesAccess Notes
'sati' (or 'agents')agents, agent_tables, agent_docsConfiguration of transactional assistants, their table permissions, and linked docs.
'conversations'conversations, messages, channelsmessages is always read-only in SQL.
'automations'automation_scripts, agent_tools, integration_connectors, oauth_connections, automation_secretsintegration_connectors and automation_secrets are always read-only in SQL.
'tasks'agent_tasksHuman task inbox, scheduled reminders, and escalation tickets.
'knowledge' (or 'docs')knowledge_docs, knowledge_folders, agent_docsKnowledge Base (Satia Docs) and folder hierarchy.
'custom_views'custom_views, shared_view_linksCustom Views (Micro-Apps/Dashboards) and shared public links.
'wallet' (or 'billing')script_executions, browser_executions + tools billing__get_account_summary and billing__get_consumption_dossierAll consumption and billing tables are strictly read-only.
'settings'platform_audit_logs, oauth_connections, automation_secretsAudit 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 TableSQL Read (database__query)SQL Write (insert/update/delete)How It Is Managed
user_tablesOwner 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_secretsSELECT (value masked)Blocked in SQLDeclare 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_connectionsSELECT (tokens redacted)Allowed per moduleOn any SELECT, access_token and refresh_token are automatically masked as "[REDACTED_SECRET]".
platform_audit_logsSELECTBlocked (Immutable)The system records every mutation automatically; users can click Restore in the web audit timeline.
script_executionsSELECTBlocked (Immutable)Written automatically by the automation worker (visible in /automations).
browser_executionsSELECTBlocked (Immutable)Written automatically by the headless browser runtime.
wallet_transactionsBlocked in direct SQLBlocked (Immutable)Query exclusively via billing__get_account_summary(transactions_limit=...).
store_tools / integration_connectorsSELECTBlocked (Global catalog)Read-only global platform catalogs.
messagesSELECTBlocked in SQLMessages 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:

  1. Strict Context Inheritance (Confused Deputy Protection):
    • If a Staff member with Read-Only ('read') access on pedidos and no access to automations connects via MCP or asks Sati to modify an order or delegate a schema change to schema_builder, the operation is immediately rejected because Sati, schema_builder, and the MCP server all inherit the exact security context of that Staff member.
  2. 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.

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?":

  1. Identify the team member's contact_id and role in the <internal_team_directory> block of your context:
    • If the member has role Owner or Admin, inform the user directly that they hold full access to all business tables and platform modules (they will have 0 rows in user_tables).
  2. If the member is Staff, query user_tables with database__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
    
  3. Cross-reference business table IDs with the workspace's business tables:
    • Use the business tables catalog (database__describe_tables()) to translate each entity_definition_id to 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 / Accesos tab) in the web platform.