Trovella Wiki

CASL Decision

Why Trovella chose CASL with hybrid RBAC+ABAC over Casbin, Permit.io, Oso, and custom JWT claims (ADR-004).

This page summarizes ADR-004. The full decision record is in docs/architecture/decisions/004-authorization-casl-rbac-abac.md.

Decision

Library: CASL (isomorphic JavaScript, MIT licensed, ~12KB, $0 at any scale)

Model: Hybrid RBAC+ABAC -- roles decide most access, attribute checks decide sensitive operations

Roles: Owner, Admin, Member (per organization)

Why CASL

The authorization system needed to:

  • Work identically across Next.js server, browser, and future React Native -- one permission definition, not separate implementations per platform
  • Layer on top of PostgreSQL RLS as an additional defense, not a replacement
  • Start simple (RBAC with 3 roles) and evolve to handle attribute-based conditions without a rewrite
  • Be declarative and centralized -- all permissions in one file, auditable, AI-agent-friendly

CASL scored highest on solopreneur-weighted criteria:

CriterionCASLCasbinPermit.ioOsoCustom JWT
Isomorphic JSYesNoNoNoYes
$0 costYesYesNo ($100-500+/mo)PartialYes
React integration@casl/react <Can>NoneNoneNoneManual
Declarative rulesSingle fileCONF DSLDashboardPolar DSLScattered checks
Maintenance burdenLowMediumLow (managed)MediumHigh
Overall score9/106/107/106/105/10

Alternatives Rejected

Casbin -- No native React integration, not truly isomorphic (browser library has different API from node-casbin), CONF policy DSL is unnecessary complexity for 3 roles.

Permit.io -- $100-500+/month vendor dependency on the critical path. PDP sidecar containers add infrastructure overhead. Full-featured dashboards are unnecessary for a solo developer.

Oso -- Polar is a new language to learn, no React integration, no isomorphic JavaScript. Would be more competitive for a purely server-rendered application.

Custom JWT claims -- Zero dependencies but permission checks scatter throughout the codebase. Every new feature requires finding and updating scattered role === 'admin' checks. AI agents are especially prone to implementing inconsistent check patterns.

SpiceDB / OpenFGA (ReBAC) -- Overkill for current scope. Requires a separate relationship graph database. Deferred to Phase 3 if complex sharing hierarchies emerge.

Implementation Trade-offs

String subjects instead of typed CASL subjects

CASL's InferSubjects type utility was incompatible with the project's TypeScript configuration when using condition objects. Plain string subjects ("Organization", "Member", "ResearchPlan", etc.) were used instead. This means CASL's MongoDB-style conditions cannot be used on the ability object itself.

ABAC conditions as explicit router checks

Because string subjects do not support CASL condition objects, the hybrid approach splits cleanly:

  • CASL handles RBAC -- role-to-permission mapping in defineAbilityFor()
  • Router code handles ABAC -- contextual/ownership checks like "admin cannot modify owners" or "member can only delete self"

This separation keeps CASL simple and makes ABAC logic visible in the router where the context is available. See Router Enforcement for examples.

Personal org restrictions via cannot() overrides

Regardless of role, personal organizations always deny create Member and manage Invitation. This is enforced via cannot() overrides applied after the role-based switch block. The alternative -- a separate ability builder for personal orgs -- would have duplicated the role logic.

Risks

  • String subjects limit future expressiveness. If complex conditions become necessary, migrating to typed subjects would require updating all ability definitions. Mitigated by the ABAC-in-router pattern, which does not depend on CASL's condition system.
  • AI agents adding allowlist exceptions. An agent could add entries to AUTHORIZED_PROCEDURE_ALLOWLIST to silence the architecture test. Mitigated by the AI review checklist, which specifically checks for allowlist modifications.
  • Role explosion at scale. If the company tier grows to 15-20 specialized roles, the single-file RBAC approach may become unwieldy. Mitigated by the planned evaluation of Permit.io or SpiceDB at Phase 3.

Phase 3 Upgrade Path

If Trovella evolves toward real-time collaboration with complex sharing hierarchies, the authorization model would be evaluated for upgrade to:

  • Permit.io -- if a non-technical admin needs to manage permissions via UI
  • SpiceDB -- if Google Docs-style sharing with relationship hierarchies is needed
  • Typed CASL subjects -- if the number of ABAC conditions grows significantly and centralizing them in the ability builder becomes worthwhile

On this page