@talosjs/permission component is a fine-grained access control layer built on CASL. You describe what is allowed as a set of abilities, each one a pair of an action (read, update, delete, …) and a subject (User, Article, all, …), then ask whether the current user can or cannot perform an action. Permissions are written as classes that extend the abstract Permission base, registered with the container, and attached to routes so authorization runs before your handler.
The authorization model
Authorization here is ability-based rather than flag-based: you grant concreteaction + subject pairs instead of scattering boolean role checks across your code. setUserPermissions(context) derives abilities from the request context, so a single permission class adapts to admins, owners, and guests. Because can() and cannot() take an optional field, you can allow reading name while forbidding password on the same subject.
For consistency, EPermissionAction ships more than 60 actions and EPermissionSubject ships common subjects, both with literal types you can extend per domain. Register a permission class with a decorator and you can resolve it from the container or wire it to a route.
How it works
A permission is a class extendingPermission. You implement three methods, then build() compiles the rules into a CASL ability you query with can/cannot.
The lifecycle is a fluent chain: declare context-driven rules, declare baseline rules, build, then query.
can and cannot throw a PermissionException (NOT_BUILT) if called before build(). The MANAGE action and the all subject act as wildcards, so this.ability.can("manage", "all") grants everything.
Decorator and usage
@decorator.permission()
Registers a permission class with the container. It accepts an optional scope (defaults to singleton).
Exceptions
The component throwsPermissionException when an ability is queried before the permission has been built. It carries a machine-readable key, a human-readable message, and a data object.
Getting permissions right
Compile the ability once after declaring rules, and always do it before checking; querying beforebuild() throws PermissionException (NOT_BUILT). Keep the chain consistent by running setUserPermissions() first, then allow(), with build() last, so context-driven rules are in place when you compile. Reach for EPermissionAction and EPermissionSubject over raw strings to keep checks consistent.
The manage / all wildcard grants every action on every subject, so scope it to trusted roles only. Keep ownership logic inside the permission: pass owner data through the context and decide in setUserPermissions() rather than in the controller. Use check() for coarse gates such as method, IP, or headers, and let can/cannot handle the fine-grained decisions. When you throw a PermissionException, keep its key constant and put the variable detail in data.
CLI command
Scaffold a permission class and its test file with the generator. It writes the class undermodules/<module>/src/permissions/<Name>Permission.ts and installs @talosjs/permission if it is missing.
The generated class is a ready-to-fill stub with the three methods and inline examples:
Use with Claude and Codex
The generator ships a matchingpermission:create skill. It runs the scaffold, then guides your AI agent through completing the permission: implementing allow() with ability rules and setUserPermissions() with role-based logic. Initialize the skills once for your agent.
- Claude
- Codex
Prompt
permission:create --name=EditArticle, then implements allow() and setUserPermissions() so only the right users can edit articles.