SYS:PRODUCT // Permissions Modeling
Stop maintaining role tables. Start modeling relationships.
IPL is a declarative language for defining authorization as relationships between users, resources, and organizations. Instead of sprawling role matrices and hardcoded if-statements, you write a schema that describes how your system actually works — and InferaDB derives every permission decision from it. Readable by engineers, auditable by compliance, version-controlled like the rest of your code.
Role tables do not scale. You already know this.
RBAC starts clean: admin, editor, viewer. Three roles, a simple lookup table, and you ship. Then the product grows. Teams need scoped access. Permissions need to inherit across organizational hierarchies. One customer needs deny-override rules for compliance. Another needs conditional access based on resource attributes.
So you add team-specific roles. Then role hierarchies. Then exception tables. Then a "super admin" role that bypasses everything because nobody can reason about the matrix anymore. Every permission change becomes a code change, a migration, a deploy, and a hope that you did not break something for a different customer.
The role explosion problem is not a skill issue
Organizations with mature RBAC systems routinely maintain hundreds or thousands of roles. Not because the engineers were careless, but because RBAC forces you to enumerate every combination of user, resource, and access level as a discrete role. The complexity is inherent to the model. You cannot engineer your way out of a paradigm problem.
Combinatorial role growth
Each new resource type or access level multiplies the number of roles. Ten resource types with four access levels is forty roles before you add team scoping or tenant isolation.
No inheritance model
RBAC has no built-in concept of permission inheritance. Org admins should be able to manage team resources — but in a role table, you have to wire that up manually for every resource type.
Impossible to audit
"Who can access this document?" In an RBAC system with role hierarchies and exception tables, answering this question requires traversing multiple tables, joining on conditions, and hoping the logic matches what the code actually enforces.
Model relationships, not roles
Instead of asking "what role does this user have?", ask "what is this user's relationship to this resource?" A user is a member of a team. That team owns a project. That project contains a document. The user can view the document — not because someone assigned them a "project-document-viewer" role, but because their relationship to the organization implies it.
Relationships compose naturally. Org membership implies team access. Team ownership implies project access. Project membership implies document access. You define these rules once, and InferaDB derives every permission from the graph of actual relationships in your system. This is the paradigm behind Google Zanzibar — the system that handles authorization for Google Drive, YouTube, and Cloud IAM — made accessible through a readable, declarative language.
Permissions follow structure
Your authorization model mirrors your product's actual hierarchy. When you add a new team to an org, its members automatically get the right access to the right resources. No role assignments needed.
One model, every question answered
"Can this user edit this document?" "Who can access this project?" "Why was this request denied?" All answered by traversing the same graph. No scattered role tables, no duplicated logic.
Changes without code changes
Adding a new access level or resource type means updating your schema — not writing migrations, updating middleware, or deploying new application code. The schema is the authorization logic.
Nine lines. An entire permission model.
IPL reads like a specification, not code. You define types, their relationships, and how permissions derive from those relationships. Here is a complete model for a SaaS product with organizations, teams, and documents:
type organization {
relation admin
relation member
}
type team {
relation parent: organization
relation member
relation admin = admin from parent
}
type document {
relation parent: team
relation viewer
relation editor
relation owner
relation can_view = viewer | editor | owner | viewer from parent
relation can_edit = editor | owner
relation can_admin = owner | admin from parent
}
That is the entire permission model. Organization admins can administer any document in any team under their org. Team viewers can view all documents in their team. Document editors can edit their specific document. Inheritance, composition, and access levels — all declarative, all testable, all versioned in source control alongside the code it protects.
What this replaces
In a traditional system, this model would require: an organizations table, a roles table, an organization_roles join table, a team_members table, a document_permissions table, application-level inheritance logic, and middleware to stitch it all together. With IPL, the schema is the logic. There is nothing else to maintain.
Expressive enough for real authorization problems
Authorization in production is not just "can user X do action Y." It involves inheritance hierarchies, conditional access, explicit denials, and custom business logic. IPL handles all of these with a small set of composable primitives.
Union, intersection, exclusion
Compose permissions like building blocks. Union grants access if any relationship matches. Intersection requires multiple relationships. Exclusion removes access for specific conditions. Three operators that cover the full space of permission logic.
Tuple-to-userset inheritance
Permissions flow through relationships automatically. An org admin inherits admin access to every team and document underneath — not through role duplication, but through graph traversal. Define the rule once, and it applies everywhere the relationship exists.
Forbid rules
Explicit deny that overrides all permits. When compliance requires that a suspended user cannot access anything regardless of their relationships, a forbid rule makes that guarantee structural. No workaround, no exception, no "unless."
WASM conditions
When declarative rules are not enough, attach custom logic written in any language that compiles to WebAssembly. Time-based access, attribute checks, geo-restrictions — sandboxed, deterministic, and evaluated at the same speed as native rules.
Three-pass validation
Before a schema goes live, IPL validates it in three passes: type checking catches structural errors, conflict detection flags rules that contradict each other, and coverage analysis identifies unreachable permissions. Bugs caught before deploy, not in production.
Wildcards
Public resources should not require individual relationship tuples for every user. IPL supports wildcard subjects for public or organization-wide access — without sacrificing the precision of the relationship model for everything else.
Schemas ship like code, because they are code
IPL schemas are plain text files that live in your repository. They go through code review, CI validation, and controlled rollout — the same workflow you already use for application code. No admin consoles, no manual configuration, no drift between environments.
Version-controlled schemas
Push schema changes via CLI or Terraform. Every change is a commit with a diff, a reviewer, and a merge decision. Roll back a permission model the same way you roll back a feature.
Decision simulator
Test your permission model before deploying it. The decision simulator evaluates checks against your schema with sample data, so you can verify that the new model does what you intend before it touches production.
Zero-downtime migrations
Schema updates apply without interrupting authorization checks. InferaDB validates the new schema, prepares the transition, and switches atomically. No maintenance windows, no permission gaps.
Expansion traces
Every authorization decision includes a trace showing exactly which relationships were traversed to reach the result. When a check returns "allowed," you can see the full path. When it returns "denied," you can see exactly where the path ended.
How teams model authorization with IPL
SaaS with customer-managed roles
Tenants define their own roles and permissions using relationships, sandboxed to their org. No code changes needed — new roles are just new relationship tuples.
Document collaboration
Model Google Docs-style sharing — viewers, editors, owners, link access, inherited permissions from parent folders. All expressed in a single schema.
Microservice authorization
Define a shared schema across services. Each service checks permissions against the same graph, eliminating policy drift and duplicated logic.
Stop building permission systems. Start shipping features.
Your authorization model should be smaller than your README.
A declarative schema that replaces role tables, inheritance logic, and permission middleware — validated before deploy, traceable in production, and version-controlled like everything else.