Skip to content

Design decisions

wardn is opinionated. These are its most important decisions. The full design document is docs/DESIGN.md.

Decision Choice Why
What wardn models Roles and permissions in a product-defined scope tree Covers the needs of nearly every product without a policy language. Sharing individual objects across millions of them is a different job, for OpenFGA or SpiceDB alongside wardn.
Where it runs A library inside your application No server to operate, no network hop per check.
Permissions vs roles Permissions are code; roles are data Admins combine what developers made checkable, never more.
Fixed evaluation rules Default deny, deny wins, errors deny The same meaning in every product; nothing to misconfigure.
Custom roles Supported, grants only, owned by a scope Every admin panel needs them; denies from admins cause confusing lockouts.
The escalation guard On for every user-initiated change; counts denies and whole wildcards The most common serious flaw in home-grown RBAC.
Guard scope Evaluated where the change happens Checking every descendant scope is unbounded; a deny restricts its holder, not what they delegate.
Time-limited access Built in, checked on every request Common in banking; adding it later would force a migration on every product.
Audit Before every change, can veto Nothing changes without a record.
Caching Off by default; caches data, not decisions; signed entries A stale permission is a security bug.
Storage A small interface plus a conformance suite Any database, proven correct.
Delivery Built complete, hardened and reviewed before adoption A standard library must not be hardened while products depend on it.
SQL Server Not supported until a product needs it Products use PostgreSQL and MySQL; untested support is a liability.
External audit None for v1.0; an internal security review instead v1.0 rests on the internal review, a test per threat, property tests, fault injection, fuzzing and mutation testing.
Your own transaction (v1.1) The transaction travels in the context; Warden.Transaction invalidates the cache after the commit Access changes must commit with the application’s own writes, without changing any v1 signature.
Checking many scopes (v1.1) CheckEach: one permission at up to 1,000 scopes, in a few store reads Filtering a list one Can at a time costs a few queries per item.
A pgx store (v1.2) A separate module with exactly the SQL store’s PostgreSQL tables pgx applications can’t make a *sql.Tx from a pgx.Tx; a shared schema means no data migration to switch stores.
Stability From v1.0, nothing breaks within v1, checked on every change Upgrading authorization must be safe and boring.

Questions about this page?

Ask below, point out something wrong or unclear, or suggest an improvement. Reading needs no account; to post, sign in with GitHub.