Getting Started
Install Warden and run your first authorization check.
Installation
Library
go get github.com/xraph/wardenCLI (warden and warden-lsp)
The warden CLI applies .warden configs to a store; warden-lsp powers the editor extensions. Three options:
go install github.com/xraph/warden/cmd/warden@latest
go install github.com/xraph/warden/cmd/warden-lsp@latest# Pick a platform from https://github.com/xraph/warden/releases/latest
curl -L -o warden.tar.gz \
https://github.com/xraph/warden/releases/latest/download/warden-<version>-<os>-<arch>.tar.gz
# Verify the checksum
curl -L -o checksums.txt \
https://github.com/xraph/warden/releases/latest/download/checksums.txt
shasum -a 256 -c checksums.txt --ignore-missing
tar -xzf warden.tar.gz
sudo mv warden warden-lsp /usr/local/bin/
warden versiongit clone https://github.com/xraph/warden.git
cd warden
make build
./bin/warden versionEach pre-built archive contains both warden and warden-lsp. Available for linux, darwin, windows × amd64, arm64.
VS Code extension
code --install-extension xraph.vscode-wardenThe extension provides syntax highlighting, completion, hover, go-to-definition, and live diagnostics for .warden files. See the DSL tooling guide for editor configuration.
Step 1: Create a Store
Warden needs a store to persist roles, permissions, policies, relations, and assignments. Start with the in-memory store for development:
import "github.com/xraph/warden/store/memory"
st := memory.New()Step 2: Create the Engine
import "github.com/xraph/warden"
eng, err := warden.NewEngine(
warden.WithStore(st),
)
if err != nil {
log.Fatal(err)
}Step 3: Create a Role and Permission
import (
"github.com/xraph/warden/id"
"github.com/xraph/warden/role"
"github.com/xraph/warden/permission"
)
// IDs are auto-assigned by the store on Create. The in-place mutation
// means r.ID and perm.ID are populated when the calls return — handy
// for downstream uses like AttachPermission below.
// Create a permission
perm := &permission.Permission{
Name: "doc:read",
Resource: "document",
Action: "read",
}
_ = st.CreatePermission(ctx, perm)
// Create a role
r := &role.Role{
Name: "Viewer",
Slug: "viewer",
}
_ = st.CreateRole(ctx, r)
// Attach permission to role
_ = st.AttachPermission(ctx, r.ID, permission.Ref{Name: "doc:read"})Step 4: Assign the Role
import "github.com/xraph/warden/assignment"
ass := &assignment.Assignment{
RoleID: r.ID,
SubjectKind: "user",
SubjectID: "user-42",
}
_ = st.CreateAssignment(ctx, ass)Step 5: Check Authorization
result, err := eng.Check(ctx, &warden.CheckRequest{
Subject: warden.Subject{Kind: "user", ID: "user-42"},
Action: "read",
ResourceType: "document",
ResourceID: "doc-123",
})
if result.Allowed {
fmt.Println("Access granted!")
} else {
fmt.Println("Access denied:", result.Reason)
}Step 6: Add ABAC Policies (Optional)
import "github.com/xraph/warden/policy"
p := &policy.Policy{
Name: "Office Hours Only",
Effect: policy.EffectDeny,
Subjects: []string{"*"},
Actions: []string{"*"},
Resources: []string{"document:*"},
Conditions: []policy.Condition{{
Field: "time",
Operator: policy.OpTimeBefore,
Value: "09:00",
}},
IsActive: true,
}
_ = st.CreatePolicy(ctx, p)Step 7: Add ReBAC Relations (Optional)
import "github.com/xraph/warden/relation"
tuple := &relation.Tuple{
ObjectType: "document",
ObjectID: "doc-123",
Relation: "viewer",
SubjectType: "user",
SubjectID: "user-42",
}
_ = st.CreateRelation(ctx, tuple)Step 8: Use with PostgreSQL
For production, switch to the PostgreSQL store:
import (
"github.com/xraph/grove"
"github.com/xraph/grove/drivers/pgdriver"
"github.com/xraph/warden/store/postgres"
)
db, err := grove.Open(pgdriver.New(
pgdriver.WithDSN("postgres://user:pass@localhost/warden"),
))
if err != nil {
log.Fatal(err)
}
pgStore := postgres.New(db)
defer pgStore.Close()
// Run migrations
_ = pgStore.Migrate(ctx)
eng, _ := warden.NewEngine(
warden.WithStore(pgStore),
)Step 8: Use the Declarative DSL (Optional)
Programmatic seeding works for examples, but production setups define roles, permissions, policies, and relations as source-controlled .warden files.
go install github.com/xraph/warden/cmd/warden@latest# config/main.warden
warden config 1
tenant t1 # optional — omit for single-tenant apps
permission "doc:read" (document : read)
role viewer {
name = "Viewer"
grants = ["doc:read"]
}
role editor : viewer {
name = "Editor"
grants += ["doc:write"]
}warden lint config/ # static checks, no DB
warden apply -f config/ --store sqlite:./warden.dbThe tenant declaration is optional. Single-tenant apps that never call warden.WithTenant should omit it — every entity lands in the global scope (tenant_id = ""), which is what the engine looks up at Check time too.
The same CLI ships a language server (warden lsp) used by the VS Code extension and Neovim/Helix configs for diagnostics, completion, hover, and go-to-definition. See DSL & Tooling.
Or embed the config
If you'd rather ship the config inside your binary instead of distributing files alongside it, use //go:embed and dsl.ApplyFS:
//go:embed all:config
var configFS embed.FS
func main() {
eng, _ := warden.NewEngine(warden.WithStore(memory.New()))
_, err := dsl.ApplyFS(ctx, eng, configFS, "config",
dsl.ApplyOptions{TenantID: "acme"},
dsl.WithVariables(dsl.Variables{"REGION": os.Getenv("AWS_REGION")}),
)
// handle err …
}Same load-resolve-apply pipeline as the CLI, exposed as a library. Full reference and a runnable example: DSL & Tooling — Programmatic apply.
Next Steps
- Architecture — Understand the engine internals
- Authorization Models — Deep dive into RBAC, ABAC, ReBAC, and PBAC
- Namespaces — Nested tenancy with cascading scope inheritance
- .warden Language Reference — Every block, field, expression, operator, and convention
- DSL & Tooling — CLI, LSP, VS Code extension, and template variables
- Policies & Conditions — Time-bound policies and obligations (PBAC)
- Forge Extension — Use Warden as a Forge extension
- REST API — Manage entities via HTTP