Policymaker is a monorepo for fast, type-safe authorization policies in JavaScript and TypeScript. The synchronous core remains the default. A separate async constructor is available when a rule genuinely needs database or network I/O.
| Package | Responsibility |
|---|---|
policymaker |
Framework-independent RBAC, ABAC, role inheritance, direct permissions, synchronous policies, and explicit async policies. |
@policymaker/react |
Typed sync/async providers and conditional UI boundaries. These checks are for user-interface behavior only. |
@policymaker/next |
Next.js server helpers that enforce a core policy through the framework's redirect() and notFound() primitives. |
The dependency direction is intentionally one way:
@policymaker/react ──> policymaker
@policymaker/next ──> policymaker
The core package has no React or Next.js dependency. Policymaker decides whether an action is allowed; it does not authenticate users, persist policy data, or transform sensitive data. Redactive remains a separate concern: Policymaker authorizes an operation, while Redactive detects, removes, masks, or transforms data. This monorepo contains no Redactive adapter.
Use createPolicy when all authorization data is already available:
import { createPolicy } from 'policymaker';
const policy = createPolicy({
actions: ['document:read'] as const,
roles: {
reader: ['document:read'],
},
});
const allowed = policy.can({
subject: { roles: ['reader'] },
action: 'document:read',
});Use createPolicyAsync only when a rule must await external data:
import { createPolicyAsync } from 'policymaker';
type Subject = { readonly id: string };
type Document = { readonly workspaceId: string };
const policy = createPolicyAsync<'document:update', Subject, Document>({
actions: ['document:update'] as const,
rules: [
{
id: 'active-membership',
effect: 'allow',
actions: ['document:update'],
when: async ({ subject, resource }) =>
resource !== undefined &&
hasActiveMembership(subject.id, resource.workspaceId),
},
],
});
const allowed = await policy.canAsync({
subject: { id: 'user_1' },
action: 'document:update',
resource: { workspaceId: 'workspace_1' },
});Do not pass an async predicate to createPolicy, and do not mix sync method
names with an async engine. See the
core documentation and
API reference for the complete contracts.
The workspace uses pnpm and requires Node.js 20 or newer.
pnpm install --frozen-lockfile
pnpm build
pnpm test
pnpm typecheckSee CONTRIBUTING.md for the complete validation matrix and RELEASING.md for the fixed-version, three-package release process.
Authorization must be enforced at a trusted server boundary. React checks can hide or disable interface controls, but they do not replace server-side authorization. Report vulnerabilities using SECURITY.md.
MIT