Console role matrix
Regenerated 2026-10-18 from FRONTEND/src/lib/consoleRbac.ts (route rules and actions),
FRONTEND/src/lib/rolePermissionTemplates.ts (role to permission templates) and the router in
FRONTEND/src/App.tsx.

Figure 1 — The Console is a read-and-act view over the Control Plane. It never stores transaction data; transaction detail is read live from the Edge through the tunnel the Edge opens. What each role can see and do is the subject of this page.
One permission model
The Console and the Control Plane use the same permission names (BACKEND Permission enum).
- Controller guards use
hasAuthority('<PERMISSION>').consoleRbac.tschecks the same permission for the route or action, so the Console hides what the server would refuse. Where a Control Plane guard is role-based (hasAnyRole),consoleRbac.tsmirrors that role set and names the guard it copies (REGULATORY_FILING_ROLES,BLACKLIST_READ_ROLES,BLACKLIST_WRITE_ROLES,EDGE_CONFIG_EDITOR_ROLES,BACKFILL_PSP_ROLES). - At runtime the permissions come from the session (
role.permissions, read fromrole_permissions_dynamic). The Console falls back totemplatePermissionsForRole()only when the session carries no grants. - The tenant role templates match
RoleService.PSP_ROLE_TEMPLATES(Java) and the Flyway seeds: V127 (PSP_ADMIN,PSP_USER,COMPLIANCE_OFFICER), V128 (MLRO,CASE_MANAGER,AUDITOR), V218 (PSP_ANALYST,SCREENING_ANALYST), V257 (no SAR permission forPSP_USER), V271 (every tenant PSP gets its own copy of the tenant role set) and V272 (MANAGE_ROLESandVIEW_AUDIT_LOGSforPSP_ADMIN;VIEW_AUDIT_LOGSforCOMPLIANCE_OFFICER). A PSP admin may edit its own roles, so a live PSP can differ from this table. SUPER_ADMINandADMINhold every permission.APP_CONTROLLERholds none by template but is a platform operator.- Role hierarchy for role-based guards (
SecurityConfig.ROLE_HIERARCHY):SUPER_ADMINandPLATFORM_ADMIN>ADMIN>PSP_ADMIN>PSP_USER;MLRO>COMPLIANCE_OFFICER>CASE_MANAGER>INVESTIGATOR>ANALYST>VIEWER;PSP_ANALYSTandSCREENING_ANALYST>ANALYST;AUDITOR>VIEWER(read-only). The hierarchy applies tohasRole/hasAnyRoleguards only, not to permissions. - Platform operator:
SUPER_ADMIN,ADMINorPLATFORM_ADMINon theHOKEKA_PLATFORMtenant (pspId 0), orAPP_CONTROLLER(isPlatformAdmininFRONTEND/src/lib/userAccess.ts). Platform roles can never be assigned to tenant users (RoleAssignmentPolicy).
Personas
| Persona | Role names | Tenant |
|---|---|---|
| Platform super admin | SUPER_ADMIN, ADMIN | HOKEKA_PLATFORM (pspId 0) |
| Platform operator | PLATFORM_ADMIN | HOKEKA_PLATFORM |
| PSP administrator | PSP_ADMIN | PSP tenant |
| MLRO | MLRO | PSP tenant |
| Compliance officer | COMPLIANCE_OFFICER | PSP tenant |
| Case manager | CASE_MANAGER | PSP tenant |
| Investigator / analysts | INVESTIGATOR, PSP_ANALYST, SCREENING_ANALYST, ANALYST | PSP tenant |
| PSP operator | PSP_USER | PSP tenant |
| Read-only | AUDITOR, VIEWER | PSP tenant |
| Fraud & risk | FRAUD_ANALYST, FRAUD_MANAGER | PSP tenant |
| Payments & disputes | PAYMENTS_OPS | PSP tenant |
| Finance & management | FINANCE_OFFICER, EXECUTIVE | PSP tenant |
-
The team roles come from V281 and
RoleService.PSP_ROLE_TEMPLATES. None holds a SAR permission.FINANCE_OFFICERandEXECUTIVEhold onlyVIEW_FINANCE: aggregate KPIs and their own Hokeka invoices, no case, alert, merchant, dispute or transaction rows.PAYMENTS_OPSholdsVIEW_DISPUTES,VIEW_TRANSACTION_DETAILSandVIEW_PLATFORM_HEALTH(read-only Edge health).FRAUD_MANAGERinheritsFRAUD_ANALYSTand nothing else. -
BANK_AUDITORandBANK_OFFICERexist as role names but are not assignable tenant roles. They are not inRoleAssignmentPolicy.TENANT_TEMPLATE_ROLES, and they are not platform roles. -
At runtime
PLATFORM_ADMINholdsMANAGE_USERSandMANAGE_ROLES(V224__platform_user_management_permissions.sql). The Console's fallback template forPLATFORM_ADMIN(FRONTEND/src/lib/rolePermissionTemplates.ts) does not list them. The Console uses the permissions the server returns, so the template only matters when those are missing.
Routes
Every authenticated route in App.tsx passes through PermissionRoute, which calls
canAccessRoute() with the most specific matching prefix. A user without access sees an in-app
Access restricted panel. Sub-routes inherit their prefix rule (/cases/*,
/transaction-monitoring/*, /users/*, /records/:recordType/:recordId,
/kyc-documents/:merchantId, /psps/:pspId/configure). Legacy aliases redirect: /monitoring to
/transaction-monitoring, /audit-logs to /audit, /rules to /rules-generation,
/reports-center to /reports.
Public routes (no session): /login, /signup (invitation-only notice), /accept-invite,
/forgot-password, /password-reset/confirm, /password-reset.html.
"Roles holding it" is computed from the role templates (platform roles at pspId 0, tenant roles at a tenant pspId).
| Route prefix | Required (any of) | Roles holding it |
|---|---|---|
/dashboard | authenticated | all roles |
/profile | authenticated | all roles |
/messages | authenticated | all roles |
/transaction-monitoring | VIEW_TRANSACTION_DETAILS | all roles |
/customer-360 | VIEW_PII or MERCHANT_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, SCREENING_ANALYST, PSP_USER, AUDITOR |
/market-surveillance | REPORT_VIEW or VIEW_TRANSACTION_DETAILS | all roles |
/mobile-money | VIEW_TRANSACTION_DETAILS | all roles |
/wallet-intelligence | VIEW_TRANSACTION_DETAILS | all roles |
/records | VIEW_TRANSACTION_DETAILS or VIEW_CASES | all roles |
/alerts | VIEW_CASES | all roles |
/cases | VIEW_CASES | all roles |
/network | VIEW_CASES | all roles |
/kyc-documents | MERCHANT_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, SCREENING_ANALYST, PSP_USER, AUDITOR |
/screening | VIEW_SCREENING_RESULTS | all roles except VIEWER |
/rules-generation | MANAGE_RULES | SUPER_ADMIN, ADMIN, PSP_ADMIN |
/limits-aml | MANAGE_RULES or CONFIGURE_SYSTEM | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
/regulatory-reports | VIEW_SAR (REPORT_VIEW alone is not enough: tipping-off) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, SCREENING_ANALYST, AUDITOR, VIEWER |
/filed-documents | VIEW_SAR and a role in REGULATORY_FILING_ROLES (SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, MLRO, COMPLIANCE_OFFICER) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, MLRO, COMPLIANCE_OFFICER |
/lists | MANAGE_WATCHLISTS, PSP_SETTINGS_VIEW or MANAGE_PSP, or a role in BLACKLIST_READ_ROLES | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, INVESTIGATOR, SCREENING_ANALYST, PSP_USER |
/risk-analytics | REPORT_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, PSP_USER, AUDITOR |
/reports | REPORT_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, PSP_USER, AUDITOR |
/chargebacks | VIEW_CASES, MERCHANT_VIEW or VIEW_DISPUTES | all roles except FINANCE_OFFICER and EXECUTIVE |
/compliance-calendar | VIEW_SAR or REPORT_VIEW | all roles except INVESTIGATOR and ANALYST |
/analytics | REPORT_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, PSP_USER, AUDITOR |
/audit | VIEW_AUDIT_LOGS | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, AUDITOR |
/data-access-log | VIEW_AUDIT_LOGS | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, AUDITOR |
/merchants | MERCHANT_VIEW (the Control Plane scopes rows to the PSP) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, SCREENING_ANALYST, PSP_USER, AUDITOR |
/psps | MANAGE_PSP, platform operator only | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
/edge-nodes | MANAGE_PSP or PSP_SETTINGS_VIEW | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
/edge-backfill | MANAGE_PSP or a role in BACKFILL_PSP_ROLES (PSP_ADMIN, ADMIN, MLRO, COMPLIANCE_OFFICER) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER |
/users | MANAGE_USERS | SUPER_ADMIN, ADMIN, PSP_ADMIN |
/settings | PSP_SETTINGS_VIEW, CONFIGURE_SYSTEM or MANAGE_PSP | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
/billing | MANAGE_PSP or PSP_SETTINGS_VIEW (tenant users are redirected to /settings, Billing tab) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
/runtime-errors | CONFIGURE_SYSTEM, platform operator only | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
/organization | PSP_SETTINGS_VIEW, tenant only | PSP_ADMIN |
"All roles" means SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST, SCREENING_ANALYST, ANALYST, PSP_USER, AUDITOR and VIEWER.
Navigation
The sidebar (buildConsoleNavGroups) shows an item only when canAccessRoute() allows its route.
Groups: Intelligence (Dashboard, Live Monitoring, Customer 360, Market Surveillance, Mobile Money,
Wallet Intelligence, Alerts, Cases, Messages, Network), Compliance (KYC, Screening, Risk Rules,
Transaction Limits, Regulatory Reports, Filed Documents, Blocklists & Limits, Chargebacks,
Compliance Calendar), Analytics (Risk Analytics, Reports, Analytics, Audit Logs, Data Access Log)
and Administration. Platform operators see Merchants, PSPs, Edge Nodes, Edge Back-fill, Users,
Runtime Errors and Settings; tenant users see My Organization, Merchants, Users (with
MANAGE_USERS), Edge Nodes (PSP_SETTINGS_VIEW, MANAGE_PSP or VIEW_PLATFORM_HEALTH), Edge Back-fill and
Settings. Compliance Calendar follows its role guard (SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, COMPLIANCE_OFFICER,
MLRO, PSP_ADMIN). The Help section (Edge install and setup guides) shows only to users who can open Edge Nodes.
Dashboard workspaces
/dashboard?workspace=<id> shows one of six layouts (FRONTEND/src/lib/dashboardWorkspaces.ts). The tabs list
only the workspaces the user may open; a requested workspace the user may not open falls back to the role default.
| Workspace | Opens with | Default for |
|---|---|---|
fraud-risk | VIEW_CASES and VIEW_TRANSACTION_DETAILS | FRAUD_*, ANALYST, PSP_ANALYST, PSP_USER |
compliance | VIEW_SAR, or INVESTIGATOR / CASE_MANAGER | MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, SCREENING_ANALYST, VIEWER, platform staff |
payments-ops | VIEW_DISPUTES or VIEW_PLATFORM_HEALTH, PSP_ADMIN, platform staff | PAYMENTS_OPS |
finance-management | VIEW_FINANCE, PSP_ADMIN, platform staff | FINANCE_OFFICER, EXECUTIVE, PSP_ADMIN |
merchant-lifecycle | MERCHANT_EDIT | none (opened from the tabs) |
platform-controls | VIEW_AUDIT_LOGS or VIEW_PLATFORM_HEALTH, platform staff | AUDITOR, BANK_AUDITOR |
The layout is not the security boundary. Each widget also needs the permission its endpoint checks: alert rows
VIEW_CASES (DashboardController live-alerts), merchant names MERCHANT_VIEW (top-risk, merchant cases),
decision rows the DecisionStatsController roles, disputes the /chargebacks route, Edge health the
/edge-nodes route, invoices VIEW_FINANCE / PSP_ADMIN (PspAdminBillingController, own PSP only). Aggregate
KPIs are open to any signed-in user. A drill-down link renders only when canAccessRoute() allows its target,
so summary-only users keep the figures without a way into the detail. Featured-case actions follow
canPerformAction().
Actions
Action (canPerformAction) | Check | Roles holding it |
|---|---|---|
alert.updateStatus, alert.resolveDisposition | ASSIGN_CASES or CLOSE_CASES (PUT /alerts/{id}/status, /resolve) | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST |
alert.export | EXPORT_DATA or VIEW_CASES | all roles |
alert.escalateToCase | CREATE_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER |
case.assign | ASSIGN_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST |
case.note | ADD_CASE_NOTES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, PSP_ANALYST, PSP_USER |
case.evidence | ADD_CASE_EVIDENCE | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST |
case.escalate | ESCALATE_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER |
case.close | CLOSE_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER |
case.reopen | REOPEN_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER |
case.updateStatus | ASSIGN_CASES, CLOSE_CASES or ESCALATE_CASES | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST |
sar.create, sar.submit | CREATE_SAR (POST /compliance/sar, /compliance/sar/workflow; a SAR without a case is raised from Edge decision records, and POST /compliance/sar/workflow/{id}/transactions sets them while DRAFT or REJECTED) | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER |
sar.approve, sar.reject | APPROVE_SAR | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER |
sar.file | FILE_SAR | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER |
filing.file | FILE_SAR, a REGULATORY_FILING_ROLES role and not a platform operator (POST /sar/{id}/file, /ctr/{id}/file; only the PSP's own staff file) | MLRO, COMPLIANCE_OFFICER |
filedDocuments.view | VIEW_SAR and a REGULATORY_FILING_ROLES role | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, MLRO, COMPLIANCE_OFFICER |
rules.manage | MANAGE_RULES | SUPER_ADMIN, ADMIN, PSP_ADMIN |
users.manage | MANAGE_USERS | SUPER_ADMIN, ADMIN, PSP_ADMIN |
roles.manage | MANAGE_ROLES (a PSP admin only within its own PSP, only permissions it holds, never a platform role) | SUPER_ADMIN, ADMIN, PSP_ADMIN |
psp.manage | MANAGE_PSP | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
screening.ingest | MANAGE_WATCHLISTS (POST /sanctions/download/trigger) | SUPER_ADMIN, ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, SCREENING_ANALYST |
screening.override | OVERRIDE_SCREENING_MATCH | SUPER_ADMIN, ADMIN, MLRO, SCREENING_ANALYST |
virtualAssets.register | VirtualAssetComplianceController wallet registration and Travel Rule preparation roles | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST, SCREENING_ANALYST, ANALYST |
virtualAssets.screen | VirtualAssetComplianceController wallet/VASP screening roles | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR, PSP_ANALYST, SCREENING_ANALYST, ANALYST |
virtualAssets.write | VASP records, Travel Rule policies and transmission (hasAnyRole compliance administrators) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER |
virtualAssets.verifyIdentity | Travel Rule identity verification roles (analyst-only roles excluded) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, CASE_MANAGER, INVESTIGATOR |
blacklist.view | BLACKLIST_READ_ROLES role or MANAGE_WATCHLISTS | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, INVESTIGATOR, SCREENING_ANALYST, PSP_USER |
blacklist.manage | BLACKLIST_WRITE_ROLES role or MANAGE_WATCHLISTS | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER, SCREENING_ANALYST |
edgeConfig.view | platform operator or any user of the PSP (PspDataSettingsAccess.requireRead) | all roles |
edgeConfig.manage | platform operator, or the PSP's PSP_ADMIN/ADMIN (requireConfigEditor) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
backfill.view, backfill.control | platform operator, or a BACKFILL_PSP_ROLES role of the PSP | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN, MLRO, COMPLIANCE_OFFICER |
backfill.start | platform operator (LedgerBackfillAccess.requirePlatformOperator) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
billing.platform | platform operator and MANAGE_PSP | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
billing.pay | PSP_SETTINGS_EDIT or PAY_INVOICES on the caller's own PSP (POST /billing/payments/invoices/{id}/checkout, /mpesa; V285) | PSP_ADMIN, FINANCE_OFFICER |
merchant.lifecycle | MERCHANT_EDIT, own PSP (POST /merchants/{id}/suspend, /reactivate, DELETE /merchants/{id}; closing or suspending blocks the merchant on the PSP's Edges) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN, PSP_ADMIN |
runtimeErrors.view | platform operator and CONFIGURE_SYSTEM | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
ai.settings | platform operator with CONFIGURE_SYSTEM (/settings, Hokeka AI tab; the APP_CONTROLLER service account gets no platform tab) | SUPER_ADMIN, ADMIN, PLATFORM_ADMIN |
dispute.recordAction | MANAGE_DISPUTES (POST /chargeback/disputes/{id}/action, own PSP only; V282) | SUPER_ADMIN, ADMIN, PSP_ADMIN, FRAUD_MANAGER, PAYMENTS_OPS |
Alert dispositions that confirm a true positive return 202 / pending_second_review until a
second, different reviewer confirms the same disposition (maker-checker, V286). A different disposition
returns the alert to investigating with review status RETURNED; escalating to a case is always final.
Server guards on SAR endpoints use the same permissions as the Console (VIEW_SAR, CREATE_SAR,
AMEND_SAR, FILE_SAR, APPROVE_SAR); /limits needs MANAGE_RULES or CONFIGURE_SYSTEM, like the
/limits-aml route. VIEW_FINANCE (finance, management) opens /settings with the Billing tab only.
Automated tests
Vitest: FRONTEND/src/lib/consoleRbac.test.ts checks nav, route and action expectations per
persona; FRONTEND/src/lib/dashboardWorkspaces.test.ts checks workspaces, row-widget exclusion and team menus.
JUnit: ConsoleBackendRbacAlignmentTest evaluates the real @PreAuthorize guards for every team role. scripts/live-console-role-matrix.mjs runs the personas against a live Console.
Demo accounts (seeded)
See BACKEND/src/main/resources/db/migration/V127__seed_demo_accounts.sql (password documented in
the migration header).