Network building on the canals and railways of the Midlands, two to four players, about two hours. Complexity 3.9 of 5. A copy is on the shelf and another sits on table three most evenings.
Jo VainioPlayed it twice at the back tables before buying. Worth the shelf space.
A simple yes or no does not tell the user why they are blocked.
The library returns a Decision object with a reason and the rule that decided. If data is missing, the status is unevaluable and the decision lists exactly which fields to fetch.
Who is asking
What they want to do
The listing is
Allowed
The bookseller who listed it wants to edit this listing. The rule seller-edits-own says yes.
See which rule decides the access and which fields the library needs for a final answer.How decisions work →
Review access changes before you ship
Identify who gains or loses access in your pull request.
A one-line rule edit can widen or narrow access for a whole group, and a line-by-line diff of the JSON does not show who is affected.
The diffMatrix function compares two policy documents. It reports granted and withdrawn permissions and which checks now read different fields of the row.
Change one rule
What this change does
GainedBooksellers can now reprice listings that they sell.listing.reprice · allow-branch-added
Needs the rowChecks for reprice now read object.sellerId of the row. Asked before the row is fetched, they answer cannot tell.listing.reprice
Know exactly what the library prevents and what your app must handle.
Developers need to know where the library ends and their responsibility begins.
The library maps 37 attack classes to CWE and OWASP entries. Some are prevented by the engine, some use provided primitives, and some are listed as your application's job.
Entries from the security register
ID
Attack
CWE / OWASP
How it is handled
SEC-004
Unevaluable deny: a deny rule reading a path the projection lacks
CWE-863, A01:2021
A deny side that cannot be read outranks a matching allow; the decision is unevaluable and names the paths to fetch
SEC-005
Prototype pollution reaching a decision
CWE-1321
Every path read and every rule lookup is own-property guarded; every decision map is written with defineProperty
SEC-010
Incorrect default permissions
CWE-276, A01:2021
Nothing is allowed without an allow rule that matched: no rules, an empty rule list and an unknown action all refuse
It is a TypeScript authorization library that builds a policy into a JSON document. This allows the same synchronous checks to run on both your server and in the browser.
Does it need a service of its own?
No. It is a library with zero dependencies. You install it via npm and run it within your own application process.
Can I trust the browser check?
No. Browser checks are advisory and used to update the UI. You must re-decide every request on your server or gateway to ensure security.
What happens when the row is not loaded?
The library returns an unevaluable decision. The result is allowed: false, and the missing field lists the data needed to make a final decision.
Does it work with React?
Yes. The @evanion/react-acl package provides a PolicyProvider and a useCan hook to check permissions inside your components.
How do I notice changes when another service publishes new rules?
Use contractDrift from @evanion/acl/testing. It replays your own questions against the contract you pinned and the one CI fetched, then reports what moved between them.
Unified authorization logic
Run the same rules across every layer of your app. Every decision provides the reason and the rule used to reach it.