PermDock

PermDock

Typed permissions for TypeScript apps, APIs, databases, and AI agents.

PermDock lets you define permissions once as typed references over the Zod, Valibot or ArkType schemas you already have, grant them to roles with portable conditions, and check them in React, React Native, Next.js, Hono, tRPC and MCP servers. The same conditions compile to SQL where clauses and Postgres Row Level Security policies, and the same decision object drives tool approvals in the Vercel AI SDK and the Claude Agent SDK.

import { definePermissions, resource, definePolicy, role, allow, subject, createPermDock } from 'permdock'

export const permissions = definePermissions({
  post: resource(Post, { id: 'id', actions: ['read', 'update', 'delete'], collection: ['create', 'list'] }),
})

export const policy = definePolicy(permissions, {
  roles: [
    role('member', [
      allow(permissions.post.read),
      allow(permissions.post.create),
      allow(permissions.post.update, { where: { authorId: subject.id } }),
    ]),
  ],
  subject: (user: User | null) => user && { id: user.id, roles: user.roles },
})

const permdock = await createPermDock(policy, user)
permdock.can(permissions.post.update, post) // boolean
permdock.decide(permissions.post.delete, post) // { outcome: 'granted' | 'denied' | 'approval-required', ... }

What makes it different

  • Reference-based. permissions.post.update is a typed object with a key, a scope and a schema. Go-to-definition works, renames are safe, and there are no template-literal unions for TypeScript 7 to chew through.
  • Standard-Schema-native. Resources are defined from any Standard Schema validator; instance types are inferred; untrusted inputs are validated at trust boundaries.
  • Policy as data. Roles are arrays of allow / deny grants with portable conditions. One condition evaluates in the browser, filters arrays, compiles to Drizzle / Prisma / Kysely where, and generates RLS.
  • Decisions, not booleans. decide() returns granted, denied or approval-required with reasons and alternatives; adapters turn that into Problem Details, MCP refusals and AI SDK approval states.
  • Non-blocking UI. Snapshots carry conditions to the client so usePermission answers ownership checks offline and <Protected> never blocks a Next.js 16.3 instant navigation.
  • Agent-native. Two-principal subject (principal + actor + delegation), MCP, AI SDK and Claude Agent SDK adapters, an AuthZEN decision endpoint, skills and llms.txt.
  • Authentication stays upstream. PermDock consumes verified material only: sessions, JWKS-verified JWTs via permdock/jwt (with a FAPI 2.0 profile), Supabase, Clerk and Better Auth claims, MCP authInfo, workload identities. Core never verifies a token.

Where to go next

Status

PermDock is in Phase 0: the product plan, this documentation tree, README.md, PRODUCT.md, AGENTS.md and the Fumadocs app at /docs exist; the permdock package does not yet. Every adapter and standards page carries a Status and Phase line. See the roadmap.

On this page