Skip to content

Roles & Permissions

Pulsabase offers a built-in Role-Based Access Control (RBAC) system. Roles and permissions are created through the Auth SDK (or the Dashboard), attached to users, and then automatically encoded into the user’s JWT so your database Row-Level Security (RLS) policies can enforce them at the database level.

ConceptDescription
RoleA named label assigned to users (e.g. admin, moderator)
PermissionA fine-grained capability key (e.g. rooms.create, invoices.delete)
Role ↔ PermissionRoles contain a set of permissions
User ↔ RoleUsers are assigned one or more roles

Roles and permissions are created via the SDK from an admin context (a user with an admin role). You typically run this once in a setup script or admin panel.

// 1. Create fine-grained permissions
await pb.auth.createPermission('rooms.create', 'Can create new rooms');
await pb.auth.createPermission('messages.delete', 'Can delete any message');
await pb.auth.createPermission('users.manage', 'Can manage users and roles');
// 2. Create roles
await pb.auth.createRole('admin', 'Full platform access');
await pb.auth.createRole('moderator', 'Can moderate content');
await pb.auth.createRole('member', 'Standard access level');
// 3. Assign permissions to roles
await pb.auth.setRolePermissions('admin', [
'rooms.create',
'messages.delete',
'users.manage',
]);
await pb.auth.setRolePermissions('moderator', ['messages.delete']);

Once roles exist, you can assign them to users. Roles are included in the user’s next JWT as user.roles.

// Assign the 'moderator' role to a specific user (requires admin)
await pb.auth.assignRoles(targetUserId, ['moderator']);
// Inspect the current user's roles from the local JWT (no network call)
const myRoles = pb.auth.getRoles(); // ['member']
const myPerms = pb.auth.getPermissions(); // ['messages.delete']
// Check roles inline (useful for conditional rendering)
if (pb.auth.hasRole('admin')) {
showAdminPanel();
}
if (pb.auth.hasPermission('rooms.create')) {
showCreateRoomButton();
}
// List all roles in the project
const allRoles = await pb.auth.listAllRoles();
// List all permissions
const allPerms = await pb.auth.listAllPermissions();
// Get permissions for a specific role
const moderatorPerms = await pb.auth.getRolePermissions('moderator');
// Get a specific user's effective permissions
const userPerms = await pb.auth.getUserPermissions(targetUserId);
// Update a user's info or roles
await pb.auth.updateUser(targetUserId, {
roles: ['admin', 'member'],
email_verified: true,
});

Roles and permissions are deeply integrated with your database via Row-Level Security (RLS). When a user makes a database request, their JWT is verified and its claims are injected into the PostgreSQL session, where RLS policies can enforce access.

import { ModelSchema, primary, text } from 'pulsabase';
export default class Room {
@primary id: string;
@text(false) name: string;
static schema: ModelSchema = {
tableName: 'rooms',
policies: [
// Any authenticated user can read rooms
{ action: ['SELECT'], authenticated: true },
// Only users with the 'rooms.create' permission can insert
{ action: ['INSERT'], permission: 'rooms.create' },
// Only admins can delete rooms
{ action: ['DELETE'], role: 'admin' },
// Moderators and admins can update rooms
{ action: ['UPDATE'], role: ['admin', 'moderator'] },
]
};
}

When a user calls pb.from(Room).insert(...), PostgreSQL extracts their JWT from the session context, checks permissions @> 'rooms.create', and enforces the policy.

Full user management is available for admin users:

// List all users in the project
const users = await pb.auth.listUsers();
// Get a specific user by ID
const user = await pb.auth.getUserById(userId);
// Set user-specific permission overrides
await pb.auth.setUserPermissions(userId,
['billing.read'], // allowed permissions
['billing.write'], // denied permissions (overrides role permissions)
);

[!NOTE] Because JWT claims cannot be invalidated mid-session, role and permission changes only take effect when a user’s token is next refreshed (typically within 60 seconds by default) or when they sign in again.