Frequently Asked Questions
Short answers with pointers into the guides. New to v3? See What’s New and Migrating from v2.
What Is “Access Control”?
Section titled “What Is “Access Control”?”The selective restriction of access to a resource. AccessControl models who is acting (roles), what they’re doing (actions), what they act on (resources and their attributes), and decides whether it’s allowed — optionally constrained by conditions, ownership, and mandatory gates. It merges RBAC and ABAC.
What’s an “Action”? A “Resource”?
Section titled “What’s an “Action”? A “Resource”?”An action is the operation performed (the CRUD verbs, or any custom action). A resource is a uniquely named thing being accessed; what counts as a distinct resource is a design decision. See Resources.
Do I Still Have to Check Ownership Myself?
Section titled “Do I Still Have to Check Ownership Myself?”No — that’s the big v3 change. Tell AccessControl how ownership is determined
once (policy.ownerField or policy.owner), pass the record in the check
context, and own is enforced. See Ownership.
With no resolver configured, own keeps its v2 behavior, so existing code
isn’t silently locked down.
.where() vs .require()?
Section titled “.where() vs .require()?”.where() is a conditional grant — it can only add access under a
condition. .require() is a mandatory gate — independent of grants, it can
only restrict. See Conditions and
Require Gates.
policy vs context — Which Goes Where?
Section titled “policy vs context — Which Goes Where?”If a condition reads it with $., it’s context; if the engine reads it to
decide behavior, it’s policy. See
Best Practices › policy vs context.
Sync or Async Checks?
Section titled “Sync or Async Checks?”Declarative checks are synchronous. A grant/gate that uses a custom function
({ fn, args }) requires the async path (grantedAsync / checkAsync). See
Async & Custom Functions.
Can I Use AccessControl with a Database?
Section titled “Can I Use AccessControl with a Database?”Yes — it’s storage‑agnostic. Persist the model as flat rows
(getGrantsList()) and rehydrate it; the round‑trip is exact. See
Serialization & Databases.
How Do I Catch Typos (Unknown Roles/Actions/Resources)?
Section titled “How Do I Catch Typos (Unknown Roles/Actions/Resources)?”Turn on strict mode. roles is on by
default; enable actions/resources to throw on unknown names instead of
silently denying.
How Do I Audit Decisions?
Section titled “How Do I Audit Decisions?”Subscribe to the access event — it fires on every resolved check (granted
and denied) with a denial reason. See
Events & Auditing.
What Do I Do When AccessControl Throws?
Section titled “What Do I Do When AccessControl Throws?”A thrown AccessControlError means a fault (usually a misconfiguration), not
a normal “denied” — denials return granted: false. Never let a thrown
error fall through to “allow”.
- On the request path, use
tryCan(): it never throws — every failure resolves togranted: false. - In development/tests, use
can()so a typo throws loudly. - Branch on the stable
err.code(detect withAccessControl.isACError(err)), not on message text.
See Security Considerations for the full hardening story.
Is It Production-safe / How Is It Tested?
Section titled “Is It Production-safe / How Is It Tested?”Single pinned runtime dependency, zero production advisories
(npm audit --omit=dev), 100% coverage, mutation‑tested, plus an adversarial
suite and a property fuzzer. See
Best Practices › Quality & testing.