Architecture: How should platform-wide role-based access control be implem...

Documentation for Sessions

🏗️ Architecture: How should platform-wide role-based access control be implem...

Date: 2026-03-31
Time: 18:41:43 UTC
Session: gspace1-293.2
Agent: angular
Confidence: 95%
Status: ✅ Documented


📋 Decision Context

Question:

How should platform-wide role-based access control be implemented across 3 backends, all frontend apps, and 238+ NX libraries?

Task: S292-293: Vitto Capability System & Feature Enhancements


🎯 Decision Made

Chosen:

Capability Matrix System: A typed VittoCapability enum (~85 values) with a CAPABILITY_MATRIX constant containing metadata (category, scope, risk, allowedRoles, overridable) as the single source of truth in @libs-foundational/core-models. Consumed by: CapabilityGuard (NestJS APP_GUARD) on all 3 backends, RoleAccessService (Angular signals) on all frontends, and a startup validator that prevents unprotected endpoints from shipping.

Options Considered:

  1. Approach A: Capability Matrix with typed enum and auto-generated docs
  2. Approach B: Extend existing DerivedFeatures (15 flags) to 50+ flags
  3. Approach C: Hybrid — Matrix generates DerivedFeatures automatically

💭 Reasoning

Root cause: The existing RBAC was binary (isAdmin vs non-admin) on the frontend and inconsistent on backends. DerivedFeatures existed (15 flags) but was used in ZERO frontend components. ~60+ backend-api controllers, ALL backend-ai-api intelligence endpoints, and critical backend-vitto-api operations (vault, skills CRUD, module generation, audit logs) had NO role checks. Approach A was chosen because: (1) TypeScript enum prevents typos at compile time, (2) startup validator prevents gaps at runtime, (3) 3-tier override resolution (user > space > matrix) maps directly to the core/personal layer architecture, (4) auto-generated table in gograb-dev serves as living documentation for all agents and developers, (5) the @RequireCapability decorator pattern mirrors existing @Roles/@Public patterns so migration is incremental. Approach B was rejected because modifying derivePermissions() with 50+ if/switch blocks is error-prone and doesn't provide a visual matrix. Approach C was rejected as unnecessary complexity — we can deprecate DerivedFeatures over time.


📁 Files Involved


🔗 Related


Auto-generated by MCP Smart Consolidation