Skip to content
BurnerByte

concepts

Roles & Permissions

BurnerByte's dynamic RBAC system with DB-backed roles and permissions.

Overview

BurnerByte uses a two-level, permission-based RBAC system. Every API endpoint checks a specific permission key from the role_permissions table in the database — there are no hardcoded rank checks. Roles and their permissions are stored in the roles, permissions, and role_permissions tables and can be managed via the admin API or Settings → Roles in the UI.

There are 5 default roles in total: 3 org-scoped (owner, admin, member) and 2 team-scoped (lead, member). is_system_admin is a separate boolean flag on the user row — it is not an RBAC role and does not appear in the roles tables (see System Admin below).

Org Roles

RoleRankDescription
owner3Full control over the organization, including deletion
admin2Manage members, domains, teams, and settings. Cannot delete the org
member1Read-only access to org resources. Access to teams they belong to

Team Roles

RoleRankDescription
lead2Full control of team settings, members, domain assignments, webhooks, API keys, and inboxes
member1Create and manage inboxes, view emails, plus all view permissions

Permission Keys

Org Permissions

KeyDescription
org.viewView organization info
org.settings.viewView organization settings
org.settings.manageEdit organization settings
org.deleteDelete the organization
org.members.viewView member list
org.members.inviteInvite new members
org.members.addDirectly add existing users
org.members.removeDeactivate members
org.members.roleChange member roles
org.domains.viewView domains list
org.domains.manageCreate, verify, delete domains
org.teams.createCreate teams
org.teams.deleteDelete teams
org.teams.manageArchive, restore, view impact
org.audit.viewView audit logs
org.audit.exportExport audit logs
org.analytics.viewView org analytics

Team Permissions

KeyDescription
team.viewView team info
team.settings.manageEdit team settings
team.members.viewView team members
team.members.manageAdd/remove team members
team.members.roleChange team member roles
team.domains.viewView domain assignments
team.domains.manageUpdate domain assignment settings
team.webhooks.viewView webhooks and delivery logs
team.webhooks.manageCreate/edit/delete webhooks
team.apikeys.viewView API keys
team.apikeys.manageCreate/revoke/rotate API keys
team.inboxes.viewView team inboxes
team.inboxes.createCreate inboxes
team.inboxes.manageManage inboxes
team.emails.viewView emails
team.emails.manageMark emails read/unread and delete emails
team.analytics.viewView team analytics

Default Role Assignments

Org Roles

PermissionOwnerAdminMember
org.view
org.settings.view
org.settings.manage
org.delete
org.members.view
org.members.invite
org.members.add
org.members.remove
org.members.role
org.domains.view
org.domains.manage
org.teams.create
org.teams.delete
org.teams.manage
org.audit.view
org.audit.export
org.analytics.view

Team Roles

PermissionLeadMember
team.view
team.settings.manage
team.members.view
team.members.manage
team.members.role
team.domains.view
team.domains.manage
team.webhooks.view
team.webhooks.manage
team.apikeys.view
team.apikeys.manage
team.inboxes.view
team.inboxes.create
team.inboxes.manage
team.emails.view
team.emails.manage
team.analytics.view

Enforcement

Every API endpoint checks a specific permission key via the checkOrgPermission or checkTeamPermission helpers. These look up the user's role, then query the in-memory permission cache to verify the role has the required key. No hardcoded rank comparisons are used for permission checks.

Self-Protection

Users cannot modify peers at the same or higher rank. Role change and deactivation endpoints use RequireRankAbove (org scope) or RequireTeamRankAbove (team scope) to enforce this — the acting user's rank must be strictly higher than the target's. System admins bypass the check.

Org-Level Fallback

Org admins and owners (rank ≥ 2) can access any team's resources in their own org without being an explicit team member — the check short-circuits on org rank before the team membership is consulted. RequireTeamPermission first confirms the {teamId} in the route actually belongs to the {orgId}, so an admin cannot pair their own org with another tenant's team.

Permission Cache

Permissions are loaded into an in-memory cache at startup from the role_permissions table. The cache is thread-safe (sync.RWMutex) and is refreshed when a role's permission set changes through the admin API (a create that supplies permissions, or a permission update). A role deletion does not currently trigger a refresh, so a deleted role stays cached until the API restarts.

Custom Roles

Admins can create custom roles with specific permissions via Settings → Roles or the admin API:

  • POST /api/v1/admin/roles — create a new role with scope, value, label, description, rank, and permissions
  • PATCH /api/v1/admin/roles/{roleId} — update label, description, or permissions
  • DELETE /api/v1/admin/roles/{roleId} — remove a custom role

Custom roles appear in the permission cache alongside system roles. Note that org_memberships.role and team_memberships.role still carry CHECK constraints limiting stored values to owner/admin/member and lead/member, so a custom role cannot yet be assigned to a member — custom roles currently only adjust the permission sets attached to the built-in role values.

System Admin

The is_system_admin flag on the user record bypasses all RBAC checks and grants access to all /api/v1/admin routes, including:

  • System stats and health checks
  • User management (list, edit, delete platform users)
  • Platform settings (registration, email verification, password policy, lockout)
  • SSO provider management
  • Role and permission management

This flag is set during the initial setup wizard for the platform owner.

Operating Without an Organization

System admins are exempt from the forced onboarding redirect. A system admin with no organization can still access the platform-level surfaces — Settings → Roles, Settings → SSO, Settings → System, and the platform audit log — without creating or joining an org. Org-scoped tabs (General, Users) and org-scoped nav items (domains, teams, analytics) appear only once an org exists.

Encryption at Rest

Sensitive configuration values (SSO client secrets, SMTP passwords) are encrypted with AES-256-GCM before storage in the system_configs table. To enable, set encryption.key to a 64-character hex string (32 bytes). Generate with:

bash
openssl rand -hex 32