A Workspace Role is the permission level a Member has within one Workspace. The starter defines three roles in workspace_members.role: owner, admin, member.
Enforcement status: roles are enforced. Every workspace mutation names the permission it needs and the shared guard answers, in the Reference Application and in the API Worker alike. Loaders are the remaining gap: a page can still render a control the actor will be refused, and the refusal only arrives on submit.
How a role becomes a decision
Roles are not checked by name. @b2b-saas-starter/authz declares a statement per resource (apiToken, webhook, auditLog, notification, assistant, mcp, onboarding, plus the five Better Auth organization plugin owns), maps each role to the actions it holds, and exposes one authorize() call. A route names the permission ({ apiToken: ['create'] }) and never the role, so the mapping lives in exactly one table. API Token scopes map onto the same statements as synthetic roles, which is why a browser session and a Bearer token resolve through the same decision.
Owner
Holds every statement, including deleting the workspace. Exactly one Member per Workspace is the owner. Ownership transfer and owner-only governance Notifications are scaffolded in the data model; the transfer flow itself and the notification routing are not yet implemented.
Admin
Holds every statement except deleting the workspace: managing members, Invitations, API Tokens, Webhook Endpoints, billing, and integration configuration. There can be multiple admins.
Member
Reads workspace data and operates within the Reference Application without changing configuration. Concretely: the member list and Notifications. A member deliberately cannot read the Audit Event log or list API Tokens: both disclose the workspace's security posture. Most users are members.
Why three, not four
Three is the minimum that supports a real B2B governance model without inventing roles that nothing meaningfully enforces. Finer-grained permissions would mean a workspace-level policy language, and runtime-defined roles are switched off for the same reason; the starter leaves that as a future module rather than guessing the right shape.