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.