Skip to content

Roles & permissions

Axowl uses a snapshot-based RBAC model. Roles are templates; assigning a role to a member resolves its permission templates into a flat, sealed set of effective permissions.

Permission (catalog: "axowl.settings.update") seeded in SystemOrg
RolePermissionTemplate (Role ↔ scope expression) per org
Role (ORG_OWNER / ORG_ADMIN / ORG_MEMBER / custom) per org, has Level
ConnectedIdRole (member ↔ role assignment) per org
▼ resolved by SnapshotService
ConnectedIdPermission (ResolvedScope, flattened) ← the O(1) check table
  • Permission is a catalog of what actions exist (Permission.cs). It carries the base scope only.
  • RolePermissionTemplate binds a permission to a role with an expression that may contain variables, e.g. server.create:region={region} (RolePermissionTemplate.cs).
  • Assigning a role runs SnapshotService.CreateSnapshotAsync (SnapshotService.cs:23), which injects variables and writes flat ConnectedIdPermission.ResolvedScope rows. Permission checks read only that table.
  • Roles are the single source of truth. MembershipType (Owner/Admin/Member) is derived from the member’s top Role.Level — never set independently. See Connected ID.

Every org is created with three preset roles, each carrying axowl.* scopes (CreateOrganizationCommandHandler.cs:446):

RoleLevelScopes
ORG_OWNEROwneraxowl.*
ORG_ADMINAdminaxowl.{resource}.* for all resources + axowl.billing.read
ORG_MEMBERReadaxowl.{organization,application,user,settings,audit}.read

Custom roles are created in the dashboard and carry any subset of the catalog. See the full scope list in Permission scopes.

Snapshots are immutable point-in-time grants. Changing a role does not retroactively change already-issued snapshots unless the role is re-snapshotted (SnapshotService.ReSnapshotBatchAsync) — modeled on “a re-issued card”, preserving auditability.