Documentation
¶
Overview ¶
Package dsl implements the .warden declarative configuration language.
The language is purpose-built for warden's domain. Inspired by SpiceDB for resource types and permission expressions, with HCL-like blocks for roles and policies. One parser, one AST, one set of references — entity definitions, permission expressions, and ABAC conditions all share the same grammar.
See the project design doc and the canonical plan for the full grammar specification.
Index ¶
- func CompileExpr(file, src string) (Expr, []*Diagnostic)
- func Export(ctx context.Context, eng *warden.Engine, opts ExportOptions) (int, error)
- func Format(prog *Program) string
- func FormatBytes(prog *Program) []byte
- func FormatExpr(e Expr) string
- func Load(arg string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func LoadDir(dir string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func LoadFS(fsys fs.FS, root string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func LoadFile(path string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func LoadFiles(paths []string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func LoadGlob(pattern string, opts ...LoadOption) (*Program, []*Diagnostic, error)
- func Parse(file string, src []byte) (*Program, []*Diagnostic)
- type AndExpr
- type ApplyOptions
- type ApplyResult
- func Apply(ctx context.Context, eng *warden.Engine, prog *Program, opts ApplyOptions) (*ApplyResult, error)
- func ApplyDir(ctx context.Context, eng *warden.Engine, dir string, opts ApplyOptions, ...) (*ApplyResult, error)
- func ApplyFS(ctx context.Context, eng *warden.Engine, fsys fs.FS, root string, ...) (*ApplyResult, error)
- func ApplyFile(ctx context.Context, eng *warden.Engine, path string, opts ApplyOptions, ...) (*ApplyResult, error)
- type Condition
- type Diagnostic
- type DiagnosticError
- type EngineEvaluator
- type EvalContext
- type Evaluator
- func (e *Evaluator) CompileAndCache(tenantID, ns, resourceType, permName, exprSrc string) (Expr, []*Diagnostic)
- func (e *Evaluator) Eval(ctx context.Context, expr Expr, ec EvalContext) (bool, error)
- func (e *Evaluator) Invalidate(tenantID, resourceType string)
- func (e *Evaluator) WithPermResolver(r PermResolver) *Evaluator
- type ExportOptions
- type Expr
- type ImportDecl
- type Layout
- type Lexer
- type LoadOption
- type NamespaceDecl
- type NotExpr
- type OrExpr
- type PermResolver
- type PermissionDecl
- type PolicyDecl
- type Pos
- type Program
- type RefExpr
- type RelationDecl
- type RelationDef
- type ResourceDecl
- type ResourcePermissionDecl
- type RoleDecl
- type SubjectType
- type Token
- type TokenKind
- type TraverseExpr
- type Variables
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CompileExpr ¶
func CompileExpr(file, src string) (Expr, []*Diagnostic)
CompileExpr parses a textual permission expression into an AST. Used by the engine to compile ResourceType.Permissions[].Expression at Check time (with caching).
func Export ¶
Export reads tenant state from the engine's store and writes .warden source to opts.OutputDir according to the chosen Layout. It returns the number of files written.
The output is canonical (passes through Format), so apply(export(state)) produces zero diff against the original state.
func Format ¶
Format renders a Program back to canonical .warden source. The output is stable: parsing then formatting again yields the same bytes.
Canonical rules (see plan section D.1.a):
- 4-space indent; LF line endings; final newline.
- One blank line between top-level decls; two between section dividers.
- Decls within a section sorted by name/slug for stability.
- Within blocks, fields ordered (name, description, flags…, grants/when last).
- Permission expressions rendered with parens elided where precedence permits.
- Multi-line string lists when len > 3, inline otherwise.
func FormatBytes ¶
FormatBytes is a byte-slice convenience wrapper around Format.
func FormatExpr ¶
FormatExpr renders an expression AST back to its canonical textual form. Used by the applier to store ResourceType.Permissions[].Expression.
func Load ¶
func Load(arg string, opts ...LoadOption) (*Program, []*Diagnostic, error)
Load auto-detects whether arg is a file, directory, or glob pattern.
func LoadDir ¶
func LoadDir(dir string, opts ...LoadOption) (*Program, []*Diagnostic, error)
LoadDir walks a directory tree and loads every .warden file under it. Hidden directories (those starting with `.`) are skipped; underscore- prefixed directories like `_shared/` are kept by convention.
func LoadFS ¶
func LoadFS(fsys fs.FS, root string, opts ...LoadOption) (*Program, []*Diagnostic, error)
LoadFS reads .warden files from an fs.FS rooted at root. Useful for embedded configs (//go:embed).
func LoadFile ¶
func LoadFile(path string, opts ...LoadOption) (*Program, []*Diagnostic, error)
LoadFile reads and parses a single .warden file.
func LoadFiles ¶
func LoadFiles(paths []string, opts ...LoadOption) (*Program, []*Diagnostic, error)
LoadFiles reads, parses, and merges a fixed list of .warden files.
Cross-file conflicts (same role/permission/policy/resource type declared in multiple files) produce diagnostics on the merged Program. The order of files determines tie-breaking only for the scope (tenant/app) fields — the first file with a non-empty value wins, and any later file declaring a conflicting non-empty value yields a diagnostic.
func LoadGlob ¶
func LoadGlob(pattern string, opts ...LoadOption) (*Program, []*Diagnostic, error)
LoadGlob loads every .warden file matching a glob pattern. Pattern semantics match filepath.Glob plus `**` for recursive matching (we don't expand `**` here — use LoadDir for that, or expand the pattern yourself with doublestar).
func Parse ¶
func Parse(file string, src []byte) (*Program, []*Diagnostic)
Parse parses `.warden` source into a Program. Errors are accumulated (one per problem) and returned as a slice; a non-nil Program is returned even when errors occur, partially populated, so editor tooling can still surface useful information.
Types ¶
type ApplyOptions ¶
type ApplyOptions struct {
// TenantID overrides Program.Tenant when non-empty.
TenantID string
// AppID overrides Program.App when non-empty.
AppID string
// DryRun, when true, plans the changes and returns the diff but writes
// nothing to the store.
DryRun bool
// Prune, when true, deletes tenant entries (within the namespaces
// covered by the program) that are not declared in the program.
// kubectl-style apply with prune.
Prune bool
// Now is the time used for CreatedAt/UpdatedAt timestamps. Defaults to
// time.Now().UTC().
Now time.Time
}
ApplyOptions configures the DSL applier.
type ApplyResult ¶
type ApplyResult struct {
Created []string // human-readable summary lines: "+ kind/name"
Updated []string // "~ kind/name (field: old → new)"
Deleted []string // "- kind/name"
NoOps int // count of unchanged entries
}
ApplyResult summarizes the outcome of an apply.
func Apply ¶
func Apply(ctx context.Context, eng *warden.Engine, prog *Program, opts ApplyOptions) (*ApplyResult, error)
Apply materializes the program against the engine's store. It is idempotent — applying the same program twice produces the same state.
Tenant scope is optional. When neither opts.TenantID nor `tenant` in source is set, every entity is written with an empty `tenant_id` — the **global scope**. Single-tenant apps that never call `warden.WithTenant` see this as the natural default; multi-tenant apps should always pass a tenant explicitly to avoid accidentally landing entities in the global bucket.
func ApplyDir ¶
func ApplyDir(ctx context.Context, eng *warden.Engine, dir string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)
ApplyDir loads, validates, and applies every .warden file under dir. Hidden directories are skipped; underscore-prefixed directories (`_shared/`, etc.) are kept by convention.
Cross-file conflicts (duplicate roles, mismatched tenant) are reported as parse-time diagnostics and short-circuit Apply.
func ApplyFS ¶
func ApplyFS(ctx context.Context, eng *warden.Engine, fsys fs.FS, root string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)
ApplyFS loads, validates, and applies every .warden file under root in fsys. Designed for `//go:embed`: pass an embed.FS as fsys and the helper will walk it like a real filesystem.
//go:embed all:config
var configFS embed.FS
res, err := dsl.ApplyFS(ctx, eng, configFS, "config",
dsl.ApplyOptions{TenantID: "acme"},
dsl.WithVariables(dsl.Variables{"REGION": region}))
Note: include the `all:` prefix in the //go:embed directive when your config tree contains directories beginning with `_` (like `_shared`) — the default //go:embed pattern strips those, but Warden's underscore-prefixed convention expects them.
func ApplyFile ¶
func ApplyFile(ctx context.Context, eng *warden.Engine, path string, opts ApplyOptions, loadOpts ...LoadOption) (*ApplyResult, error)
ApplyFile loads, validates, and applies a single .warden file against the engine. It is a convenience wrapper for the common Load → check → Apply sequence:
prog, parseErrs, err := dsl.LoadFile(path, loadOpts...) // check err, parseErrs, then call dsl.Apply(ctx, eng, prog, opts)
Parse-time diagnostics short-circuit Apply and are returned as *DiagnosticError. Resolver diagnostics surface the same way (Apply emits them itself). Use errors.As to inspect the diagnostics list.
Pass dsl.WithVariables(...) via loadOpts to expand ${NAME} placeholders before parsing.
type Condition ¶
type Condition struct {
// Atomic predicate.
Field string
Operator string
Value any
Negate bool
// Boolean groups.
AllOf []*Condition
AnyOf []*Condition
Pos Pos
}
Condition is a single ABAC predicate or boolean group.
Exactly one of {Field+Operator+Value, AllOf, AnyOf} is populated.
type Diagnostic ¶
Diagnostic is a parser/checker error with a source position and message.
func Resolve ¶
func Resolve(prog *Program) []*Diagnostic
Resolve performs name resolution and type checking against a parsed program (or merged program from a multi-file load set). Returns any diagnostics found; the program may still be applied even with warnings.
Checks performed:
- duplicate slugs / names within and across files
- role parent slug resolves to a real role (local or ancestor namespace)
- cycle detection in the role parent graph
- resource-type permission expressions reference declared relations
- traversal expressions hop through declared relation targets
- condition operators are valid
- identifier conventions (slug regex, name regex, namespace path)
func SubstituteVariables ¶
func SubstituteVariables(file string, src []byte, vars Variables) ([]byte, []*Diagnostic)
SubstituteVariables expands `${NAME}` placeholders in src using vars. Each well-formed `${NAME}` reference is replaced with `vars[NAME]`.
Escape: a literal `$$` in the source emits a single `$` and skips substitution at that byte. So `$${VAR}` produces `${VAR}` in the output (useful for documentation strings that mention the syntax).
Diagnostics are produced for:
- unclosed `${...` (no matching `}` before EOL or EOF)
- invalid identifiers inside `${...}`
- undefined variables (name not in vars)
Diagnostics carry the placeholder's source position so the LSP / `warden lint` can underline them precisely. The placeholder is left in the output untouched on diagnostic so downstream parsing still has bytes to work with — the parser's own errors then point at the resulting (still-broken) source.
Substitution is purely textual: placeholders inside string literals and inside line/block comments are substituted just like everywhere else. This keeps the function simple and the rules predictable — users who genuinely want a literal `${...}` write `$$` to escape.
func (*Diagnostic) Error ¶
func (d *Diagnostic) Error() string
Error returns the textual diagnostic string for use as an error.
func (*Diagnostic) String ¶
func (d *Diagnostic) String() string
type DiagnosticError ¶
type DiagnosticError struct {
Diags []*Diagnostic
}
DiagnosticError is the error type Apply / ApplyFile / ApplyDir / ApplyFS return when source-level errors prevent application. Use errors.As to extract the underlying diagnostics:
var derr *dsl.DiagnosticError
if errors.As(err, &derr) {
for _, d := range derr.Diagnostics() {
fmt.Println(d) // file:line:col: message
}
}
The Error() representation joins every diagnostic with a newline so printing the error directly produces a multi-line list suitable for CI logs.
func (*DiagnosticError) Diagnostics ¶
func (e *DiagnosticError) Diagnostics() []*Diagnostic
Diagnostics returns the underlying diagnostics for inspection.
func (*DiagnosticError) Error ¶
func (e *DiagnosticError) Error() string
type EngineEvaluator ¶
type EngineEvaluator struct {
// contains filtered or unexported fields
}
EngineEvaluator implements warden.ExpressionEvaluator by looking up the resource type's permission expression, compiling+caching it, and evaluating against the relation graph.
Wire it via:
ev := dsl.NewEngineEvaluator(store) eng := warden.NewEngine(warden.WithStore(store), warden.WithExpressionEvaluator(ev))
func NewEngineEvaluator ¶
func NewEngineEvaluator(s engineStore) *EngineEvaluator
NewEngineEvaluator constructs an evaluator backed by the engine's store. The store must implement both relation.Store and resourcetype.Store — every backend in this repo (memory, sqlite, postgres, mongo) satisfies that.
func (*EngineEvaluator) EvalPermission ¶
func (e *EngineEvaluator) EvalPermission( ctx context.Context, tenantID, namespacePath, resourceType, permName, subjectKind, subjectID, resourceID string, ) (bool, error)
EvalPermission implements warden.ExpressionEvaluator.
Walks the namespace ancestor chain looking for a resource type with this name, then within that resource type's permissions for one named after the request's action. If found and the expression is non-empty, it's compiled (cached) and evaluated.
type EvalContext ¶
type EvalContext struct {
TenantID string
NamespacePath string
// NamespacePaths is the set of namespaces a relation lookup may match —
// typically the request namespace and its ancestors, so relations cascade
// like roles and policies. When empty, lookups fall back to the single
// NamespacePath (see nsList).
NamespacePaths []string
ObjectType string
ObjectID string
SubjectType string
SubjectID string
// MaxDepth bounds traversal recursion. The engine sets this from its
// configured graph walker depth.
MaxDepth int
}
EvalContext is the runtime input to expression evaluation.
The evaluator walks an Expr AST against the relation graph to decide whether `subject` has the named permission on `resource`. It uses the store to look up tuples and recurses across traversal hops up to a bounded depth (taken from MaxDepth).
type Evaluator ¶
type Evaluator struct {
// contains filtered or unexported fields
}
Evaluator evaluates resource-type permission expressions.
func NewEvaluator ¶
NewEvaluator constructs an evaluator backed by the given relation store.
func (*Evaluator) CompileAndCache ¶
func (e *Evaluator) CompileAndCache(tenantID, ns, resourceType, permName, exprSrc string) (Expr, []*Diagnostic)
CompileAndCache parses the expression for a resource-type permission and caches the result. Subsequent Eval calls reuse the AST.
func (*Evaluator) Eval ¶
Eval walks the expression AST and returns true iff the subject has the permission on the object given the relation tuples in store.
func (*Evaluator) Invalidate ¶
Invalidate clears all cached expressions for a tenant/resource type. Engine calls this when ResourceType records are updated.
func (*Evaluator) WithPermResolver ¶
func (e *Evaluator) WithPermResolver(r PermResolver) *Evaluator
WithPermResolver sets the permission resolver used during traversal to recursively evaluate permissions on hopped-to resource types.
type ExportOptions ¶
type ExportOptions struct {
// TenantID is required — the tenant whose state is exported.
TenantID string
// AppID is optional and propagates to the emitted header.
AppID string
// NamespacePrefix limits the export to entities whose namespace_path
// equals or descends from this value. Empty means "all namespaces".
NamespacePrefix string
// Layout controls file layout (see Layout consts).
Layout Layout
// OutputDir is the destination root. Created if missing.
OutputDir string
}
ExportOptions configures Export.
type Expr ¶
type Expr interface {
Position() Pos
// contains filtered or unexported methods
}
Expr is the interface for permission-expression AST nodes.
type ImportDecl ¶
ImportDecl is a literal `import "<path>"` directive (reserved for the future explicit-import flow; the loader does not consume them today).
type Layout ¶
type Layout int
Layout controls how `warden export` distributes a tenant's state across .warden files.
const ( // FlatLayout writes everything to a single main.warden file. FlatLayout Layout = iota // SectionalLayout splits by entity kind: 00-resource-types.warden, // 10-permissions.warden, 20-roles.warden, 30-policies.warden, // 40-relations.warden. SectionalLayout // DomainLayout groups by namespace path. Each top-level segment becomes // a directory; entities at the tenant root go to a `_root/` directory. DomainLayout )
type Lexer ¶
type Lexer struct {
// contains filtered or unexported fields
}
Lexer is a hand-written tokenizer for `.warden` source.
Encoding is UTF-8; positions are tracked in 1-based line and byte column. The lexer never errors fatally — it returns ILLEGAL tokens for malformed input and lets the parser report richer diagnostics.
type LoadOption ¶
type LoadOption func(*loadConfig)
LoadOption configures a Load* call. Use the With* helpers below.
func WithVariables ¶
func WithVariables(v Variables) LoadOption
WithVariables expands `${NAME}` placeholders in every loaded source file using the supplied map. Names match `[A-Za-z_][A-Za-z0-9_]*`. Substitution is purely textual; see SubstituteVariables for the full rules. Pass nil or an empty map to disable substitution (the default).
type NamespaceDecl ¶
type NamespaceDecl struct {
// Name is the path segment, not the absolute path.
Name string
// Children are the decls declared directly inside this block. After
// flattening, the absolute namespace path of each is recorded on the
// decl itself; this field is kept for reference / formatter use.
ResourceTypes []*ResourceDecl
Permissions []*PermissionDecl
Roles []*RoleDecl
Policies []*PolicyDecl
Relations []*RelationDecl
Namespaces []*NamespaceDecl
Pos Pos
}
NamespaceDecl wraps a nested `namespace "name" { ... }` block. The parser flattens nested namespaces into per-decl NamespacePath strings, but it also keeps the original NamespaceDecl tree around for fmt-friendly re-emission.
type PermResolver ¶
type PermResolver func(ctx context.Context, tenantID, namespacePath, resourceType, permName string) (Expr, bool)
PermResolver returns the compiled permission expression for a resource type's named permission, if one exists. Used by the traversal walker to recursively evaluate permissions across resource-type hops (e.g. `parent->read` where `read` is itself a permission expression on the hopped resource type).
Returns (nil, false) when no expression is defined for that combination.
type PermissionDecl ¶
type PermissionDecl struct {
Name string // the literal "resource:action" string
NamespacePath string
Resource string // parsed from Name or set explicitly
Action string // parsed from Name or set explicitly
Description string
IsSystem bool
Pos Pos
}
PermissionDecl is a top-level `permission "<name>" (...)` or `{...}` block.
type PolicyDecl ¶
type PolicyDecl struct {
Name string
NamespacePath string
Description string
Effect string // "allow" | "deny"
Priority int
Active bool
NotBefore *time.Time
NotAfter *time.Time
Obligations []string
Actions []string
Resources []string
Conditions []*Condition
Pos Pos
}
PolicyDecl is a top-level `policy "<name>" { ... }` block.
PBAC fields:
- NotBefore / NotAfter: optional RFC3339 instants bounding when the policy is effective. Either may be nil ("no bound on that side").
- Obligations: list of named side-effect actions emitted when the policy matches (e.g. "audit-log", "require-mfa").
type Program ¶
type Program struct {
// File this program was parsed from. For multi-file load sets, this is
// the root path (the load entrypoint).
File string
// Header values.
Version int
Tenant string // optional; resolved at apply time
App string // optional
// Top-level declarations.
ResourceTypes []*ResourceDecl
Permissions []*PermissionDecl
Roles []*RoleDecl
Policies []*PolicyDecl
Relations []*RelationDecl
Imports []*ImportDecl
Namespaces []*NamespaceDecl
// HeaderPos is the position of the `warden config N` line.
HeaderPos Pos
}
Program is the root AST node — one parsed `.warden` file or set of files.
All decls within a program share the same logical namespace; cross-file references are resolved against the merged Program.
func BuildProgram ¶
BuildProgram reads tenant state and constructs an in-memory Program. Useful for tests that want to round-trip without writing to disk, and for custom emitters.
type RefExpr ¶
RefExpr references a relation by name. Used as the leaf of expressions inside a resource type ("viewer", "editor").
type RelationDecl ¶
type RelationDecl struct {
NamespacePath string
ObjectType string
ObjectID string
Relation string
SubjectType string
SubjectID string
SubjectRelation string
Pos Pos
}
RelationDecl is a top-level `relation <obj_type>:<obj_id> <rel> = <subj_type>:<subj_id>[#<subj_rel>]`.
type RelationDef ¶
type RelationDef struct {
Name string
AllowedSubjects []SubjectType
Pos Pos
}
RelationDef is a `relation <name>: <subj> | <subj>#<rel> | ...` line inside a resource block.
type ResourceDecl ¶
type ResourceDecl struct {
Name string
NamespacePath string // absolute path, empty = tenant root
Description string
Relations []*RelationDef
Permissions []*ResourcePermissionDecl
Pos Pos
}
ResourceDecl is a `resource <name> { ... }` block.
type ResourcePermissionDecl ¶
ResourcePermissionDecl is a `permission <name> = <expr>` inside a resource block.
type RoleDecl ¶
type RoleDecl struct {
Slug string
NamespacePath string
Parent string // raw — may be a local slug or absolute path starting with "/"
Name string
Description string
IsSystem bool
IsDefault bool
MaxMembers int
Grants []string // permission names; possibly globs
GrantsAppend bool // true if `grants += [...]`
Pos Pos
}
RoleDecl is a `role <slug> [: <parent>] { ... }` block.
type SubjectType ¶
SubjectType is one entry in a relation's allowed_subjects list. Relation, when non-empty, restricts to a relation-set (`group#member`).
type Token ¶
type Token struct {
Kind TokenKind
Value string // raw lexeme; for STRING this is the unescaped string literal
Pos Pos
}
Token represents a single lexed token.
type TokenKind ¶
type TokenKind int
TokenKind enumerates lexical token categories.
const ( // EOF marks end-of-input. EOF TokenKind = iota // ILLEGAL is an unrecognized character or unterminated literal. ILLEGAL // IDENT is a lowercase identifier — `[a-z_][a-z0-9_-]*`. IDENT // STRING is a double-quoted string literal with the value already // unescaped (\\, \", \n, \t). STRING // INT is an unsigned integer literal — `[0-9]+`. INT // BOOL is `true` or `false`. BOOL // Single-character punctuation. LBRACE // { RBRACE // } LPAREN // ( RPAREN // ) LBRACKET // [ RBRACKET // ] COMMA // , COLON // : SEMI // ; DOT // . PIPE // | HASH // # BANG // ! SLASH // / (used for absolute namespace paths in role parent refs) // Operators. ASSIGN // = APPEND // += ARROW // -> EQ // == NE // != LT // < GT // > LE // <= GE // >= PLUS // + (also used as union operator on expressions) MINUS // - (also unary negation) AMP // & (also intersection on expressions) REGEX // =~ // Keywords. Lexer picks these out of the IDENT stream. WARDEN // warden CONFIG // config TENANT // tenant APP // app NAMESPACE // namespace IMPORT // import RESOURCE // resource RELATION // relation PERMISSION // permission ROLE // role POLICY // policy EFFECT // effect ALLOW // allow DENY // deny ACTIONS // actions RESOURCES // resources SUBJECTS // subjects WHEN // when NEGATE // negate GRANTS // grants NAME // name DESCRIPTION // description PRIORITY // priority ACTIVE // active IS_SYSTEM //nolint:revive // matches DSL keyword spelling IS_DEFAULT //nolint:revive // matches DSL keyword spelling MAX_MEMBERS //nolint:revive // matches DSL keyword spelling METADATA // metadata OR // or AND // and NOT // not IN // in CONTAINS // contains STARTS_WITH //nolint:revive // matches DSL keyword spelling ENDS_WITH //nolint:revive // matches DSL keyword spelling EXISTS // exists IP_IN_CIDR //nolint:revive // matches DSL keyword spelling TIME_AFTER //nolint:revive // matches DSL keyword spelling TIME_BEFORE //nolint:revive // matches DSL keyword spelling ALL_OF //nolint:revive // matches DSL keyword spelling ANY_OF //nolint:revive // matches DSL keyword spelling NOT_BEFORE //nolint:revive // matches DSL keyword spelling NOT_AFTER //nolint:revive // matches DSL keyword spelling OBLIGATIONS // obligations (PBAC: named side-effect actions) )
type TraverseExpr ¶
type TraverseExpr struct {
// Steps holds at least 2 names: the first is a relation on the current
// resource type, subsequent names are relations or permissions on the
// target type after each hop.
Steps []string
Pos Pos
}
TraverseExpr is `<rel>->...->target` — walk one or more relation hops and then check the final permission/relation on the resulting object.
func (*TraverseExpr) Position ¶
func (e *TraverseExpr) Position() Pos
type Variables ¶
Variables is a name → value map of template variables expanded during loading. Names match the regex `[A-Za-z_][A-Za-z0-9_]*`. Values are inserted verbatim — substitution is purely textual, including inside string literals and comments.
Use cases: per-environment tenant IDs, namespace prefixes, region tags, or any other field that varies between deployments without changing the rest of the config.
func EnvVariables ¶
func EnvVariables() Variables
EnvVariables collects every os.Environ() entry whose name starts with "WARDEN_VAR_" and exposes them under the suffix as a Variables map. Example: `WARDEN_VAR_TENANT=acme` becomes `${TENANT}` in source.
Use this to thread CI-injected values through to the loader without per-flag plumbing. Returns an empty map (never nil) if nothing matches.
func MergeVariables ¶
MergeVariables returns a new Variables map composed of every layer. Later layers override earlier ones, so the typical call order is (defaults, env, cli) — CLI flags win over env, env wins over defaults. Nil layers are skipped. Empty-string values are treated as set.