permix
Version:
Permix is a lightweight, framework-agnostic, type-safe permissions management library for JavaScript applications on the client and server sides.
111 lines (84 loc) • 3.09 kB
Markdown
name: permix-check
description: >-
Checks permissions with Permix check(): dot paths, callbacks, ~all and ~any,
ActionSpec with entity data, ReBAC closures, isReady. Use when implementing
authorization checks, resource-level rules, or permix.check in app code.
type: core
library: permix
library_version: '4.1.1'
requires:
- permix-getting-started
sources:
- 'letstri/permix:docs/content/docs/guide/check.mdx'
- 'letstri/permix:docs/content/docs/guide/rebac.mdx'
- 'letstri/permix:docs/content/docs/guide/ready.mdx'
- 'letstri/permix:permix/src/core/check.ts'
# Permix — checking permissions
Docs: https://permix.letstri.dev/docs/guide/check
## Dot-path (single action)
```ts
permix.check('post.create')
permix.check('workspace.billing.view')
```
## Callback (multiple conditions)
The callback receives `c`. Each `c('path')` is evaluated immediately to a boolean:
```ts
permix.check(c => c('post.read') && c('post.update'))
permix.check(c => c('post.delete') || c('admin.override'))
permix.check(c => !c('post.read'))
```
## Aggregate tokens
| Token | Meaning |
|-------|---------|
| `'~any'` | At least one rule in scope is truthy |
| `'~all'` | Every rule in scope is truthy |
```ts
permix.check('~any') // any permission in the tree
permix.check('post.~all') // all post.* rules
permix.check('workspace.~any') // any rule under workspace
```
Dynamic function rules are invoked without data when aggregating; entity-only rules may count as `false`.
## Entity-aware actions (ReBAC / ABAC)
Attach a `type` to actions that need the resource at check time:
```ts
interface Post {
id: string
authorId: string
}
const permix = createPermix<{
post: [
'read',
{ name: 'update', type: Post },
{ name: 'delete', type: Post, required: true },
]
}>()
const currentUser = { id: userId }
permix.setup({
post: {
read: true,
update: post => post?.authorId === currentUser.id,
delete: post => post.authorId === currentUser.id,
},
})
permix.check('post.update', post) // optional data if not required: true
permix.check('post.delete', post) // required: true — data required
```
**ReBAC pattern**: capture the **actor** in closures at `setup` time; pass the **resource** at `check` time. No separate ReBAC API.
## Readiness
```ts
permix.isReady() // sync
await permix.isReadyAsync() // wait until setup (or initial rules) completed
```
Gate UI or early checks when permissions load asynchronously (fetch user, then `setup`).
## Common mistakes
| Mistake | Fix |
|---------|-----|
| Checking before `setup` | Call `setup` after auth; use `isReady` in UI |
| Path not in schema | Add action to `createPermix<D>()` generic |
| Missing check data | Add `type` / `required: true` on action; pass entity to `check` |
| Trusting client-only checks | Enforce same paths on server (**permix-server** skill) |
| Expecting lazy `c()` | Combine with `&&` / `\|\|`; each `c()` runs immediately |
## Server vs client
Use the same schema and path strings on server middleware and client hooks so types and behavior stay aligned.