# Rbac Design

> Role-based access control design with roles + granular permissions — role × domain matrix, N:N database modeling and an approval process before implementation. Use whenever the project involves permissions, user roles, access control, admin panels or "who can do what" questions in the system.

- Skill: `joaoequer/rbac-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add joaoequer/rbac-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/joaoequer/rbac-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: JoaoEquer (https://skillmd.com/u/joaoequer)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/joaoequer/rbac-design

---


# RBAC design

Badly designed RBAC is guaranteed rework. This is the process and the model that work.

## The model: roles + granular permissions (not pure roles)

Pure roles ("admin can do everything, editor can edit") break at the first real exception. The house model:

- **Permission** = atomic action on a domain (e.g. `task:create`, `document:approve`, `report:export`).
- **Role** = named set of permissions (N:N role ↔ permission).
- **User** receives roles (N:N user ↔ role), and the system resolves the effective permission set.

```prisma
model Role {
  id          String  @id @default(uuid())
  workspaceId String
  name        String
  permissions RolePermission[]
}

model Permission {
  id     String @id @default(uuid())
  domain String   // e.g. "task", "document", "report"
  action String   // e.g. "create", "read", "update", "approve", "delete"
  roles  RolePermission[]
  @@unique([domain, action])
}

model RolePermission {
  roleId       String
  permissionId String
  role         Role       @relation(fields: [roleId], references: [id])
  permission   Permission @relation(fields: [permissionId], references: [id])
  @@id([roleId, permissionId])
}
```

Server-side check via guard/decorator (`@RequirePermission('document:approve')`). The frontend uses permissions only to hide buttons — never as real control.

## The process: matrix before code

**Do not implement RBAC straight into code.** The correct flow:

1. **Map the domains** of the system (the "areas": tasks, documents, users, reports, settings...). Medium systems have 8–15 domains.
2. **Map the real roles** of the client's operation (not the ones you imagine). Usually 5–8 roles are enough; more than that smells like roles that should be standalone permissions.
3. **Build the role × domain matrix** in a readable document (table: rows = roles, columns = domains, cells = allowed actions).
4. **Submit it for approval** by the architect/tech lead and, when it makes sense, the client. The matrix is cheap to change; code and migrations are not.
5. **Only after approval**, implement — and if implementing into an existing system, verify the real codebase first (do not implement against an imagined codebase).

## Known traps

- **Implicit permission**: an undocumented "everyone can read" becomes a security hole when requirements change. Every permission is explicit in the matrix.
- **The ever-growing God role**: the "admin" role that accumulates everything. Acceptable, but record that it is a super-user by design.
- **RBAC without tenant**: in a multi-tenant system, roles and assignments belong to the workspace (`workspaceId` on `Role`). Global roles only with a recorded decision.
- **Premature granularity**: start with standard actions (create/read/update/delete + the 2–3 domain-specific business actions, like "approve"). Do not invent 40 actions per domain on day 1.

