Roles & Permissions β
The domain model currently defines three user roles within an Operator (tenant).
Current Roles β
graph LR
O[Operator / Tenant] --> A[Manager]
O --> Di[Dispatcher]
O --> Dr[Driver]
A -->|full access| AA[workspace]
Di -->|operational access| AA
Dr -->|read-only assignments| DA[driver]| Role | Frontend Access | Capabilities |
|---|---|---|
| Manager | workspace (full) | Everything a Dispatcher can do, plus: user/staff management, RBAC assignments, payment integrations (Mollie/Klarna), API key management, company branding, communication templates |
| Dispatcher | workspace (scoped) | tour/trip CRUD, booking management, passenger CRM, seat reassignments, fleet management (vehicles), refund initiation*, view metrics/activity |
| Driver | driver only | View daily assignments, live passenger manifest, QR check-in scanning, "Missing Passengers" actions |
NOTE
Driver authentication: Drivers authenticate via Nhost auth.users like all other actors. A driver has an auth.users row (for login) and a linked crew_members row (for operational data β qualifications, schedules, license). The DRIVER role in user_tenant_assignments grants access to the Driver PWA only. See ADR-005 Β§OnboardCrewMember for the linking flow.
NOTE
*Dispatcher-initiated refunds represent a cross-context action. Since Dispatchers operate in the Backoffice context, they cannot directly mutate tables in the Commerce context. A Hasura Action triggers the refund, functioning as a saga coordinator to orchestrate the Commerce mutation.
Proposed Additions β
| Role | Rationale | Frontend | Access |
|---|---|---|---|
| Owner | Distinct from Manager β the business owner who sees billing, subscription, financial dashboards, but doesn't operate day-to-day | workspace (billing/analytics views) | Subscription management, revenue analytics, DATEV exports, contract/plan changes |
| Viewer / Read-only | For accountants, external auditors, or partner agencies who need read access to bookings/revenue without write permissions | workspace (read-only mode) | View bookings, revenue, passenger lists β no edit capabilities |
TIP
Starting with the three core roles (Manager, Dispatcher, Driver) is the right MVP approach. We can introduce the Owner and Viewer roles in V1.1+ when billing dashboards and DATEV exports land.
NOTE
Frontend Feature Gating: The JWT plan_id claim (injected via Nhost custom claims webhook from tenant_subscriptions) drives subscription tier visibility in the workspace frontend. Premium navigation items (e.g., Dynamic Pricing, Omnichannel Inbox) render as greyscaled/locked for CORE tier operators. Clicking a locked feature surfaces an internal upsell prompt. See rbac-matrix.md Β§Implementation Notes for the Hasura-level enforcement via X-Hasura-Plan.
NOTE
Granular RBAC: Beyond the core roles, the user_access_grants table enables additive capability grants (e.g., a Dispatcher who also needs CREW_MGMT). See the Capability Mapping section below and ADR-005.
Platform Roles β
In addition to tenant-specific roles, the system supports cross-tenant roles for Busflow employees:
| Role | Access | Capabilities |
|---|---|---|
| Busflow Staff (System Agent) | admin-console / API | System administration, tenant provisioning, cross-tenant support (via impersonation tokens), billing and usage analytics. |
IMPORTANT
Implementation: Busflow Staff are Nhost auth.users rows with default_role = busflow_staff. They do not have a user_tenant_assignment. They bypass the tenant_id filter via the elevated Hasura session variable x-hasura-role: busflow_staff, which grants unrestricted cross-tenant select permissions. Hasura Actions route all staff mutations through mandatory change_events audit logging (see ADR-004 Β§Staff Audit).
Capability Mapping β
Beyond the binary role β frontend access model, the system supports additive capabilities via user_access_grants. This enables small operators where one person fills multiple roles.
Base Capabilities per Role β
| Role | Base Capabilities | Notes |
|---|---|---|
MANAGER | ALL (implicit) | Manager has every capability. Access grants are never needed. |
DISPATCHER | DISPATCH, BOOKING_MGMT, FLEET_MGMT | Can manage tours, bookings, and vehicles. |
DRIVER | DRIVER_APP | Driver PWA access only. |
Available Capabilities β
| Capability | Description |
|---|---|
DISPATCH | Create/edit tour departures, manage scheduling |
BOOKING_MGMT | View/manage bookings, initiate refunds |
FLEET_MGMT | Manage vehicles, assign to departures |
DRIVER_APP | Access driver PWA (boarding, manifests) |
CREW_MGMT | Manage crew members, qualifications, absences |
FINANCIAL_REPORTS | View revenue analytics, DATEV exports |
Resolution β
effective_capabilities = base_capabilities(default_role) βͺ granted_capabilities
Resolved at the application layer (NestJS guards via @RequiresCapability() decorator), not in the JWT. See ADR-005 Β§Capability Resolution.
WARNING
Hasura Inherited Roles. Hasura supports inherited roles β grouping multiple roles into a composite role. The capability model could map directly to Hasura inherited roles (e.g., a dispatcher_with_fleet inherited role combining dispatcher + fleet_manager permission sets). We should evaluate this during RBAC implementation to determine whether capability resolution belongs in NestJS, Hasura, or both.
Role β Frontend Matrix β
| landing | marketing | booking-widget | passenger | workspace | driver | |
|---|---|---|---|---|---|---|
| Unauthenticated visitor | β | β | β | β | β | β |
| Passenger (Customer) | β | β | β | β | β | β |
| Manager | β | β | β | β | β (full) | β |
| Dispatcher | β | β | β | β | β (scoped) | β |
| Driver | β | β | β | β | β | β |
| Owner (future) | β | β | β | β | β (analytics) | β |
| Viewer (future) | β | β | β | β | β (read-only) | β |
NOTE
Passengers are not users of the Operator's tenant β they interact with the system via booking-widget (pre-trip) and passenger (post-booking). Authentication for passengers is lightweight: email + booking code or magic link, not traditional login. We plan a dedicated B2C authentication specification handling token TTLs and flow diagrams for [future].