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.

John Baek
John Baek
Founder, CollabOps
We Moved the Permission Model Onto a Graph — Why That Was the Decision

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, bob

Each 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 / deny

The 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.
  • Simulationwhat 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.

Tags#permission#authorization#architecture#product#devops