We Moved the Permission Model Onto a Graph — Why That Was the Decision
Four months of redesigning permissions from policies fragmented across tools to a single graph. Why RBAC fell short, why ABAC wasn't the answer, and why graph-based delegation was load-bearing.
Last May a customer CISO asked us a single question.
"Where do I encode that the release manager can close issues only at the moment right before a production deploy?"
The answer was three places — our permission system, the issue tracker, the deploy tool. Three systems answering with three different models. That day we decided to redesign the permission model from scratch. Four months later it shipped.
What was missing — RBAC
We started with RBAC (Role-Based Access Control). Roles are bundles of permissions, users have roles. Same model as Linux user/group/permission.
First place RBAC breaks: conditional permissions. "Release manager can close issues only right before a production deploy." RBAC has nowhere to express the time/state condition.
Second break: delegation. "I'm on vacation, please give my deploy permission to X temporarily." RBAC's user-role mapping is permanent. There's no first-class citizen for temporary delegation.
Why ABAC wasn't the answer
We then evaluated ABAC (Attribute-Based Access Control) — express policy as a function of subject, resource, and environment attributes. AWS IAM and OPA Rego follow this pattern.
ABAC handles conditional permissions well. It's weak on delegation, audit, and revocation. Policies are declarative; the graph of who acted under whose authority for what doesn't form automatically.
The bigger problem was debugging. Past 100 policies, "why was this user denied?" takes minutes to answer. Enterprise security reviews regularly request policy simulation. With ABAC, that becomes code analysis.
Our answer — delegation on a graph
Four months of work compressed to one sentence:
Permission is not a node in the graph. It's an edge.
User (alice) ─────[role: release-manager]────▶ Project (X)
│
├─ scope: env=prod
├─ window: deploy-pending state
└─ granted-by: bob (CTO), 2025-05-12
User (alice) ─────[delegated-to: charlie]────▶ Project (X)
│
├─ for: 2025-08-01 ~ 2025-08-15
├─ scope: same as original
└─ revocable: alice, bobEach permission is a graph edge. Edges carry attributes — scope, window, granted-by, revocable. Delegation is a second edge that temporarily redirects the original to another user.
This is what gave us RBAC + ABAC simultaneously.
- What RBAC did well — simple mapping. Expressed as role-as-edge.
- What ABAC did well — conditions. Expressed as edge attributes (scope, window).
- What neither did well — delegation, revocation, audit. Naturally answered by graph traversal.
"Release manager closes issues only right before a deploy" — answered
The CISO's question collapses to one place:
User (release-manager-X)
─[permission: close]→ Issue
scope: project=ProjectX
window: deploy-pending state of ProjectX
Close request →
graph traversal →
edge found → evaluate window condition →
is current state "deploy-pending"? →
allow / denyThe issue tracker, the deploy tool, and our permission system all read the same graph. Encode policy in one place, all three follow.
The expensive four months
Six weeks of code. The other ten weeks were graph migration of existing RBAC policies + delegation workflow interviews per customer.
We had 723 RBAC policies to migrate. Auto-conversion handled 60%. The remaining 40% had ambiguous original intent — "why does this role have this permission?" answered with "the original author left two years ago." We couldn't migrate without reconstructing the intent. That reconstruction was the most expensive part.
What became possible
On top of this single model, the following became possible with no additional infrastructure:
- AI agent permissions — agents hold the same kind of edge humans do. They live on the same graph, get audited the same way.
- Just-in-time elevation — no permission normally; temporary elevation only at PR review. Naturally expressed as a window condition.
- Delegation chains — A→B delegate, B→C re-delegate. One traversal gives the full chain.
- Simulation — what could alice do if she had bob's permissions? answered as a graph query. With ABAC this requires building a separate policy simulation engine.
So when should you move your permission model to a graph?
The threshold for a graph-based permission model is the point where your RBAC system crosses 100 policies and "why was this user denied?" starts taking minutes to answer. A graph isn't the right answer from day one. RBAC is enough to start; past the threshold, maintaining one graph costs less than dragging along the union of RBAC + ABAC. If answering denial questions already takes minutes in your system, that signal has arrived.
Related posts
What Should a 100 Million Won Software Contract Change for the Customer?
The contract size a vendor wants and the reason a customer pays are two separate explanations. Working through a hypothetical ROI calculation to the end shows why counting only the hours saved per deployment falls short, and which items belong in the same table.
John Baek
The Customer Said They Hate Jira. Do They Need a New Jira?
Complaints about a familiar tool are easy to hear. But a complaint is not the same as a buying problem. What one conversation taught me about separating the friction a customer mentions from the problem their organization actually has to solve, and the four questions to ask instead.
John Baek
More Features, and a Harder Question: Why Us?
Issues, repositories, reviews, CI/CD, docs, AI. Every new feature made the product description longer, but not the reason to choose it today. So we changed the question: what decision can the customer not make right now? Why Change sits at the center of CollabOps.
John Baek