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@latest1warden config 12tenant acme3 4permission "doc:read" (document : read)5permission "doc:write" (document : edit)6 7resource document {8 relation owner: user9 relation viewer: user10 permission read = viewer or owner11}12 13role viewer {14 grants = ["doc:read"]15}16 17role editor : viewer {18 grants += ["doc:write"]19}RBAC · ABAC · ReBAC · PBAC
Postgres · SQLite · Mongo · Memory
Lex · parse · resolve · apply
Cascading scope inheritance
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
Define your full RBAC + ABAC + ReBAC + PBAC topology as source-controlled files. One CLI: lint, apply, diff, fmt, export. Idempotent and prune-aware.
warden config 1tenant 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
Syntax highlighting, cross-file completion, hover, go-to-definition, diagnostics, formatting. One language server in the box, every editor.
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
not_before / not_after windows for incident freezes and scheduled grants. Plus obligations — named side-effects (audit-log, require-mfa) emitted on match.
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.
Plugin lifecycle hooks
Auto-discovered hooks for audit, metrics, and dispatcher integrations. PolicyObligationFired, RoleAssigned, RelationWritten, AfterCheck, more.
//go:embed your config
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.
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.
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: ¬After,25 Actions: []string{"deploy:*"},26 Obligations: []string{"notify-oncall", "audit-log"},27})1// Same config as the Go on the left, in source-controlled form.2warden config 13tenant t14 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 = deny19 priority = 120 active = true21 not_after = "2026-06-01T00:00:00Z"22 actions = ["deploy:*"]23 obligations = ["notify-oncall", "audit-log"]24}# 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-freeze5 created, 0 unchanged $ warden apply -f config/ --store sqlite:./warden.db✓ appliedIdempotent 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.
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.
1warden config 12tenant ${TENANT} // template variable3 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 walk14 name = "Platform Admin"15 grants = ["deploy:prod"]16 }17 }18}19 20policy "incident-freeze" {21 effect = deny22 priority = 123 active = true24 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 VS Code Marketplace$ code --install-extension xraph.vscode-wardenInstalling 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
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.
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.
1package main2 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}1package main2 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:eng18 _ = 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:alpha34 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}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