Cadrena DSL is a small, deterministic authorization language for defining relationship-based entities, reusable permissions, executable actions, and parameter-aware guards.
The repository is a Go library. CLIs, gateways, servers, and control planes import it as a package; the core repository does not ship a general-purpose command.
entity user {}
entity group {
relation manager @user @group#member
relation member @user @group#member
permission access = member or manager
}
entity organization {
relation administrator @user @group#access
relation member @user @group#access
permission admin = administrator
permission access = member or administrator
}
entity document {
relation organization @organization
relation viewer @user @group#access
relation editor @user @group#access
action view = viewer or editor or organization.access
action edit = editor or organization.admin
}
Parameter-sensitive actions can add a guard without turning the relationship language into a programming language:
guard payment.refund {
deny when arguments.amount_minor > 100000
require_approval finance_approver when arguments.amount_minor >= 10000
allow otherwise
}
A guard is evaluated only after the referenced graph action grants access. Final precedence is:
deny > require_approval > allow > no matching allow = deny
go get github.com/cadrena/dslprogram, err := dsl.Compile("policy.cdr", source)
if err != nil {
// Report source-located diagnostics.
}
result, err := program.Check(ctx, dsl.Request{
Subject: dsl.EntityRef{Type: "user", ID: "alice"},
Resource: dsl.EntityRef{Type: "document", ID: "roadmap"},
Action: "view",
}, tuples)TupleReader keeps the evaluator storage-agnostic. NewTupleSet provides an
immutable in-memory implementation for tests, examples, and small embedded
datasets. Results include a stable decision/reason, matched action, matched
guard effects, required approvals, and a deterministic graph trace.
CLIs can discover externally checkable entry points without inspecting the AST:
for _, action := range program.Actions() {
fmt.Printf("%s.%s guarded=%t\n", action.Entity, action.Action, action.Guarded)
}Untrusted policy input is bounded by defaults from DefaultLimits. Callers can
lower or explicitly raise them with CompileWithLimits:
limits := dsl.DefaultLimits()
limits.MaxEntities = 64
program, err := dsl.CompileWithLimits("policy.cdr", source, limits)See example_test.go for executable package usage,
examples/ for complete policies, and SPEC.md for
the normative v1 grammar and semantics. Versioned, implementation-neutral
decision fixtures live under conformance/v1.
- Simple for policy authors; strict for implementers.
- Deny by default.
- Deterministic output and explain traces.
- No embedded Go, JavaScript, CEL, Rego, functions, loops, or plugins.
- No network, database, clock, or global mutable state in the evaluator.
- Relationship data is supplied through a small reader interface.
- Parser and evaluator errors carry stable, testable information.
- Library first: no required storage implementation or service runtime.
go test ./...
go test -race ./...
go vet ./...
gofmt -l .
go test -fuzz=FuzzParseNeverPanics -fuzztime=10sVersion 1 is stable. The language and compiled-artifact surfaces follow semantic
versioning from v1.0.0 and are protected by conformance tests.
Apache License 2.0. See LICENSE.