URD: Permissions
| Module | CORE-02 | Version | v0.6 |
|---|---|---|---|
| Status | Built · hierarchy In-progress | Date | 2026-06-04 |
Business documentation. This URD is Permissions' feature list - each feature below is one Functional Area (
<AREA>). The same<AREA>keys the feature's PRDs (PRD-<AREA>-NNN) and tests (TC-<AREA>-NNN), and each feature is listed in the Delivery feature catalog. See the Feature Spine convention.
1. Purpose
Define how access to features and data is controlled. Permissions lets administrators assign roles to users, grant permissions to those roles, scope roles to a specific organization or merchant, and have every query automatically filtered to what the requesting user is allowed to see - enforced through a Casbin priority-based RBAC model with per-merchant domains.
2. Scope
| Included | Excluded |
|---|---|
| Eight fixed system roles + priority hierarchy | Wildcard permissions |
| System-role bypass (Super Admin / Admin / Operator) | Permission categories / UI grouping |
| Automatic role-based data filtering | Time- or shift-based permissions |
| Custom role creation with priority and scope | Permission audit logging |
| Permission catalog (create / update / delete) | Role templates / bundles |
| Grant / revoke permissions to roles | Per-merchant active-role switching |
| Grant / revoke roles to users | Technical API specifications (see developer docs) |
| Effective-permission query (direct + inherited) | |
| Per-organization & per-merchant scoping | |
| Privilege-escalation guard |
3. Definitions
| Term | Definition |
|---|---|
| Role | A named access level with a numeric priority. SYSTEM (fixed) or CUSTOM (user-created). |
| Permission | A named action on a resource, identified by a globally unique code. |
| Grant (policy) | A record granting a permission to a role or directly to a user. |
| Membership (group) | A record linking a user to a role, a user to an org/merchant, or a role to an org/merchant. |
| Effective permissions | The union of all permissions a user holds: direct grants + permissions inherited through roles. |
| Scoped role | A custom role bound to a specific organization or merchant, limiting where it applies. |
| Domain | The merchant a grant applies within, selected per request by the active-merchant header. |
| System-role bypass | Super Admin, Admin, and Operator skip all data filtering and hold every permission. |
| HQ-owner expansion | An Owner at a head-quarter merchant automatically reaches every sibling merchant of that organizer. |
| Privilege escalation | Managing a role at or above the actor's own priority - always blocked. |
4. Conceptual Model
Conceptual only - the full policy data model lives in the developer RBAC docs.
5. Feature Catalog
The feature list of this module. Each row is one feature (a Functional Area). Detail in §6. Mirrored in the Delivery feature catalog.
| Feature ID | Feature | Phase | Status | Priority |
|---|---|---|---|---|
ROLE | Fixed Roles | P1 | Built | High |
CROLE | Custom Roles | P2 | Built | High |
PERM | Permission Catalog | P2 | Built | High |
GRANT | Grant / Revoke | P2 | Built | High |
EFF | Effective Permissions & Scope | P2 | Built | High |
HIER | Resource, Action & Domain Hierarchy | P3 | In-progress | High |
DECL | Permission Declaration | P3 | In-progress | High |
Status: live from Plane where mapped, otherwise registry-declared. Vocabulary mirrors Plane (state-group / phase).
6. Features
One sub-section per feature, in catalog order. Each feature keeps its description, requirements, and acceptance together. Priority = MoSCoW (Must / Should / Could / Won't).
ROLE - Fixed Roles Built
Feature ID: permissions/ROLE · Phase: P1 · PRDs: - · Dev: @nx/identity
What it does for users: eight system roles ship pre-seeded with a fixed priority order; the highest three see all data, while Owner, Cashier, and Employee are automatically limited to the organization or merchants they belong to.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-ROLE-001 | M | Provide eight fixed roles seeded at startup: Super Admin, Admin, Operator, Owner, Cashier, Employee, Customer, Guest |
| URD-ROLE-002 | M | Fixed (SYSTEM) roles cannot be modified or deleted |
| URD-ROLE-003 | M | Each role has a numeric priority that sets its place in the hierarchy |
| URD-ROLE-004 | M | Super Admin, Admin, and Operator bypass all data filtering and hold every permission |
| URD-ROLE-005 | M | Owner sees only their own organization and its merchants |
| URD-ROLE-006 | M | Employee (and Cashier) see only merchants they are assigned to |
| URD-ROLE-007 | M | Every list and count operation is filtered by the requesting user's scope |
| URD-ROLE-008 | M | Automatic filtering cannot be overridden by a user-supplied filter or direct ID access |
| URD-ROLE-009 | S | An Owner at a head-quarter merchant reaches every sibling merchant of that organizer |
Acceptance
AC-ROLE-01: Data filtering by role
| Given | When | Then |
|---|---|---|
| Users with different roles | They hit the same endpoint | Super Admin/Admin/Operator see all; Owner sees own org; Employee/Cashier see assigned merchants |
| Any list/count query | Run by a scoped user | Filtering is automatic and cannot be overridden |
| A scoped user supplies a wider filter or a foreign ID | Request is made | The filter is ignored / access denied |
AC-ROLE-02: Fixed-role protection
| Given | When | Then |
|---|---|---|
| Any of the eight system roles | User tries to modify or delete it | Operation rejected |
| A fixed role's priority | User tries to change it | Rejected - priorities are immutable |
CROLE - Custom Roles Built
Feature ID: permissions/CROLE · Phase: P2 · PRDs: - · Dev: @nx/identity
What it does for users: administrators and owners create their own roles with a custom name, priority, and an optional organization/merchant scope - always below their own priority - and remove them safely once no users remain assigned.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-CROLE-001 | M | Authorized users can create custom roles with an i18n name, priority, and optional scope |
| URD-CROLE-002 | M | Role identifier is auto-generated from priority + name and is unique within its scope |
| URD-CROLE-003 | M | A new role's priority must be strictly lower than the creator's own highest priority |
| URD-CROLE-004 | M | Custom roles can be scoped to a specific organization or merchant |
| URD-CROLE-005 | M | Custom roles can be updated (name, description, priority) |
| URD-CROLE-006 | M | A role cannot be deleted while users are still assigned to it |
| URD-CROLE-007 | M | Deleting a role cascade-removes its permission grants and scope links |
| URD-CROLE-008 | S | An Owner can create roles scoped only to their own organization or merchants |
Acceptance
AC-CROLE-01: Custom role creation
| Given | When | Then |
|---|---|---|
| Admin or Owner | Creates a role with priority + name | Role created with type CUSTOM, identifier auto-generated |
| Priority ≥ creator's own | Creation attempted | Rejected (privilege escalation) |
| Org/merchant scope provided | Role created | Scope link created |
| Same priority + name in same scope | Creation attempted | Rejected (identifier collision) |
AC-CROLE-02: Role deletion
| Given | When | Then |
|---|---|---|
| Custom role with no users | Admin deletes | Role + grants + scope links soft-deleted |
| Custom role with assigned users | Admin deletes | Blocked - must unassign users first |
| System role | Admin deletes | Rejected - fixed roles are immutable |
PERM - Permission Catalog Built
Feature ID: permissions/PERM · Phase: P2 · PRDs: - · Dev: @nx/identity
What it does for users: administrators maintain the catalog of permissions - each a uniquely coded action on a resource with i18n name and description - and cannot remove one that is still in use.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-PERM-001 | M | Admin can create permissions with a unique code, action, scope, and subject |
| URD-PERM-002 | M | Permission code must be globally unique |
| URD-PERM-003 | M | Permission name and description support i18n |
| URD-PERM-004 | M | Admin can update and delete permissions |
| URD-PERM-005 | M | A permission with active grants cannot be deleted |
Acceptance
AC-PERM-01: Permission catalog integrity
| Given | When | Then |
|---|---|---|
| Admin defines a permission | A unique code, action, scope, and subject are supplied | The permission is created |
| A duplicate code | Creation attempted | Rejected - codes are globally unique |
| A permission with active grants | Admin deletes | Blocked - must revoke grants first |
GRANT - Grant / Revoke Built
Feature ID: permissions/GRANT · Phase: P2 · PRDs: - · Dev: @nx/identity
What it does for users: administrators attach or remove permissions on a role and assign or unassign roles to users; every operation is idempotent and blocked when it would touch a role at or above the actor's own priority.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-GRANT-001 | M | Admin can grant one or more permissions to a role |
| URD-GRANT-002 | M | Admin can revoke one or more permissions from a role |
| URD-GRANT-003 | M | Granting an already-granted permission is idempotent (skipped, not duplicated) |
| URD-GRANT-004 | M | Admin can grant and revoke roles to/from users |
| URD-GRANT-005 | M | Privilege-escalation guard is enforced on every grant/revoke operation |
| URD-GRANT-006 | M | Admin can view all permissions granted to a role |
| URD-GRANT-007 | M | Admin can view all users assigned to a role and all roles assigned to a user |
Acceptance
AC-GRANT-01: Permission grant
| Given | When | Then |
|---|---|---|
| Role + permission IDs | Admin grants | Grant records created, count returned |
| Permission already granted | Admin grants again | Skipped (idempotent), skip count returned |
| Actor priority ≤ role priority | Admin grants | Rejected (privilege escalation) |
EFF - Effective Permissions & Scope Built
Feature ID: permissions/EFF · Phase: P2 · PRDs: - · Dev: @nx/identity
What it does for users: anyone can ask what a user is actually allowed to do - the deduplicated union of direct grants and role-inherited grants - and which organizations and merchants they belong to, resolved within the active merchant domain.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-EFF-001 | M | Compute effective permissions as the union of direct + role-inherited grants (deduplicated) |
| URD-EFF-002 | S | The query supports a mode filter: direct, inherit, or both |
| URD-EFF-003 | M | Retrieve all organizations and all merchants a user belongs to |
| URD-EFF-004 | M | A grant resolves only within the active merchant domain chosen per request |
Acceptance
AC-EFF-01: Effective permissions
| Given | When | Then |
|---|---|---|
| User with roles + direct grants | Effective query | Union of direct + inherited, deduplicated |
| Mode = direct | Query | Only direct grants returned |
| Mode = inherit | Query | Only role-inherited grants returned |
HIER - Resource, Action & Domain Hierarchy In-progress
Feature ID: permissions/HIER · Phase: P3 · PRDs: PRD-HIER-001 · Map: Permission Hierarchy · Dev: @nx/identity
What it does for users: permissions form a resource tree × action lattice × domain tree, so one coarse grant (e.g. Sale:manage) replaces many flat grants - covering every subject, operation, and merchant beneath it.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-HIER-001 | M | A grant MAY target a resource at any level - module, subject, or operation - and automatically covers all descendants |
| URD-HIER-002 | M | Operation resources nest under their subject by dotted code (SaleOrder.refund ⊂ SaleOrder) with no extra configuration |
| URD-HIER-003 | M | A subject MAY roll up to a module so a module-level grant covers every subject in it (grant on Sale ⇒ SaleOrder, SaleOrderItem, …) |
| URD-HIER-004 | S | A child entity MAY nest under a parent across naming (SaleOrderItem ⊂ SaleOrder) so the parent grant covers the child |
| URD-HIER-005 | M | Actions form a lattice - manage ⊃ {write, read, execute}, write ⊃ {create, update, delete} - a broader granted action satisfies any narrower request |
| URD-HIER-006 | M | A route requests a base action (read/create/update/delete/execute); a grant MAY use any tier including manage/write |
| URD-HIER-007 | M | A grant scoped to an Organizer applies to every merchant under it; a grant scoped to a Merchant applies only there |
| URD-HIER-008 | M | A head-quarter / management role reaches all merchants of its organizer via an organizer-scoped grant - without a per-merchant membership row |
| URD-HIER-009 | S | A grant MAY pin to SYSTEM_WIDE (everywhere) or ANY_MEMBER (every joined merchant) |
Acceptance
AC-HIER-01: Coarse grant covers descendants (manage example)
| Given | When | Then |
|---|---|---|
A role granted (SaleOrder, manage) | A request to read SaleOrder.refund | Allowed - manage ⊃ read (lattice) and SaleOrder.refund ⊂ SaleOrder (dotted) |
A role granted (Sale, manage) (module) | Any operation on SaleOrderItem | Allowed - SaleOrderItem ⊂ Sale (module roll-up) |
A role granted (SaleOrder, read) only | A request to create SaleOrder | Denied - read does not cover create |
AC-HIER-02: Domain scope (organizer vs merchant / HQ)
| Given | When | Then |
|---|---|---|
A role granted on Organizer_9 | A request in any Merchant under organizer 9 | Allowed via g3 - no per-merchant membership needed |
A role granted on Merchant_7 only | A request in Merchant_8 | Denied - scope is a single merchant |
| An HQ/management user (organizer-scoped grant) | Acting on any sibling merchant | Allowed - reaches all merchants of the organizer |
DECL - Permission Declaration In-progress
Feature ID: permissions/DECL · Phase: P3 · PRDs: PRD-HIER-001 · Dev: @nx/identity
What it does for users: permissions, the action lattice, and the resource/domain hierarchy are declared in code with minimal boilerplate and seeded idempotently, so a new controller needs one helper call and removed codes are reconciled away automatically.
Requirements
| ID | P | Requirement |
|---|---|---|
| URD-DECL-001 | M | Permissions, the action lattice, and resource/domain hierarchy edges are declared in code and seeded idempotently |
| URD-DECL-002 | M | Declaring a controller's operations + resource node + base action takes minimal boilerplate (one helper call) |
| URD-DECL-003 | M | Role→grant assignment is declared as a small matrix (e.g. OWNER → Sale:manage), not a per-operation enumeration |
| URD-DECL-004 | M | Seeding reconciles removed codes idempotently - no stale permissions/grants accumulate |
| URD-DECL-005 | C | The toolkit is built in nx-seller first, with a clear path to port into IGNIS later |
Acceptance
AC-DECL-01: Minimal-boilerplate declaration
| Given | When | Then |
|---|---|---|
| A new controller | Author declares its resource + operations in one helper call | Permissions + route authorize specs are produced without enumerating CRUD |
| The action lattice + hierarchy edges | Migrations run | g5/g4/g3 edges seeded idempotently; re-run changes nothing |
| A permission code removed from the declaration | Migrations run | The stale permission + its grants are reconciled away |
7. Constraints & Non-Goals
Constraints
| ID | Constraint |
|---|---|
| C-01 | Eight fixed roles are seeded at startup and are immutable |
| C-02 | No one can create or manage a role at or above their own priority |
| C-03 | Permission codes are globally unique |
| C-04 | Grant operations are idempotent |
| C-05 | All records are soft-deleted, never physically removed |
| C-06 | The session token is stateless - role/permission changes take effect on next sign-in |
| C-07 | All operations require authentication |
| C-08 | A grant resolves within one active merchant domain per request |
Non-Goals
- Arbitrary glob/regex permissions (e.g.
sales.*) - coarse access comes from the resource tree +manageaction (HIER), not globs - Time- or shift-based permissions
- Permission audit log
- Per-merchant active-role switching
Superseded by
HIER: the former non-goals "wildcard permissions", "permission categories / UI grouping" (now module roll-up), and "role templates / bundles" (now a coarse role→grant matrix) are replaced by the resource/action/domain hierarchy.
8. Version History
| Date | Author | Description | Ver |
|---|---|---|---|
| 2026-02-26 | P. Do - Product Owner | Initial user stories | v0.1 |
| 2026-04-16 | Product | Restructured to URD format | v0.3 |
| 2026-05-29 | Product | Migrated to module-docs convention; reconciled with Casbin per-merchant RBAC (8 roles, priorities, domains); re-keyed areas (ROLE/CROLE/PERM/GRANT/EFF) | v0.4 |
| 2026-06-04 | Product | Added Resource/Action/Domain Hierarchy (HIER) + Permission Declaration (DECL) areas for the resource-action-hierarchy increment (PRD-HIER-001); superseded wildcard/categories/bundles non-goals | v0.5 |
| 2026-06-04 | Claude (AI pair) | Reorganize by feature (Feature Spine); each feature carries its own requirements + acceptance | v0.6 |