Skip to content

Permissions

Zatabase enforces permissions at every API boundary using an organization-scoped role-based access control system. Every authenticated request carries a set of principals (user, roles, groups, labels) that are checked against a permission table before the operation proceeds.

A permission grant is a tuple of (principal, resource_kind, action) scoped to a single organization. The system evaluates whether any of the request’s principals have been granted the required action on the target resource kind.

TypeDescription
UserA single authenticated user identified by user ID
RoleA named role (e.g., admin, operator, reader) assigned to one or more users
GroupA group of users with shared permissions
LabelA tag-based principal for flexible, dynamic access control
ResourceDescription
CollectionsNoSQL document collections
TablesRelational SQL tables
IndexesHNSW and secondary indexes
JobsCompute jobs and artifacts
FilesUploaded files and artifacts
PermissionsThe permission rules themselves

Each grant specifies one of: Create, Read, Update, or Delete.

Zatabase ships with three built-in roles that cover common access patterns:

RoleCollectionsTablesIndexesJobsFilesPermissions
adminCRUDCRUDCRUDCRUDCRUDCRUD
operatorCRUDCRUDCRUDCRUDCRUDR
readerRRRRR

The admin role has full access to everything, including the ability to manage permissions. The operator role can read and write all data but cannot modify permission rules. The reader role is read-only across all resource types.

See what the current session is allowed to do:

Terminal window
curl -s https://your-project.zatabase.io/v1/permissions/effective \
-H "Authorization: Bearer $ZATABASE_TOKEN" | jq

Grant a role the ability to read tables:

Terminal window
curl -s -X POST https://your-project.zatabase.io/v1/permissions \
-H "Authorization: Bearer $ZATABASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"principal": {"Role": "analyst"},
"resource": "Tables",
"action": "Read"
}' | jq
Terminal window
curl -s -X DELETE https://your-project.zatabase.io/v1/permissions/{permission_id} \
-H "Authorization: Bearer $ZATABASE_TOKEN"
Terminal window
curl -s https://your-project.zatabase.io/v1/permissions \
-H "Authorization: Bearer $ZATABASE_TOKEN" | jq

Permission checks are applied by every handler in zserver before executing the requested operation. The flow is:

  1. The JWT token is decoded to extract the user ID, org ID, roles, groups, and labels.
  2. A list of principals is constructed from these claims.
  3. The handler calls ensure_permission(org_id, principals, resource_kind, action).
  4. If any principal has a matching grant, the operation proceeds. If none match, a 403 Forbidden response is returned.

This check happens on every request, including SQL queries over the PG wire protocol and operations over WebRTC data channels.

Permissions are always scoped to a single organization. A user with admin in organization A has no access to organization B unless explicitly granted. Storage is physically isolated per organization, so even a bug in the permission layer cannot leak data across tenants.