From College to Carta’s Access Control

Emma Vickery
Building Carta
Published in
6 min readNov 28, 2022

Carta’s transition from product to platform brings new requirements for how our customers interact with us. It also brought me, just a year out of college, an opportunity to implement a key part of those requirements.

Customers need greater control over who can view and change their data and have confidence that the right people have the right level of access. Administering access must be simple and appropriately restrictive. We recently redesigned how we define access controls to improve how employees, companies, and investors access the ownership network we give them. Using an in-house implementation of Google Zanzibar we are unlocking solutions for two shortcomings of our existing process: the single point of contact and over-privileged users.

The single point of contact problem

When a startup onboards with Carta, it often makes sense for a single person to administer their new account. As startups grow, they begin to use more of Carta’s platform functionalities. Ideally, new people take on some of that administrative work, but often that single point of contact remains responsible for tasks increasingly beyond their area of expertise. An HR administrator might be asked to approve equity exercises, sign certificates, and review financing investments. Presenting too much responsibility to a single user within the application causes mistakes to occur and tasks to be overlooked.

Our platform needed to better represent the growing and diverse responsibilities of our customers. Leveraging my teammates’ experience and skills, we formulated a plan of action. We could have chosen to extend our original implementation using Django models by enhancing our existing user role model or by creating an entirely new one, but we wanted a better foundation. We wanted to open the path for roles to define platform capabilities and actions, unlocking a rare opportunity to rebuild our permissioning fundamentals. So rather than using Django, we selected a far more capable framework to handle permissioning: Carta’s in-house AuthZ system. It was an opportunity for me as well when I accepted the project’s Lead Developer role.

Implementation using AuthZ

AuthZ, based on the Google Zanzibar white paper, lets us add and remove relations within a graph-like structure stored in Postgres. Relations are represented as tuples that define edges between nodes in the graph. These relation tuples represent an (actor, relationship, object). The actor has the specified relationship to the object. We can perform access checks that use a secondary index to see if an edge path exists from a starting node (actor) to the ending node (object).

Aaron Tainter, creator of Carta’s AuthZ system and Staff Engineer, was instrumental in getting our project off the ground. He took time to walk me through setup, building out the relation tuples, and the best methods for our use case. For more details on AuthZ, you can read from Aaron about its implementation here, and a great walkthrough of an example use case here.

Aaron helped define our relationships as user responsibilities using this relation tuple:

(user:id, responsibility_key, corporation:id)

This gives the user a specific responsibility for a specific corporation. In an AuthZ graph, it would look like this:

Adding additional user responsibilities for the corporation:

And that’s it — we now have our user responsibilities stored in AuthZ. This not only solves the single point of contact problem, but also unlocks the solution to our second problem: role-based permissioning.

Why unlock role-based permissioning?

Our current permission structure divides application functionality into Django views, each view defines a set of required access levels. The three most often used are:

  1. viewer
  2. editor
  3. administrator

An “administrator” can access all application functionality for a specific company, while “editor” and “viewer” have successively limited access. When a user requests a page or performs an action, the system calls a permission check method on the request. If their access level matches the view, then the request goes through.

But three roles are simply not enough. As our platform’s network of ownership grows, our customers need more sophisticated control to avoid granting users too much or too little access.

The Goldilocks problem

Imagine an employee who needs to approve stock option exercise requests. They need to view and effectively modify equity and stakeholder information. If their employer gives them “editor” permission, the employee will have access to the information they need, but also have access to functionality and information outside their responsibility. Here, the user has too much access to our platform. Giving them the “viewer” permission would prevent them from accessing improper functions, but they would not have access to the information they need. In this case, they would have too little access to our platform. We need to provide the right level of access, no more and no less.

Unlocking role-based permissioning with AuthZ

Our new user responsibility relation tuples now enable us to add role actions as their own relation tuples, fully defining each user’s role. The structure of an action relation tuple is the following:

(corporation:id#role_key, permission_level, corporation:id)

This action relation tuple lets us build a graph like this:

AuthZ can now define our complete permissioning graph.

Role explosion

Role-based Access Control (RBAC) systems, whether based on traditional technologies or something as advanced as AuthZ, can suffer from role explosion. As more and more functionality is added, there exists a growing temptation to add more and more roles. At some point, customers can lose sight of which role controls which function. Companies lose oversight and administration becomes unmanageable.

A lack of monitoring and defined roles causes disorder and role explosion.

At Carta, we’ve built an AuthZ visualizer called Concord to provide our product and engineering teams with a clear overview of how our roles and permissions are modeled. This provides the clarity and insight we need to prevent role explosion.

Additionally, we simplified the monitoring of our system by limiting our roles to eleven. Though we could add a few more if needed, fewer roles and disciplined product-owners prevent the system from becoming too complex.

Multiple roles can have connections to the same actions, but that can also drive role explosion in traditional RBAC systems. In our system, if Concord reveals an increasing number of overlaps between roles and actions to the product owner, we can avoid role explosion by adding an intermediary layer to the AuthZ graph between roles and actions. Roles would then point to this intermediary layer rather than directly to actions, and AuthZ won’t contain duplicate relation tuples.

Building for the future

AuthZ now stores each user’s responsibilities for every corporation on our platform, and that seemingly simple graph offers great power. It solves both our single point of contact problem and the Goldilocks problem without falling victim to the role explosion so common in role-based permissioning.

I had never pictured myself a year out of college leading a project at this scale. Alongside my amazing team, Carta continues to provide me with endless learning opportunities. I couldn’t imagine a better place to have started my career.

If you want to not only join others in solving hard problems, but lead them too, check out opportunities at Carta here!

--

--