Most SaaS platforms enforce plan limits by trust ("hope nobody hits the limit") or by post-fact billing ("charge them when they go over"). Apragya AI enforces by hard gates - if your plan doesn't include a feature, you literally can't use it. This guide explains the model so admins don't try to RBAC-grant a feature their plan doesn't include and then wonder why it doesn't work.
The two-layer model
- Layer 1: Plan-feature gate. Does your plan even include this capability? Stored in pricing_plans.feature_flags (JSONB), edited by SaaS Admin via the Edit Plan modal
- Layer 2: RBAC gate. Within what the plan allows, does this user's role have the right permission? Stored in the per-app permission matrix, edited by Org Admin
- Plan-feature gate runs FIRST. RBAC gate runs SECOND - only on the subset of features the plan includes
Why the order matters
RBAC's can() resolvers intentionally short-circuit to TRUE for admin-tier roles (SaaS Admin, Platform Developer, Org Admin). That's correct for ROLE checks - admins should be able to do anything within their scope. But it's catastrophically wrong if a developer reaches for can() to gate a PLAN feature - the admin bypass would leak paid capability to underpaying tenants. The plan resolver (useFeatureGate.can / check_feature) has no admin bypass.
What's gated
- Vibe Studio (Chat-to-Build) - requires studio.local_llm flag on the plan
- Multi-region deployment - requires platform.multi_region
- SSO (SAML/OIDC) - requires security.sso
- Audit log export to SIEM - requires audit.siem_export
- Custom roles - requires rbac.custom_roles (built-in roles always work)
- Add-on AI Credit packs - requires billing.addon_packs
- And ~25 other capability keys; see Admin Settings > Pricing Plans > Edit Plan for the full list
Strict mode
A missing feature flag in pricing_plans.feature_flags is treated as OFF, not ON. The platform refuses to invent permissive defaults - if SaaS Admin hasn't explicitly granted the capability on the plan, it's gated. This is the safe default for any new feature; it requires the SaaS Admin to deliberately enable, not deliberately disable.
Common gotchas
- "I'm an Org Admin and I can't do X" - check the plan first. Org Admin bypasses RBAC, not plan gates
- "This feature worked yesterday" - someone changed the plan, or you got downgraded. Check pricing_plans audit history
- "The sidebar item disappeared" - sidebar hides off-plan items entirely (it doesn't show them disabled). Easy to mistake for a regression
- "My API call returns 403" - the error body's detail field carries the feature key that gated it. Grep your code for that key + the plan to confirm