zap-permit-policy-authoring
SKILL.md
@zap-studio/permit — Policy Authoring
Setup
import { z } from 'zod';
import { createPolicy, allow, when } from '@zap-studio/permit';
import type { Resources, Actions } from '@zap-studio/permit/types';
const resources = {
post: z.object({ id: z.string(), authorId: z.string() }),
} satisfies Resources;
const actions = {
post: ['read', 'write'],
} as const satisfies Actions<typeof resources>;
type AppContext = { user: { id: string; role: 'user' | 'admin' } };
const policy = createPolicy<AppContext>({
resources,
actions,
rules: {
post: {
read: allow(),
write: when((ctx, _action, post) => ctx.user.id === post.authorId),
},
},
});
Core Patterns
Compose conditions with and, or, and not
import { when, and, or, not } from '@zap-studio/permit';
const canEdit = when(
and(
(ctx, _action, post) => ctx.user.id === post.authorId,
not((_ctx, _action, post) => post.locked === true),
),
);
const canRead = when(
or(
(_ctx, _action, post) => post.visibility === 'public',
(ctx, _action, post) => ctx.user.id === post.authorId,
),
);
Add role inheritance checks with hasRole
import { when, hasRole } from '@zap-studio/permit';
const hierarchy = {
guest: [],
user: ['guest'],
admin: ['user'],
} as const;
const adminOnly = when(hasRole('admin', hierarchy));
Choose merge strategy explicitly
import { mergePolicies, mergePoliciesAny } from '@zap-studio/permit';
const strict = mergePolicies(basePolicy, tenantPolicy); // all must allow
const permissive = mergePoliciesAny(basePolicy, temporaryOverridePolicy); // any can allow
Common Mistakes
HIGH Using action missing from actions map
Wrong:
await policy.can(ctx, 'publish', 'post', post);
Correct:
const actions = {
post: ['read', 'write', 'publish'],
} as const;
await policy.can(ctx, 'publish', 'post', post);
can() first checks actions[resourceType]; missing actions always resolve to false.
Source: zap-studio/monorepo:packages/permit/src/index.ts
HIGH Assuming invalid resources still hit rule function
Wrong:
await policy.can(ctx, 'write', 'post', { id: 123 } as any);
Correct:
await policy.can(ctx, 'write', 'post', {
id: '123',
authorId: ctx.user.id,
});
Resource validation runs before policy evaluation; invalid payloads short-circuit to deny.
Source: zap-studio/monorepo:packages/permit/src/index.ts
MEDIUM Expecting mergePoliciesAny to enforce deny-overrides
Wrong:
const merged = mergePoliciesAny(basePolicy, restrictivePolicy);
// expecting restrictivePolicy to always win
Correct:
const merged = mergePolicies(basePolicy, restrictivePolicy);
// deny-overrides behavior
mergePoliciesAny returns allowed when any policy allows; use mergePolicies for strict composition.
Source: zap-studio/monorepo:packages/permit/src/index.ts
See also: zap-validation-standard-schema/SKILL.md — invalid resource payload behavior.
Weekly Installs
1
Repository
zap-studio/monorepoGitHub Stars
153
First Seen
9 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1