Polizy Authorization
Polizy is a Zanzibar-inspired, embeddable ReBAC authorization library for
TypeScript/Node.js. Permissions are stored as relationship tuples
(subject, relation, object[, condition]) and resolved through direct grants,
group membership (nested), hierarchy propagation, and wildcards.
When to Apply
Activate this skill when:
- User mentions "polizy", "authorization", "permissions", "access control"
- User asks "who can do what", "can user X do Y"
- User wants RBAC, ReBAC, or Zanzibar-style authorization
- User needs to check, grant, or revoke permissions
- User is implementing team/group-based access
- User is implementing folder/file permission inheritance
- User wants end users to define their own roles at runtime or a
permissions matrix (click-to-toggle roles × permissions)
- User wants to upgrade polizy to a newer version
Upgrading between versions
When the user wants to upgrade polizy in their project (e.g. "upgrade my
project to the latest polizy version", "I just bumped polizy, walk me through the
migration", "get me from 0.1 to the newest version"), route to
migrations/README.md — the upgrade router. It detects
the installed and previous versions, finds the relevant migration guides, and
applies them in order, step by step (e.g. 0.2→0.3→0.4→…) up to the newest
version available. Each published release bundles the full migrations/ history,
so multi-version jumps work.
Quick Concepts
| Concept |
Description |
| Tuple |
(subject, relation, object) — stored permission fact |
| Subject |
Who: { type: "user", id: "alice" } (a group can also be a subject) |
| Object |
What: { type: "document", id: "doc1" } |
| Relation |
Role typed direct | group | hierarchy (owner, member, parent, …) |
| Action |
Intent: view, edit, delete — mapped to relations |
| Condition |
Optional time window (validSince/validUntil) and/or attribute predicates (ABAC) |
Capabilities (0.6.x)
- Checks & queries:
check, checkMany (batch), checkOrThrow, explain
(why allowed/denied; now accepts an optional 2nd arg and never throws MaxDepthExceededError),
listAccessibleObjects (paginated), listSubjects (paginated via limit/offset after deterministic sort),
someoneCan (existence; short-circuits), countSubjects / countAccessibleObjects (always unpaginated),
listTuples (paginated). All public and ReadScope queries accept uniform ReadOptions
(consistency, preload, and contextualTuples). Note that checkMany shares contextual tuples batch-wide (per-request not supported),
and withReadScope operations accept no per-operation read options.
- Contract Test Suite: Validate custom adapters using the published
polizy/storage-tests suite.
- Writes (idempotent):
allow, allowMany, disallowAllMatching,
addMember/removeMember, setParent/removeParent. Use as to pick a
relation when the schema declares more than one group/hierarchy relation.
- Runtime custom roles:
withRoleScaffold(schema, …) merges a generic role
scaffold into a schema (type-preserving) and RoleRegistry gives typed sugar —
defineRole, grantToRole/revokeFromRole, assignRole/unassignRole,
deleteRole, listRoles, permissionMatrix (one read backing a
click-to-toggle roles × permissions UI). Roles are pure tuples: a custom
role built from existing actions needs no schema change. An optional
RoleCatalogStore (InMemory or Prisma) tracks role existence + labels only —
the engine never reads it.
- Conditions: time-boxed grants + attribute predicates (
eq ne in nin gt gte lt lte, dot-paths) evaluated against a per-check context.
- Wildcards:
everyone("user") grants to every subject of a type — and
(0.5.0) wildcard membership now propagates through group recursion, so
assignRole(everyone("user"), role) grants every subject of that type.
- Field-level ids: opt-in per object type via
fieldLevelObjects.
- Config:
defaultCheckDepth, maxDepthBehavior ("throw" | "deny"),
logger, fieldSeparator, and (0.5.0) defaultGroupRelation /
defaultHierarchyRelation (which relation addMember/setParent use when the
schema has more than one) plus nonSubjectTypes (object types kept out of
listSubjects unless requested via ofType).
Route to Specialized Skill
| Task |
Skill |
| Install / first-time setup |
polizy-setup |
| Define or change the model (relations, actions, fields, hierarchy) |
polizy-schema |
| Implement a scenario (team access, inheritance, temp access, revocation, fields) |
polizy-patterns |
End-user / runtime custom roles, permissions matrix (RoleRegistry) |
polizy-patterns (schema setup via withRoleScaffold in polizy-schema) |
Storage adapters (InMemory, Prisma, custom), polizy/storage-tests, performance, production |
polizy-storage |
| Debug unexpected allow/deny, errors |
polizy-troubleshooting |
| Upgrade polizy between versions |
migrations/README.md |
Minimal Example
import { defineSchema, AuthSystem, InMemoryStorageAdapter } from "polizy";
// 1. Define schema
const schema = defineSchema({
relations: {
owner: { type: "direct" },
viewer: { type: "direct" },
},
actionToRelations: {
edit: ["owner"],
view: ["owner", "viewer"],
},
});
// 2. Create AuthSystem
const authz = new AuthSystem({
storage: new InMemoryStorageAdapter(),
schema,
});
// 3. Grant permission (idempotent)
await authz.allow({
who: { type: "user", id: "alice" },
toBe: "owner",
onWhat: { type: "document", id: "doc1" },
});
// 4. Check permission
const canEdit = await authz.check({
who: { type: "user", id: "alice" },
canThey: "edit",
onWhat: { type: "document", id: "doc1" },
});
// => true
Related Skills
1---2name: polizy3description: Router for the polizy authorization library. Use when the user mentions authorization, permissions, access control, RBAC, ReBAC, Zanzibar, or asks "who can do what" questions. Routes to specialized skills. For upgrading polizy between versions, route through `migrations/README.md`.4license: MIT5---67# Polizy Authorization89Polizy is a Zanzibar-inspired, embeddable ReBAC authorization library for10TypeScript/Node.js. Permissions are stored as relationship tuples11`(subject, relation, object[, condition])` and resolved through direct grants,12group membership (nested), hierarchy propagation, and wildcards.1314## When to Apply1516Activate this skill when:17- User mentions "polizy", "authorization", "permissions", "access control"18- User asks "who can do what", "can user X do Y"19- User wants RBAC, ReBAC, or Zanzibar-style authorization20- User needs to check, grant, or revoke permissions21- User is implementing team/group-based access22- User is implementing folder/file permission inheritance23- User wants **end users to define their own roles at runtime** or a24 **permissions matrix** (click-to-toggle roles × permissions)25- User wants to **upgrade polizy** to a newer version2627## Upgrading between versions2829When the user wants to upgrade `polizy` in their project (e.g. "upgrade my30project to the latest polizy version", "I just bumped polizy, walk me through the31migration", "get me from 0.1 to the newest version"), route to32[`migrations/README.md`](./migrations/README.md) — the upgrade router. It detects33the installed and previous versions, finds the relevant migration guides, and34applies them **in order, step by step** (e.g. `0.2→0.3→0.4→…`) up to the newest35version available. Each published release bundles the full `migrations/` history,36so multi-version jumps work.3738## Quick Concepts3940| Concept | Description |41|---------|-------------|42| **Tuple** | `(subject, relation, object)` — stored permission fact |43| **Subject** | Who: `{ type: "user", id: "alice" }` (a group can also be a subject) |44| **Object** | What: `{ type: "document", id: "doc1" }` |45| **Relation** | Role typed `direct` \| `group` \| `hierarchy` (`owner`, `member`, `parent`, …) |46| **Action** | Intent: `view`, `edit`, `delete` — mapped to relations |47| **Condition** | Optional time window (`validSince`/`validUntil`) and/or attribute predicates (ABAC) |4849## Capabilities (0.6.x)5051- **Checks & queries:** `check`, `checkMany` (batch), `checkOrThrow`, `explain`52 (why allowed/denied; now accepts an optional 2nd arg and never throws `MaxDepthExceededError`),53 `listAccessibleObjects` (paginated), `listSubjects` (paginated via `limit`/`offset` after deterministic sort),54 `someoneCan` (existence; short-circuits), `countSubjects` / `countAccessibleObjects` (always unpaginated),55 `listTuples` (paginated). All public and `ReadScope` queries accept uniform `ReadOptions`56 (`consistency`, `preload`, and `contextualTuples`). Note that `checkMany` shares contextual tuples batch-wide (per-request not supported),57 and `withReadScope` operations accept no per-operation read options.58- **Contract Test Suite:** Validate custom adapters using the published `polizy/storage-tests` suite.59- **Writes (idempotent):** `allow`, `allowMany`, `disallowAllMatching`,60 `addMember`/`removeMember`, `setParent`/`removeParent`. Use `as` to pick a61 relation when the schema declares more than one group/hierarchy relation.62- **Runtime custom roles:** `withRoleScaffold(schema, …)` merges a generic role63 scaffold into a schema (type-preserving) and `RoleRegistry` gives typed sugar —64 `defineRole`, `grantToRole`/`revokeFromRole`, `assignRole`/`unassignRole`,65 `deleteRole`, `listRoles`, `permissionMatrix` (one read backing a66 click-to-toggle roles × permissions UI). Roles are **pure tuples**: a custom67 role built from existing actions needs no schema change. An optional68 `RoleCatalogStore` (InMemory or Prisma) tracks role existence + labels only —69 the engine never reads it.70- **Conditions:** time-boxed grants + attribute predicates (`eq ne in nin gt gte71 lt lte`, dot-paths) evaluated against a per-check `context`.72- **Wildcards:** `everyone("user")` grants to every subject of a type — and73 (0.5.0) wildcard membership now **propagates through group recursion**, so74 `assignRole(everyone("user"), role)` grants every subject of that type.75- **Field-level ids:** opt-in per object type via `fieldLevelObjects`.76- **Config:** `defaultCheckDepth`, `maxDepthBehavior` (`"throw"` | `"deny"`),77 `logger`, `fieldSeparator`, and (0.5.0) `defaultGroupRelation` /78 `defaultHierarchyRelation` (which relation `addMember`/`setParent` use when the79 schema has more than one) plus `nonSubjectTypes` (object types kept out of80 `listSubjects` unless requested via `ofType`).8182## Route to Specialized Skill8384| Task | Skill |85|---|---|86| Install / first-time setup | `polizy-setup` |87| Define or change the model (relations, actions, fields, hierarchy) | `polizy-schema` |88| Implement a scenario (team access, inheritance, temp access, revocation, fields) | `polizy-patterns` |89| End-user / runtime custom roles, permissions matrix (`RoleRegistry`) | `polizy-patterns` (schema setup via `withRoleScaffold` in `polizy-schema`) |90| Storage adapters (InMemory, Prisma, custom), `polizy/storage-tests`, performance, production | `polizy-storage` |91| Debug unexpected allow/deny, errors | `polizy-troubleshooting` |92| **Upgrade polizy between versions** | [`migrations/README.md`](./migrations/README.md) |9394## Minimal Example9596```typescript97import { defineSchema, AuthSystem, InMemoryStorageAdapter } from "polizy";9899// 1. Define schema100const schema = defineSchema({101 relations: {102 owner: { type: "direct" },103 viewer: { type: "direct" },104 },105 actionToRelations: {106 edit: ["owner"],107 view: ["owner", "viewer"],108 },109});110111// 2. Create AuthSystem112const authz = new AuthSystem({113 storage: new InMemoryStorageAdapter(),114 schema,115});116117// 3. Grant permission (idempotent)118await authz.allow({119 who: { type: "user", id: "alice" },120 toBe: "owner",121 onWhat: { type: "document", id: "doc1" },122});123124// 4. Check permission125const canEdit = await authz.check({126 who: { type: "user", id: "alice" },127 canThey: "edit",128 onWhat: { type: "document", id: "doc1" },129});130// => true131```132133## Related Skills134135- [polizy-setup](../polizy-setup/SKILL.md) — Installation and configuration136- [polizy-schema](../polizy-schema/SKILL.md) — Schema design137- [polizy-patterns](../polizy-patterns/SKILL.md) — Implementation patterns138- [polizy-storage](../polizy-storage/SKILL.md) — Storage adapters139- [polizy-troubleshooting](../polizy-troubleshooting/SKILL.md) — Debugging140- [migrations/README.md](./migrations/README.md) — Version upgrade router