Library / SDK
ory/ladon avatar
ory/ladon

ory/ladon is called stable, but its install snippet still exports GO111MODULE

A SDK for access control policies: authorization for the microservice and IoT age. Inspired by AWS IAM policies. Written for Go.

2,457 stars225 forksGoApache-2.0

At a glance

What is it?
A Go access control library that answers who is able to do what on something given some context, modelled on AWS IAM policies and shipped with in-memory storage only. The file and the repository disagree in small ways that all point the same direction: the prose describes a version of the project from an earlier decade than the one in go.mod and the tree.
Who is it for?
Ladon is a reasonable fit when you want policy documents you can read and diff, with conditions evaluated in Go, and you are willing to write the server, the transport and the persistence yourself, because the library supplies none of the three. Check four things first.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 45 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • ory/ladon2,457 ★ · -2
Star history
Repository2026-09-292026-11-14
ory/ladon2.5k2.5k

-2 Stars in the last 46 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The install snippet and the stated Go floor predate go.mod

The Installation section is two commands:

code
export GO111MODULE=on
go get github.com/ory/ladon

Both belong to a specific moment in the toolchain. Modules are enabled by default in modern Go, so explicitly setting GO111MODULE=on is usually unnecessary; the variable can still change module behavior when set to another value. The `go get` form is how a library used to be installed before installing and adding became separate operations, which is why the surrounding text now reads as a historical note rather than an instruction. Directly above it the file says this library works with Go 1.11+, while `go.mod` declares `go 1.19`. So the prose and the module file disagree about the floor by eight minor releases, and the prose is the one a newcomer reads. The dependency file is the one the toolchain obeys. Nothing else in the visible file gives a Go version, so a reader has two numbers and no way to tell which one a build actually enforced, and no statement anywhere about which release of Go the test suite runs on. `go.sum` sits beside `go.mod`, so the dependency graph is pinned and reproducible even though the human-facing version claim is not. That is the pattern across this file: the machine-readable metadata is careful and current, and the instructions a person reads were written years earlier.

The semver warning still describes the phase before 1.0

The Installation section ends with a versioning note. It says Ladon uses semantic versioning, and that versions beginning with zero, giving `0.1.2` as the example, might introduce backwards compatibility breaks with each minor version, linking to the part of the semver specification that covers the initial development phase. The current version is not in that phase. Releases run v1.1.0 on 2020-01-08, v1.2.0 on 2020-04-30 and v1.3.0 on 2024-03-06, so the library passed 1.0 before the second of those three was cut. A caveat that describes the 0.1.x contract sitting under instructions for 1.3.0 tells you the paragraph was written once and carried forward. It is not wrong in a way that breaks a build, but it does mean the compatibility promise the file makes is the one for a version that no longer exists, while the actual guarantee for 1.x is left to the specification itself. The release dates tell the same story from the other side. Three years separate v1.1.0 from v1.2.0, six weeks separate v1.2.0 from v1.3.0, and roughly two years pass between v1.3.0 and the last recorded push on 2026-09-30. The root carries a HISTORY.md, so the release notes exist somewhere in the tree, but the readme's own version paragraph is the one most readers will read and it was written for the earlier period.

Two uuid libraries, a 2018 error package and a zero-point internal dependency

`go.mod` requires eight modules directly and three more indirectly, and several of the pins are worth reading before you depend on them. Two uuid packages appear in one dependency list: `github.com/pborman/uuid` as a direct requirement and `github.com/google/uuid` marked indirect. An error package is pinned at `github.com/pkg/errors v0.8.0` and the assertion library at `github.com/stretchr/testify v1.2.2`, both of which sit well behind the versions of their own lines. `github.com/ory/pagination` is required at v0.0.1, which by the semver convention this very file discusses means no compatibility promise at all, and it is an internal project of the same organisation. A cache library, `github.com/hashicorp/golang-lru`, is pinned at v0.5.0. The one entry with a modern version is `github.com/dlclark/regexp2 v1.2.0`, a regex engine that is not the one in the standard library, and a mocking package at v1.6.0 that appears as a direct requirement rather than a test-only one. That mocking requirement has a visible consequence: the root carries `manager_mock_test.go`, `manager_helper_test.go` and `manager_all_test.go`, so interfaces are faked through generated mocks rather than small hand-written stubs. The dependency list is also split into two require blocks, direct and indirect, which is the normal shape and the clearest sign in the file that go.mod is the maintained artefact while the prose around it is not.

The first REST example stops inside its own JSON

The file is explicit that there is no server here. It says Ladon does not come with an HTTP or server implementation, that it does not restrict JSON either, and that deciding between Protobuf, RESTful, HTTP, AMQP or something else is the reader's job, ending with the phrase it's up to you to write the server. What follows is offered as an illustration of what a RESTful flow could look like, against an artificial endpoint. The first request POSTs a policy document to a `/policies` path on a host called my-ladon-implementation.localhost. That request is cut off. The fence closes in the middle of the conditions object, on the opening of the word `opti`, so the JSON in the file is not parseable and the curl invocation never completes. The second request does finish, and it is the one that answers the question. So the illustrative flow has one working half and one broken half, and the broken half is the one that creates the policy the working half then queries. The paths themselves are a naming suggestion rather than a contract: `/policies` for storage and `/warden` for the decision, both under the same artificial host. Nothing in the library defines either route, and no authentication, authorisation or input validation appears anywhere in the example, because there is nothing here to authenticate against. A reader who copies the curl block is copying a shape, not a deployment.

The worked example writes a host address as a network prefix

The policy document is the centre of the library and the file shows it twice, once standalone and once inside the truncated curl. It has a description, a list of subjects, a list of actions, an effect of allow, a list of resources, and an optional conditions object. Subjects, actions and resources use a colon-delimited convention with angle brackets standing in for alternation, so the entry `users:<peter|ken>` is a pattern rather than two subjects, and `resources:articles:<.*>` matches any article. The conditions object is keyed by the name of a context field, and this example keys it on `remoteIP` with a type of CIDRCondition and a cidr option of `192.168.0.1/16`. That is a host address carrying a network mask; a sixteen-bit prefix on a dotted address names a network, not the single host that address belongs to. The access request that follows supplies a remoteIP of `192.168.0.5`. Whether the library normalises a host address before matching is not stated anywhere in the file, so the example cannot be checked from the example alone. It is the first thing anyone copying a condition from this document has to verify against the implementation.

The warden answers with a boolean and no explanation

The access request is small: a subject, an action, a resource, and a context object holding the remoteIP. The response is smaller:

code
{
    "allowed": true
}

One boolean, with no policy identifier, no matching rule, no reason and no list of the conditions that were evaluated. For a decision engine that is a deliberate minimalism, and it is also the whole observable behaviour of the component the library is named around. The file's own table of contents lists a section called Access Control (Warden) and another called Audit Log (Warden), and it is the second one that has to carry the explanation. The root confirms that split at the file level: `warden.go` and `warden_test.go` for the decision, `audit_logger.go`, `audit_logger_info.go` and `audit_logger_noop.go` for the record of it, plus `metric.go` and `metric_noop.go`. Metrics have a no-op variant and an audit logger has one too, so both are optional and silent by default. A caller who wants to answer why an action was refused has to attach a logger rather than read anything from the answer.

The docs point at two different organisation paths for dockertest

The line above the table of contents says Ladon uses ory-am/dockertest for its tests and sends readers to `github.com/ory-am/dockertest` for how to set up a testing environment. Further up, in the list of related projects, the same tool appears as `github.com/ory/dockertest` with the note about writing better integration tests. Two paths, one organisation name, one namespace apart, in the same file. The badge row above both is from the same era: a build badge pointing at travis-ci.org, a coverage badge pointing at coveralls.io, a report card pointing at goreportcard.com, and documentation pointing at godoc.org. The tree agrees with the oldest of them, since the root carries a `.travis.yml` rather than a workflow directory. Godoc is the retired documentation host, and the rest of the governance files in the root, a MAINTAINERS file, a security policy, a code of conduct, a contributing guide, an issue template and a history file, are current. So the contributor-facing scaffolding has been kept up while the build badges and the install snippet have not. The one field that is not a link at all is the project homepage, which points at the organisation's marketing site with three campaign parameters attached to it rather than at documentation for this library. Everything else at the top of the file is either a badge or a link to another repository, so the file has no address of its own for anyone looking for API documentation beyond the retired godoc host.

The library's Go files sit at the repository root beside the docs

The tree puts source where a repository usually keeps its readme. At the top level sit `ladon.go`, `warden.go`, `policy.go`, `matcher.go`, `matcher_regexp.go`, `condition.go`, `context.go`, `const.go`, `errors.go`, `manager.go`, `metric.go` and the audit logger files, with `go.mod`, `go.sum` and `LICENSE` alongside them. Only four directories hold real structure: `compiler/`, `manager/`, `docs/` and the configuration directories. Conditions are one file each, and the seven names match the seven condition types in the file's own table of contents exactly, from `condition_cidr.go` through `condition_string_pairs_equal.go` and `condition_resource_contains.go`, plus a test file for most of them. Two matcher files sit side by side, which suggests more than one matching strategy without the file saying what the second one is. Storage is the other half of the picture, and it is the thinnest part: the project officially ships an in-memory storage implementation, and the one database adapter named, for CockroachDB, is a community project under a different account. Three root directories describe the toolchain rather than the library: `.bin/`, `.reports/` and a file called `.reference-ignore`. Whatever lives in those is build output and coverage data kept in the tree rather than ignored, which is consistent with a project whose `.travis.yml` and coverage badge come from an earlier generation of Go tooling. Nothing in the visible file explains what any of the three is for.

Editorial conclusion

Ladon is a reasonable fit when you want policy documents you can read and diff, with conditions evaluated in Go, and you are willing to write the server, the transport and the persistence yourself, because the library supplies none of the three. Check four things first. The install snippet and the stated Go floor predate the module declaration in go.mod, so treat the readme as describing an earlier release. Official storage is in memory, so policies do not survive a restart and the one adapter that would fix that is community-supported. The decision you get back is a bare boolean, so wire the audit log warden if you need to explain a denial. And the first REST example in the file stops in the middle of its own JSON, so copy the policy document rather than the request.

Frequently asked questions

What question does ory/ladon answer?

Who is able to do what on something, given some context. A subject name, an action name, a resource name, an effect of allow or deny, and an optional context carrying things like a remote IP or a request date. Policies are JSON documents and requests are JSON objects.

Does Ladon ship a database?

It officially ships an in-memory storage implementation. The CockroachDB adapter is described as community-supported and lives outside the repository, so there is no officially supported persistent store in the project itself.

Which Go version does Ladon need?

The file says the library works with Go 1.11+, while go.mod declares go 1.19. The install snippet exports GO111MODULE=on and runs go get github.com/ory/ladon.

Does Ladon include an HTTP server?

No. The file says Ladon does not come with an HTTP or server implementation and does not restrict JSON, and that choosing between Protobuf, RESTful, HTTP, AMQP or another protocol, and writing the server, is the reader's job.

Which regular expression engine does Ladon use?

The dependency list carries github.com/dlclark/regexp2 at v1.2.0 rather than relying on the standard library alone, and the only entry under Limitations in the file is Regular expressions. The root holds matcher.go and matcher_regexp.go.

When was the last ory/ladon release?

v1.3.0 on 2024-03-06, after v1.2.0 on 2020-04-30 and v1.1.0 on 2020-01-08. The last recorded push to the repository is 2026-09-30, and the file states that Ladon is now considered stable.

Official sources

  1. License: Apache-2.0
  2. ory/ladon on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ory-ladon.svg)](https://hysenlabs.com/projects/ory-ladon)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/ory-ladon.svg)](https://hysenlabs.com/projects/ory-ladon)

Was this useful?