Roles & Permissions
Shelf has four organization roles. Every member of a workspace holds at least one; the few who hold several are judged by the most privileged (see Precedence below).
| Role | In plain language |
|---|---|
| Owner | Created (or was handed) the workspace. Everything an Administrator can do, plus billing, add-ons, SSO/SCIM settings and transferring ownership. Exactly one per workspace. |
| Administrator | Runs the workspace: catalogue, settings, team, every booking and every custody assignment. Only the Owner can grant, change or revoke it. |
| Self service | Books equipment and takes custody for themselves. Sees and changes their own bookings; sees others' bookings or custody only when the workspace allows it. |
| Base | Requests bookings for themselves. Cannot take custody. Sees others' bookings or custody only when the workspace allows it. |
How a decision is made
Two questions, two resolvers, both in @shelf/permissions:
| Question | Answered by |
|---|---|
| May this role do action on entity? | the permission matrix: userHasPermission (client), requirePermission / requireMobilePermission (server). A member holding several roles gets the union of their grants; Owner and Administrator are allowed everything. |
| How far does this role reach? Whose bookings, whose custody, which audits, which limits, who hears about what? | the role policy table ROLE_POLICIES, read through resolveRoleAccess to a RoleAccess object. It reads the member's single highest-rank role and folds in the workspace's visibility toggles. |
Where to get it:
- Web loaders and actions:
const { access } = await requirePermission(…). - Mobile API:
const { access } = await getMobileUserContext(userId, organizationId). - Web components:
useRoleAccess()for reach,useOrganizationRoles()+userHasPermissionfor the matrix. - Companion:
useRoleAccess()(apps/companion/hooks/use-role-access.ts) from the roles and toggles/api/mobile/mereturns. - Owner-only actions:
isWorkspaceOwner(roles)/access.ownsWorkspace. - Prisma audiences:
roles: { hasSome: rolesWhere((p) => p.notifications.orgBookingBroadcasts) }.
Never compare role names in app code. The lint rule local-rules/no-direct-role-checks refuses it in the webapp and the companion: a comparison decides for the roles it names and guesses for the rest.
Workspace visibility toggles
Settings → Bookings has four switches: Self service can see bookings, Base can see bookings, Self service can see custody, Base can see custody. Each widens what one role may see. None widens what it may change.
Effective access
Rendered from the characterization fixture (apps/webapp/app/utils/permissions/__snapshots__/effective-access.json). Do not edit by hand: run pnpm --filter @shelf/webapp docs:roles.
Precedence (a member holding several roles is judged by the highest): Owner > Administrator > Self service > Base.
Reach
| Owner | Administrator | Self service | Base | |
|---|---|---|---|---|
| Sees every booking (toggles off / on) | yes / yes | yes / yes | no / yes | no / yes |
| Sees every custodian (toggles off / on) | yes / yes | yes / yes | no / yes | no / yes |
| Removes booking items in | DRAFT, RESERVED, ONGOING, OVERDUE | DRAFT, RESERVED, ONGOING, OVERDUE | DRAFT, RESERVED | DRAFT |
| Default asset index | ADVANCED | ADVANCED | SIMPLE | SIMPLE |
Permission matrix
| Entity | Owner | Administrator | Self service | Base |
|---|---|---|---|---|
asset | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read, custody | read |
assetIndexSettings | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read | read |
assetModel | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | read |
assetReminders | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
audit | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read, update | read, update |
auditNote | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read | create, read |
booking | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, archive, cancel, extend, manage-assets, manage-kits | create, read, update, delete, export, manage-assets, manage-kits |
bookingNote | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read | create, read |
category | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
command-palette-search | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read | read |
custody | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
customField | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
dashboard | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
emailSettings | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
generalSettings | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
kit | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read, custody | read |
location | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
locationNote | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
nonRegisteredMember | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
note | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
qr | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read | read |
reports | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
scan | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
subscription | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
tag | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
teamMember | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
teamMemberNote | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
teamMemberProfile | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
update | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read | read |
user-data | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read, update | read, update |
workingHours | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | read | read |
workspace | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | create, read, update, delete, checkout, checkin, export, import, archive, cancel, extend, manage-assets, custody, manage-kits, change-role | None | None |
Adding a role
- Add the value to the
OrganizationRolesenum inpackages/database/prisma/schema.prismaand write the migration (ALTER TYPE … ADD VALUE, alone in its file). - Add it to
ORGANIZATION_ROLESinpackages/permissions/src/roles.ts; the parity check forces both sides to match. - Add its row to
Role2PermissionMap(matrix.ts),ROLE_POLICIES(policies.ts) andROLE_LABELS(roles.ts). All three are total records: the build fails until each has an entry. - If an SSO group can confer it, add an
SsoDetailscolumn and its entry inSSO_GROUP_ROLE(apps/webapp/app/utils/sso-group-roles.ts). - Regenerate the fixture and this page, and review every changed row.
