The ERP Financial Agent enforces access at three levels: role-level (which actions a user can take), module-level (which ERP data domains and fact tables they can query), and row-level (whichDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/yohangr3/agentelanggrafh/llms.txt
Use this file to discover all available pages before exploring further.
centro_beneficio codes are visible). All three levels are resolved once per request from Cognito claims into an immutable Principal object that travels through both the outer conversation and inner SQL pipeline graphs.
Roles
The system defines seven roles — five active in v1, two pre-designed and inactive:| Role | Rank | Status | Description |
|---|---|---|---|
platform_admin | 100 | ✅ Active | Vendor super-admin. Cross-tenant operations, all permissions, all modules. |
org_admin | 80 | ✅ Active | Customer-side administrator. User management, RBAC assignment, full financial data. |
finance_lead | 60 | ✅ Active | CFO / Controller. Full financial data access, all agent actions, no user management. |
analyst | 40 | ✅ Active | Financial / data analyst. Agent + export + file upload, assigned modules only. |
viewer | 20 | ✅ Active | Read-only consumer. Chat + charts for assigned modules. No export or raw SQL. |
auditor | 50 | 🔒 Inactive | External auditor. Read-only + audit log. Time-boxed. |
nomina_officer | 30 | 🔒 Inactive | Payroll / HR officer. Only the nomina module. |
auditor or nomina_officer, set active=True in the ROLE_CATALOG in src/core/rbac/catalog.py.
Permissions
Each role grants afrozenset[Permission]. Permissions use dotted namespaced names:
Permission Bundles per Role
- viewer
- analyst / finance_lead
- org_admin
- platform_admin
chat.query · viz.create · feedback.submitData Modules
Modules gate fact tables and views. Dimension / lookup tables (ia_dm_*) are never gated — they contain labels (client names, cost center codes) that every authenticated user may reference in JOINs.
Module → Fact Table Mapping
| Module | Gated Fact Tables |
|---|---|
ingresos | ia_fct_ingresos, ia_fct_gastos_costos_ingresos, ia_fct_gastos_costos_ingresos_balance, ia_fct_ingresos_final |
costos | ia_fct_costos_centros*, vista_rentabilidad_material, vw_costo_venta, vw_costo_venta_multidimensional*, vw_driver_cantidades, ia_fct_gastos_costos_ingresos, ia_fct_gastos_costos_ingresos_balance |
nomina | Same cost fact tables — controls visibility of COSTOS MANO DE OBRA accounts |
presupuestos | ia_fct_ingresos_presupuestos, ia_fct_gastos_costos_ingresos_presupuestos, ia_fct_gastos_costos_ingresos_balance_presupuestos |
pagos | t_fsj_transacciones_pago_unificada, t_fsj_breb, t_fsj_orders_report_rappi, t_fsj_reporte_comercio_transaccion_redeban, t_fsj_reserve_release_mercado_pago_libre, t_fsj_wompi, t_fsj_transacciones_pos |
produccion | ia_fct_orden_fabricacion_cabecera/detalle/movimientos, vw_costo_produccion_unitario* |
compras | ia_fct_cabecera_pedidos_compras, ia_fct_detalle_pedidos_compras, ia_fct_historial_documentos_compra, ia_fct_plan_entregas_pedidos |
inventario | ia_fct_cabecera_documentos_material, ia_fct_cabecera/detalle_movimientos_material, ia_fct_inventario_materiales_centro, ia_fct_movimientos_materiales, vw_stock_material, vw_cobertura_inventario, ia_fct_metricas_inventario |
logistica | ia_fct_cabecera/detalle_documentos_transporte, ia_fct_etapas_transporte |
*ia_fct_costos_centros no longer exists in the live database. It remains in the module mapping for historical reference only. All cost queries should use ia_fct_gastos_costos_ingresos_balance.The Principal Object
Principal is an immutable frozen dataclass resolved once per request. It carries the user’s effective access — after role resolution, DynamoDB overrides, and Cognito claim parsing.
Principal Resolution Flow
Extract Cognito claims
The FastAPI dependency
get_current_user verifies the JWT and extracts user_id, username, email, groups (Cognito group memberships), custom:tenantId, custom:data_scope, and custom:tenants.Pick effective role
_pick_role(groups) maps each Cognito group to a RoleName using GROUP_TO_ROLE. If multiple groups match, the highest-rank role wins. If no group matches, DEFAULT_ROLE (viewer) is assigned.Resolve modules and tables
resolve_module_set(role_def.modules) expands the ALL_MODULES sentinel if present. tables_for_modules(modules) computes ALWAYS_ALLOWED_TABLES ∪ gated_tables_for_modules.Apply DynamoDB overrides
enrich_principal_with_overrides(principal) fetches the erp-agent-rbac-overrides DynamoDB table for tenant#user_id. Applied formula: effective_modules = (role_modules ∪ added) \ removed. Falls back to the original Principal on any error — DynamoDB unavailability never blocks login.Labor Cost Restriction
TheNOMINA module is an add-on to COSTOS. A user who has COSTOS but not NOMINA has labor_cost_restricted = True. The schema linker and SQL validator then hide any account where:
Row-Level Scoping: allowed_cebes
Whenallowed_cebes is a non-empty frozenset, the SQL generator injects a WHERE centro_beneficio IN (...) clause — or an equivalent filter — into every query that touches tables with a centro_beneficio column. An empty frozenset means no row-level restriction.
Shadow Mode vs Enforcement
RBAC operates in two modes controlled by therbac_enforce setting:
| Mode | Behavior |
|---|---|
rbac_enforce=false (shadow) | Permissions are resolved and logged, but no requests are blocked. Useful during initial rollout. |
rbac_enforce=true (enforced) | Any request lacking the required permission raises PermissionDeniedError, which the API layer maps to HTTP 403 and the agent node maps to a polite refusal in chat. |
Role Assignment Rules
An admin can only assign a role to another user if:- They hold the
admin.rbac.assignpermission, and - Their own role’s
rankis strictly higher than the target role’s rank.
org_admin (rank 80) from minting a platform_admin (rank 100).