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.
Permission Model
Section titled “Permission Model”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.
Principals
Section titled “Principals”| Type | Description |
|---|---|
User | A single authenticated user identified by user ID |
Role | A named role (e.g., admin, operator, reader) assigned to one or more users |
Group | A group of users with shared permissions |
Label | A tag-based principal for flexible, dynamic access control |
Resource Kinds
Section titled “Resource Kinds”| Resource | Description |
|---|---|
Collections | NoSQL document collections |
Tables | Relational SQL tables |
Indexes | HNSW and secondary indexes |
Jobs | Compute jobs and artifacts |
Files | Uploaded files and artifacts |
Permissions | The permission rules themselves |
Actions (CRUD)
Section titled “Actions (CRUD)”Each grant specifies one of: Create, Read, Update, or Delete.
Built-In Roles
Section titled “Built-In Roles”Zatabase ships with three built-in roles that cover common access patterns:
| Role | Collections | Tables | Indexes | Jobs | Files | Permissions |
|---|---|---|---|---|---|---|
admin | CRUD | CRUD | CRUD | CRUD | CRUD | CRUD |
operator | CRUD | CRUD | CRUD | CRUD | CRUD | R |
reader | R | R | R | R | R | — |
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.
Managing Permissions
Section titled “Managing Permissions”List Effective Permissions
Section titled “List Effective Permissions”See what the current session is allowed to do:
curl -s https://your-project.zatabase.io/v1/permissions/effective \ -H "Authorization: Bearer $ZATABASE_TOKEN" | jqGrant a Permission
Section titled “Grant a Permission”Grant a role the ability to read tables:
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" }' | jqRevoke a Permission
Section titled “Revoke a Permission”curl -s -X DELETE https://your-project.zatabase.io/v1/permissions/{permission_id} \ -H "Authorization: Bearer $ZATABASE_TOKEN"List All Grants
Section titled “List All Grants”curl -s https://your-project.zatabase.io/v1/permissions \ -H "Authorization: Bearer $ZATABASE_TOKEN" | jqHow Enforcement Works
Section titled “How Enforcement Works”Permission checks are applied by every handler in zserver before executing the requested operation. The flow is:
- The JWT token is decoded to extract the user ID, org ID, roles, groups, and labels.
- A list of principals is constructed from these claims.
- The handler calls
ensure_permission(org_id, principals, resource_kind, action). - 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.
Multi-Tenancy Isolation
Section titled “Multi-Tenancy Isolation”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.