Trovella Wiki

Authorization

How Trovella uses CASL to enforce role-based and attribute-based access control across the application layer.

Authorization in Trovella is the application layer of a four-layer defense-in-depth model. It determines what an authenticated user is allowed to do within a specific organization, using CASL for declarative role-based permission checks and imperative attribute-based checks in router code.

How It Works

Every feature endpoint uses authorizedProcedure, which builds a CASL ability from the user's role and organization type. Router handlers then check ctx.ability.can(action, subject) before performing operations. This is the third layer in the defense model:

  1. Client -- UI elements hidden based on permissions (defense-in-depth, not security)
  2. Proxy -- route-level protection via Caddy
  3. Application -- CASL permission checks in tRPC routers (this topic)
  4. Database -- RLS policies enforce tenant isolation regardless of application logic

Pages in This Topic

CASL Decision

Why CASL was chosen over Casbin, Permit.io, Oso, and custom JWT claims. The ADR with full alternatives analysis and trade-offs.

Ability Definitions

The defineAbilityFor() function, its type signatures, the RBAC role switch, personal org restrictions, and the unknown-role fallback.

Role Permission Matrix

Complete matrix of what each role (owner, admin, member, default) can do with each subject, including personal org overrides.

Router Enforcement

How routers use ctx.ability for RBAC checks, how ABAC conditions are handled as imperative checks, and the architecture test that enforces authorizedProcedure usage.

Key Concepts

RBAC (role-based access control) handles 90% of authorization decisions. The user's role within an organization (owner, admin, or member) determines their permissions. CASL resolves these from the session with zero database lookups beyond the initial member record fetch.

ABAC (attribute-based access control) handles the remaining 10%. These are contextual checks like "an admin cannot modify an owner's role" or "a member can only remove themselves." ABAC conditions live in router code as explicit if statements, not in CASL's condition system.

Pure function ability builder. defineAbilityFor() takes { userId, role, orgType } and returns a CASL ability object. No database access, no side effects, deterministic output. This makes it safe to call in any context -- server, client, test -- and easy for AI agents to reason about.

On this page