Why permissions can be dangerous and slow
In most CRMs, permissions are bolted on — after the product is shipped. That leads to:
Redundant checks in every component
Overfetching or underfetching sensitive data
Hard-to-debug “why can’t I see this?” tickets
Slow interfaces with unnecessary guards
We wanted permission logic that was:
Declarative, centralized, cache-friendly, and composable.
Our permission model in a sentence
Every object (deal, note, summary, etc.) has a defined scope, and every user has an evaluated role within that scope.
That means instead of checking user → object directly, we check user → role → scope. This gave us flexibility.
The schema
We created a simple permissions matrix:
Roles include:
owner
collaborator
observer
manager
This schema is indexed by object + user, so reads are lightning fast.
Evaluating permissions (inline logic)
On the server:
The frontend simply calls an endpoint like:
This avoids logic duplication — and lets us toggle logic centrally.
Smart caching for fast UI
Permissions are cached per session in Redis with a key like:
This lets us hydrate client UIs fast with role-based guards, without async waterfalls.
Feature toggles layered in
Since roles aren’t the only gate, we wrapped a feature toggle system around it. Some actions require both:
✅ permission level
✅ feature flag (e.g. AI Summaries enabled)
That allowed product to test features with specific cohorts without breaking the role model.
Final Thought
Access control is foundational — but it doesn’t have to be messy. With the right schema and structure, you can build a permission system that feels invisible to the user — and rock solid to the team.