RBAC · ABAC · ReBAC · PBAC — one engine

Authorization, declared.

Four authorization models behind one Check API. A purpose-built .warden config language. A language server, VS Code extension, and CLI in the box.

$go install github.com/xraph/warden/cmd/warden@latest
warden · vscode-warden 0.1.0
warden lsp
● LSP ready
1warden config 1
2tenant acme
3 
4permission "doc:read" (document : read)
5permission "doc:write" (document : edit)
6 
7resource document {
8 relation owner: user
9 relation viewer: user
10 permission read = viewer or owner
11}
12 
13role viewer {
14 grants = ["doc:read"]
15}
16 
17role editor : viewer {
18 grants += ["doc:write"]
19}
warden-lspno problems
UTF-8 · LF · Warden · 19 lines
What's in the box
RBACABACReBACPBACDeclarative .warden DSLLanguage ServerVS Code extensionNested namespacesTime-bound policiesObligationsMulti-tenantTypeID identityPlugin lifecycle hooksPostgres · SQLite · Mongo · MemoryForge integration//go:embed applyRBACABACReBACPBACDeclarative .warden DSLLanguage ServerVS Code extensionNested namespacesTime-bound policiesObligationsMulti-tenantTypeID identityPlugin lifecycle hooksPostgres · SQLite · Mongo · MemoryForge integration//go:embed apply
4Auth models

RBAC · ABAC · ReBAC · PBAC

4Store backends

Postgres · SQLite · Mongo · Memory

1Config language

Lex · parse · resolve · apply

8Namespace depth

Cascading scope inheritance

Feature surface

Composable. Declarative. Observable.

Mix authorization models freely, define your config in source-controlled .warden files, and edit them with first-class editor tooling. Every layer is built to compose with the next.

Declarative .warden DSL

New

Define your full RBAC + ABAC + ReBAC + PBAC topology as source-controlled files. One CLI: lint, apply, diff, fmt, export. Idempotent and prune-aware.

config/main.warden
warden config 1
tenant t1
 
permission "doc:read" (document : read)
permission "doc:write" (document : edit)
 
role viewer {
grants = ["doc:read"]
}
 
role editor : viewer {
grants += ["doc:write"]
}

VS Code · Neovim · Helix

LSP

Syntax highlighting, cross-file completion, hover, go-to-definition, diagnostics, formatting. One language server in the box, every editor.

Workspace-wide completion of role parents
Type-checked permission expressions
Inline diagnostics as you type
Whole-document canonical formatter
Hover docs · go-to-def across files
Install from the VS Code Marketplace — search Warden by xraph.

RBAC

Hierarchical roles with permissions, resource-scoped assignments, and glob matching. Auto-ID + auto-timestamp on Create — no boilerplate.

ABAC

Allow/deny policies with conditions on IP ranges, time windows, departments, and any context attribute. 15+ operators including ip_in_cidr and regex.

ReBAC

Google Zanzibar-inspired relation tuples with BFS graph traversal. Subject sets via group#member. Configurable max depth and cycle detection.

PBAC — time-bound + obligations

New

not_before / not_after windows for incident freezes and scheduled grants. Plus obligations — named side-effects (audit-log, require-mfa) emitted on match.

// incident freeze until 2026-06-01
policy "incident-freeze" {
effect = deny
not_after = "2026-06-01T00:00:00Z"
obligations = ["notify-oncall"]
}

Multi-tenant + nested namespaces

Hard tenant walls plus a soft namespace hierarchy inside each tenant. Cascading inheritance from ancestors, sibling isolation, default-empty global scope.

acme
├── engineering
│ ├── platform
│ │ └── sre
│ └── frontend
└── billing
Roles defined at engineering cascade to platform and frontend — but never to billing.

Plugin lifecycle hooks

Auto-discovered hooks for audit, metrics, and dispatcher integrations. PolicyObligationFired, RoleAssigned, RelationWritten, AfterCheck, more.

//go:embed your config

New

dsl.ApplyFS over an embed.FS ships your .warden tree inside the binary. One-line bootstrap on engine start. No external files in production.

Four store backends

Postgres, SQLite, MongoDB, in-memory. Same composite Store interface; pick by DSN. Migrations managed by grove, idempotent and round-trip tested.

Declarative DSL

One config language. Two ways to write it.

Define your full RBAC + ABAC + ReBAC + PBAC topology programmatically in Go, or as source-controlled .warden files. Same engine, same store, same semantics — pick whichever fits your workflow.

Programmatic (Go)
setup.go
1// Programmatic config — every entity created via the store API.
2viewer := &role.Role{Name: "Viewer", Slug: "viewer"}
3editor := &role.Role{Name: "Editor", Slug: "editor", ParentSlug: "viewer"}
4store.CreateRole(ctx, viewer)
5store.CreateRole(ctx, editor)
6 
7store.CreatePermission(ctx, &permission.Permission{
8 Name: "doc:read", Resource: "document", Action: "read",
9})
10store.CreatePermission(ctx, &permission.Permission{
11 Name: "doc:write", Resource: "document", Action: "edit",
12})
13 
14store.AttachPermission(ctx, viewer.ID, permission.Ref{Name: "doc:read"})
15store.AttachPermission(ctx, editor.ID, permission.Ref{Name: "doc:write"})
16 
17// Time-bound deny: incident freeze for the next 30 days.
18notAfter := time.Now().AddDate(0, 0, 30)
19store.CreatePolicy(ctx, &policy.Policy{
20 Name: "incident-freeze",
21 Effect: policy.EffectDeny,
22 Priority: 1,
23 IsActive: true,
24 NotAfter: &notAfter,
25 Actions: []string{"deploy:*"},
26 Obligations: []string{"notify-oncall", "audit-log"},
27})
Declarative (.warden)
config/main.warden
1// Same config as the Go on the left, in source-controlled form.
2warden config 1
3tenant t1
4 
5permission "doc:read" (document : read)
6permission "doc:write" (document : edit)
7 
8role viewer {
9 grants = ["doc:read"]
10}
11 
12role editor : viewer {
13 grants += ["doc:write"]
14}
15 
16// Time-bound deny: incident freeze for the next 30 days.
17policy "incident-freeze" {
18 effect = deny
19 priority = 1
20 active = true
21 not_after = "2026-06-01T00:00:00Z"
22 actions = ["deploy:*"]
23 obligations = ["notify-oncall", "audit-log"]
24}
Apply pipeline
terminal
# Lint, dry-run, and apply from a sibling config/ directory.
$ warden lint config/
warden lint: config/ — 2 roles, 2 permissions, 0 resource types, 1 policies — OK
 
$ warden apply --dry-run -f config/ --store sqlite:./warden.db
+ permission/doc:read
+ permission/doc:write
+ role/viewer
+ role/editor
+ policy/incident-freeze
5 created, 0 unchanged
 
$ warden apply -f config/ --store sqlite:./warden.db
✓ applied

Idempotent apply

Apply the same config twice; the second run reports zero changes. Re-runs after edits diff cleanly.

Embed via //go:embed

dsl.ApplyFS over an embed.FS ships your config inside the binary. One-line bootstrap on engine start.

Variable substitution

${TENANT}, ${REGION}, any ${VAR} you need. Bind at apply time via --var or WARDEN_VAR_*.

Round-trip export

warden export dumps live tenant state back to .warden source. apply(export(state)) is a no-op.

Editor Support

VS Code extension. Same LSP for every editor.

One purpose-built language server (warden lsp) drives the VS Code extension and works as-is in Neovim, Helix, Zed, and any other LSP client. Install in one command — no Shell Command setup required on macOS.

config/main.warden
1warden config 1
2tenant ${TENANT} // template variable
3 
4 
5namespace "engineering" {
6 permission "deploy:prod" (service : deploy)
7 
8 role eng-viewer {
9 name = "Engineering Viewer"
10 }
11 
12 namespace "platform" {
13 role platform-admin : eng-viewer { // ancestor walk
14 name = "Platform Admin"
15 grants = ["deploy:prod"]
16 }
17 }
18}
19 
20policy "incident-freeze" {
21 effect = deny
22 priority = 1
23 active = true
24 not_after = "2026-06-01T00:00:00Z"
25 actions = ["deploy:*"]
26 obligations = ["notify-oncall", "audit-log"]
27}

Syntax highlighting

TextMate grammar covers every keyword, expression operator, and condition predicate. Tracks dsl/parser.go in lockstep.

Cross-file completion

Workspace document index — completing a role parent suggests slugs declared anywhere in your config tree, not just the current file.

Inline diagnostics

Parser + resolver errors as you type, with file:line:col precision. Undefined variables, unknown parents, traversal type mismatches.

Hover + go-to-definition

Markdown summaries for roles, permissions, resource types, policies. Click a parent slug to jump to its declaration.

Whole-document formatter

Canonical .warden output. Stable under reapply: fmt(parse(fmt(parse(src)))) ≡ fmt(parse(src)).

Same LSP everywhere

warden lsp powers the VS Code extension and Neovim, Helix, Zed via stdio. One server, every editor.

Install from the Marketplace
terminal
# Install from the VS Code Marketplace
$ code --install-extension xraph.vscode-warden
Installing extensions...
Extension 'xraph.vscode-warden' v0.1.0 was successfully installed.
 
# Or search "Warden" in the Extensions panel (Cmd+Shift+X).

Published as xraph.vscode-warden on the VS Code Marketplace and Open VSX. Building from source is also supported — see the extension's README for the dev recipe.

Neovim

lspconfig.warden = { cmd = {'warden', 'lsp'} }

Helix

[[language]]
name = "warden"
language-server = { command = "warden", args = ["lsp"] }

Zed

warden lsp via the language-server config
Authorization Pipeline

From request to decision.

Warden orchestrates the entire authorization lifecycle — RBAC evaluation, ABAC policy matching, ReBAC graph traversal, and decision merging.

Three Models, One Check

RBAC, ABAC, and ReBAC are evaluated in parallel and their decisions merged with deny-overrides priority. A single Check() call handles everything.

BFS Graph Traversal

ReBAC uses breadth-first search with cycle detection and configurable max depth to traverse relation graphs and find transitive permissions.

Audit Trail & Metrics

Every check is logged with subject, action, resource, decision, and duration. Wire in Chronicle for audit events or Prometheus for metrics via the plugin system.

Request
authz.check
Evaluate3 models
Mergedecision
rbac.matched
Allowed
abac.evaluated
Evaluating
rebac.traversed
Path Found
Allowed
Evaluating
No Opinion
Denied
Developer Experience

Simple API. Powerful primitives.

Set up role-based access or traverse a relationship graph in under 30 lines. Warden handles evaluation, merging, and audit logging.

RBAC
rbac.go
1package main
2 
3import (
4 "context"
5 "fmt"
6 
7 "github.com/xraph/warden"
8 "github.com/xraph/warden/assignment"
9 "github.com/xraph/warden/permission"
10 "github.com/xraph/warden/role"
11 "github.com/xraph/warden/store/memory"
12)
13 
14func main() {
15 ctx := context.Background()
16 st := memory.New()
17 eng, _ := warden.NewEngine(warden.WithStore(st))
18 
19 // IDs and timestamps are auto-assigned by the store.
20 perm := &permission.Permission{
21 Name: "doc:read", Resource: "document", Action: "read",
22 }
23 r := &role.Role{Name: "Viewer", Slug: "viewer"}
24 _ = st.CreatePermission(ctx, perm)
25 _ = st.CreateRole(ctx, r)
26 _ = st.AttachPermission(ctx, r.ID, permission.Ref{Name: "doc:read"})
27 
28 _ = st.CreateAssignment(ctx, &assignment.Assignment{
29 RoleID: r.ID,
30 SubjectKind: "user", SubjectID: "alice",
31 })
32 
33 result, _ := eng.Check(ctx, &warden.CheckRequest{
34 Subject: warden.Subject{Kind: warden.SubjectUser, ID: "alice"},
35 Action: warden.Action{Name: "read"},
36 Resource: warden.Resource{Type: "document", ID: "doc-1"},
37 })
38 fmt.Printf("allowed=%v reason=%q\n",
39 result.Allowed, result.Reason)
40 // allowed=true reason="role.viewer grants doc:read"
41}
ReBAC
rebac.go
1package main
2 
3import (
4 "context"
5 "fmt"
6 
7 "github.com/xraph/warden"
8 "github.com/xraph/warden/relation"
9 "github.com/xraph/warden/store/memory"
10)
11 
12func main() {
13 ctx := context.Background()
14 st := memory.New()
15 eng, _ := warden.NewEngine(warden.WithStore(st))
16 
17 // user:alice is member of team:eng
18 _ = st.CreateRelation(ctx, &relation.Tuple{
19 ObjectType: "team", ObjectID: "eng",
20 Relation: "member",
21 SubjectType: "user", SubjectID: "alice",
22 })
23 
24 // team:eng is editor of project:alpha (via subject set #member)
25 _ = st.CreateRelation(ctx, &relation.Tuple{
26 ObjectType: "project", ObjectID: "alpha",
27 Relation: "editor",
28 SubjectType: "team", SubjectID: "eng",
29 SubjectRelation: "member",
30 })
31 
32 // Can alice edit project:alpha?
33 // BFS: alice → team:eng#member → project:alpha
34 result, _ := eng.Check(ctx, &warden.CheckRequest{
35 Subject: warden.Subject{Kind: warden.SubjectUser, ID: "alice"},
36 Action: warden.Action{Name: "write"},
37 Resource: warden.Resource{Type: "project", ID: "alpha"},
38 })
39 fmt.Printf("allowed=%v\n", result.Allowed)
40 // allowed=true (via transitive relation)
41}
Ready when you are

Ship authorization, without the boilerplate.

One Go module. One CLI. One language server. Define your full authorization topology in source-controlled .warden files, ship it inside your binary, and Check at runtime — across every model.

$go install github.com/xraph/warden/cmd/warden@latest