Trovella Wiki

Role Permission Matrix

Complete matrix of what each role can do with each subject, including personal org overrides and the unknown-role fallback.

This page documents the complete permission matrix derived from defineAbilityFor() in packages/api/src/abilities/define-ability.ts.

Roles

Trovella has three defined roles plus a safe default:

RoleDescription
OwnerFull control. One per organization. Cannot be demoted via the API.
AdminManages the organization, its members, and invitations. Cannot delete the organization.
MemberReads org metadata, creates and manages research plans and artifacts. No member management.
DefaultFallback for unknown/corrupted role values. Read-only access to org and member data.

Company and Family Organizations

SubjectActionOwnerAdminMemberDefault
OrganizationreadYesYesYesYes
OrganizationupdateYesYes----
OrganizationdeleteYes------
MemberreadYesYesYesYes
MembercreateYesYes----
MemberupdateYesYes----
MemberdeleteYesYes----
InvitationreadYesYesYes--
InvitationcreateYesYes----
InvitationupdateYesYes----
InvitationdeleteYesYes----
ResearchPlanreadYesYesYes--
ResearchPlancreateYesYesYes--
ResearchPlanupdateYesYesYes--
ResearchPlandeleteYes------
ResearchArtifactreadYesYesYes--
ResearchArtifactcreateYesYesYes--
ResearchArtifactupdateYesYesYes--
ResearchArtifactdeleteYes------

Notes:

  • Owner gets manage all, which CASL expands to every action on every subject (including future subjects). The explicit "Yes" entries in the owner column are a consequence of this wildcard.
  • Admin gets manage Invitation, which CASL expands to all four CRUD actions on invitations.
  • Admin does not get explicit research plan/artifact permissions. Those subjects are not mentioned in the admin case. However, if an admin also holds a member role (not currently possible -- roles are exclusive), they would inherit member permissions.
  • Member gets read Invitation but not create, update, or delete. Members can see who has been invited but cannot manage invitations.

Personal Organizations

Personal organizations apply cannot() overrides after the role switch:

SubjectActionEffect
MembercreateDenied regardless of role
InvitationmanageDenied regardless of role (all CRUD)

This means an owner of a personal organization retains manage all for everything except member creation and invitation management. All other permissions remain unchanged.

Personal orgs are single-user accounts. The collaboration features (members and invitations) are disabled at the authorization layer, not just the UI.

ABAC Conditions (Router-Level)

The matrix above covers RBAC -- what the CASL ability object permits. Some operations have additional attribute-based restrictions enforced in router code:

RouterConditionEffect
member.updateRoleTarget member has role "owner"Blocked: "Cannot change an owner's role"
member.removeTarget member has role "owner"Blocked: "Cannot remove the organization owner"
member.removeCaller is the target memberAllowed: members can remove themselves even without delete Member permission

These ABAC conditions are not encoded in the CASL ability -- they are explicit if statements in the router handler. See Router Enforcement for details on why and how.

How to Read This Matrix Against Code

The source of truth is defineAbilityFor() in packages/api/src/abilities/define-ability.ts. If this matrix and the code disagree, the code wins.

To verify a specific permission:

import { defineAbilityFor } from "./abilities";

const ability = defineAbilityFor({
  userId: "test-user",
  role: "admin",
  orgType: "company",
});

ability.can("delete", "Organization"); // false
ability.can("update", "Organization"); // true
ability.can("manage", "Invitation"); // true

The test file at packages/api/src/abilities/__tests__/define-ability.test.ts contains assertions for every cell in this matrix.

On this page