@alfiz/core holds the evaluator: the catalog, grammar, closures, and check logic. @alfiz/application wraps a storage driver into the full local provider. You define your permissions in TypeScript, wire up storage, and every can() check runs in-process against your own database with no external round-trip.
This page gets you from an empty project to a passing scoped check. It uses the in-memory driver so you can run it immediately; swapping in Prisma or MongoDB is a two-line change at the end.
1
Install the packages
@alfiz/application already depends on @alfiz/core; installing both is
for the explicit import. Add the verifier as a dev dependency when you are
ready to enforce coverage in CI:"type": "module"), and @alfiz/verify needs
TypeScript 5.5 or newer.2
Declare the catalog
The catalog is the single source of truth for every permission key and
every scope type. Declare it once, in the codebase that enforces it.Permissions are declared by their full dotted key, the same notation
every check, grant, role pattern, and nav entry uses, so a key at a call
site greps straight back to its declaration. Group levels (
src/alfiz.ts
docs,
docs.files) are inferred from the keys; you never declare them.parent is a commitment rather than a hint. A type declared parent: null has
the ancestor chain [scope, "*"] by declaration, which is what lets
a snapshot check it synchronously without consulting your resolver.
Omitting parent means exactly the same thing. If folders should nest
inside folders, name the type as its own parent
("docs.folder": { parent: "docs.folder" }) and it becomes
hierarchical like docs.doc.3
Create the Application
The Application is the provider: your database, your hierarchy, resolved
by your code.
ancestry is the seam where Alfiz asks your application
where a resource sits.src/alfiz.ts
parentPointerResolver builds a resolver from a synchronous
parent-pointer lookup. When your parent lookup hits the database, write
the resolver directly instead. An AncestryResolver is any function from
a scope id to its ancestor chain, nearest-first, and it may be async:4
Create the client
The client binds the catalog to the provider and gives you typed checks.Create it once at startup and share it across requests. Call
src/alfiz.ts
alfiz.close() on shutdown to detach from the provider’s invalidation
stream.5
Grant access
Every form of access is one row. Grant to a user, a group, an org,
everyone, or a service, and the shape does not change.provenance is required on every write and is validated before anything
is stored, so a written row can never be missing its audit entry.6
Run your first check
7
Take one snapshot per request
Server-rendered pages make hundreds of conditional-UI checks inside
A snapshot is one consistent instant of the caches, a stronger
per-request guarantee than repeated
.map() callbacks and render helpers that cannot be async. Take one
snapshot per request and check synchronously:can calls. Pre-resolve the
hierarchical scopes you intend to check with { scopes: [...] }; for a
list page whose row ids are not known until after the query, extend it
with await snap.resolve(rowScopes).Moving to a real database
memoryDriver() keeps everything in process and is for tests and local
development. For production, merge the Prisma schema fragment into your own
schema and swap the driver:
@alfiz/mongo at a
database:
Wire the deletion paths
Grants key on subject and scope strings, not foreign keys, so deleting a user or a resource in your own tables does not clean up its access. Call these from the same code paths that do the delete, or a reused id inherits stranded access:Next steps
Core Concepts
The five building blocks (catalog, grants, scopes, subjects, checks) and the semantic rules that are fixed rather than configurable.
The four enforcement points
Where checks belong: page, navigation, server action, and conditional UI, and which shape each one takes.
Static verification
Run
alfiz-verify in CI so an ungated server action fails the build instead of shipping.Per-request snapshots
One provider round-trip per request, then synchronous checks. The recommended pattern for server-rendered frameworks.