# gorbac: a small role-based access control library for Go

> gorbac is a Go package that models identities, roles, permissions and role inheritance, with generics for the identifier type and assertion callbacks for context-dependent checks. It suits a service that needs role logic without a policy engine or a database of its own.

**mikespook/gorbac** — goRBAC provides a lightweight role-based access control (RBAC) implementation in Golang.

- Repository: https://github.com/mikespook/gorbac
- Stars: 1,676 · Forks: 180
- Language: Go
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/mikespook-gorbac

## Identities, roles, permissions and a parent chain

The model is stated in three sentences, and they are the whole specification. An identity has one or more roles, a role requests access to a permission, and a permission is given to a role. From that the documentation derives the shape of the data: a many to many relationship between identities and roles, a many to many relationship between roles and permissions, and roles that can have parent roles, inheriting permissions from them. The parent chain is the part that gives the library its character, because a role that inherits from two others is how you model a staff role that is also a reader, and the flattening of that graph at check time is the interesting bit of implementation. The package is deliberately small in scope. It is a library, not a service, it holds the role graph in memory, and it does not decide who an identity is, which means the identity to role mapping is something you maintain yourself, most likely in your database. The examples directory makes that split visible, with separate directories for persistence, for a playground and for a user-defined setup, which tells you the author expects you to supply the storage and the identity resolution.

## A two phase design: prepare the graph, then check it

The library is designed for use with two phases, preparing and checking, and the documentation says you can adjust the RBAC instance at any time and it is absolutely safe. That reassurance matters because the alternative design, where you build the role graph on every permission check, is simple and slow, while this one lets you load the graph once at startup and serve checks from it. Preparing means creating the instance, creating roles and permissions, assigning permissions to roles, adding the roles to the instance, and then setting the inheritance with parent links. Creating the instance takes a type parameter, and the simplest case is a string identifier type:

```go
rbac := gorbac.New[string]()
```

Roles and permissions are created by name, and permissions are attached to roles with an Assign call, which reads as the permission being given to the role rather than the role requesting it:

```go
rA := gorbac.NewRole("role-a")
pA := gorbac.NewPermission("permission-a")
rA.Assign(pA)
```

The inheritance is then expressed with SetParent for a single parent and SetParents for a slice of them, which is the call that turns a flat list of roles into a graph with branches.

## IsGranted takes a role, a permission and an optional assertion

The check is the part you will call on a hot path, and its signature is the whole design in one line:

```go
if rbac.IsGranted("role-a", pA, nil) &&
	rbac.IsGranted("role-a", pB, nil) &&
	rbac.IsGranted("role-a", pC, nil) &&
	rbac.IsGranted("role-a", pD, nil) {
	fmt.Println("The role-a has been granted permis-a, b, c and d.")
}
```

Three things to notice. The check is by role rather than by identity, so the identity to role mapping is the caller's problem, which keeps the library free of a user model. The permission is passed as a value rather than looked up by name, so a permission object is a first class thing you can hold in a variable. And the third argument is an assertion function that can be nil, which is how a purely static check and a context-dependent check share one code path. The assertion variant takes the RBAC instance, the identifier and the permission, and returns a bool, so you can implement rules the role graph cannot express, such as a check that depends on the request context. Passing nil for the common case means the callback is optional rather than mandatory, and that is what keeps the library usable for teams that do not need context rules at all.

## InherCircle catches the mistake that locks your graph

The utility functions are small and each one solves a specific operational problem. InherCircle detects circular inheritance in the role hierarchy, and it is the one you want in your startup path:

```go
rbac.SetParent("role-c", "role-a")
if err := gorbac.InherCircle(rbac); err != nil {
	fmt.Println("A circle inheritance occurred.")
}
```

A cycle in the role graph is a mistake that is easy to make, since parent links are set individually and nothing in SetParent complains, and the failure mode is a check that either loops forever or returns a wrong answer depending on the implementation. Calling InherCircle once after loading the graph turns that into a startup error rather than a production incident. The other utilities are about combining roles rather than about the graph itself. AnyGranted checks whether any of the specified roles have a permission, and AllGranted checks whether all of them do, both taking a slice of role identifiers:

```go
roles := []string{"role-a", "role-b", "role-c"}
if gorbac.AnyGranted(rbac, roles, pA, nil) {
	fmt.Println("At least one role has permission-a.")
}
```

Those two map directly onto the two shapes of question an application asks: does this person have access at all, and does this person have every one of these capabilities. The Walk function iterates through all roles in the instance and hands each one to a callback along with its parents, which is what you want for administration screens and for exporting the role graph.

## Generics let the identifier type be your own

The version 4 line is what introduced generics, and the identifier type is the parameter that matters. The simplest case is a string, but the documentation shows integer identifiers and a custom struct:

```go
// Using integer IDs
rbacInt := gorbac.New[int]()
role1 := gorbac.NewRole(1)
permission1 := gorbac.NewPermission(100)

// Using custom struct IDs
type RoleID struct {
	Name string
	Type string
}

rbacStruct := gorbac.New[RoleID]()
roleCustom := gorbac.NewRole(RoleID{Name: "admin", Type: "system"})
permissionCustom := gorbac.NewPermission(RoleID{Name: "read", Type: "data"})
```

The struct case is the interesting one, because it lets the type of an identifier carry structure. A role identified by a name and a type can be namespaced without a naming convention, so a role called admin of type system is a different role from one called admin of type tenant, and the compiler prevents mixing them up. That is a small design choice with a real effect on a multi-tenant service, where the usual failure is stringly typed tenant identifiers leaking into a global role. The permission interface is open as well, since you can implement gorbac.Permission for your own data structure, which means a permission is not necessarily the built-in type with a string name. The module path is github.com/mikespook/gorbac/v4, and the go.mod file declares go 1.24, while the README describes the generics version as requiring Go 1.18 and above, so the module file has moved on from the prose.

## Persistence is a blog post, not a feature

The most asked question about the library is how to persist the RBAC instance, and the answer given is a post on the author's blog titled how to persist gorbac instance rather than a feature in the package. That tells you where the boundary is. The library is an in-memory model with a well-defined shape, roles with parents and permissions, and the persistence layer is yours to write, which is the right division of labour for something that should sit on top of an existing schema. The examples directory has a persistence directory among its three, so there is working code to look at even if the mechanism is not built in. A second boundary is worth naming: the library answers a yes or no question about a role and a permission, and it does not record why. If your compliance requirements include an audit trail of authorization decisions, you will have to add it, because the check is a function call that returns a bool. That is a normal shape for a small library, and it is also the most common reason teams end up writing their own authorization layer instead. The authors are credited as Xing Xing, with a blog and a Twitter handle, and the licence is MIT as stated in the README.

## An old release list and a module that has moved on

The maintenance picture needs reading carefully, because the two signals point in different directions. The last push was on 2026-07-21, so the branch is being worked on. The release list, however, shows a single entry, 2.2.0 with the note about API changes in the helper methods, dated 2019-01-09, while the module path is v4 and the current code uses generics, which is a feature that did not exist in the Go version of 2019. The two are not contradictory, since a v4 module path with no v4 tag can exist, but the practical consequence is that there is no tagged version to pin against, and the documentation itself says the current version is under active development. That phrasing comes from the README and it is the most accurate description available. The build tooling is conventional and small, with a Makefile whose default target runs the tests, a coverage target that produces a coverage profile and an HTML report, a pprof target and a flamegraph target, and the test target running go vet before go test. The repository also carries a .travis.yml for continuous integration, per-file test files next to each source file, an example_test.go, and two documents that stand out in an otherwise conventional Go package, LLM_CODER_GUIDE.md and PROMPTS.md, which suggest the author has written guidance for coding agents working in the repository.

## Conclusion

gorbac is a good fit for a Go service whose authorization rules are genuinely role shaped, that wants the role graph in memory rather than in a policy service, and that can live with the fact that persistence is left to you. It is the wrong tool for attribute-based or contextual authorization, for anything that needs an audit trail of decisions, and for a team that wants a maintained policy engine rather than a small library, since the last tagged release in the list is 2.2.0 from 2019 even though the module path is now v4 and the last push was on 2026-07-21. Before adopting it, read the two phase design in the documentation, because the library assumes you prepare the role graph and then check it, and note that the module requires go 1.24 in go.mod even though the README talks about Go 1.18 and above. The library is MIT licensed.

## FAQ

### What model does gorbac use for roles and permissions?

An identity has one or more roles, a role requests access to a permission, and a permission is given to a role. That produces a many to many relationship between identities and roles, a many to many relationship between roles and permissions, and roles that can have parent roles which inherit permissions.

### How do I install gorbac and create an instance?

The package is installed with go get github.com/mikespook/gorbac/v4 and imported as that path. A new instance is created with gorbac.New[string]() when identifiers are strings, and the module file declares go 1.24 even though the README describes the generics version as needing Go 1.18 and above.

### What do the assertion functions in gorbac do?

They allow fine-grained permission controls beyond the role graph. The assertion receives the RBAC instance, the identifier and the permission, and returns a bool, and it is passed as the third argument to IsGranted, where nil means a plain static check.

### How do I detect circular role inheritance in gorbac?

The InherCircle function detects circular inheritance in the role hierarchy and returns an error if it finds one, so it can be called once after loading the role graph at startup. SetParent and SetParents do not report cycles themselves.

### How is a gorbac instance persisted?

The library does not include persistence. The documentation points to a post on the author's blog, HOW TO PERSIST GORBAC INSTANCE, for the approach, and the repository has an examples/persistence directory with working code. The examples directory also contains playground and user-defined setups.

## Sources

- [Issues](https://github.com/mikespook/gorbac/issues)
- [License: MIT](https://github.com/mikespook/gorbac/blob/master/LICENSE)
- [mikespook/gorbac on GitHub](https://github.com/mikespook/gorbac)
- [README](https://github.com/mikespook/gorbac/blob/master/README.md)
- [Releases](https://github.com/mikespook/gorbac/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mikespook-gorbac
