PermDock

Comparison

How PermDock differs from permix, CASL, Kilpi, zap-studio/permit, Better Auth access control, Cedar and Amazon Verified Permissions, Open Policy Agent, the Zanzibar family (OpenFGA, Auth0 FGA, SpiceDB, WorkOS FGA), Casbin, accesscontrol, hosted PDPs, ZenStack and the AI SDK OPA adapter.

PermDock is in Phase 0: every PermDock capability below is a design, not shipped code. The other libraries are described as they were in September 2026, based on the landscape survey and the deep-dive research pages. Where a fact could not be verified it is left out rather than guessed.

The short version: the TypeScript space splits into in-process libraries with good key inference but weak data-layer and agent stories (permix, CASL, Kilpi, permit, Better Auth, accesscontrol, Casbin) and external policy engines with strings in and booleans out (Cedar and Amazon Verified Permissions, Open Policy Agent, the Zanzibar family of OpenFGA, Auth0 FGA, SpiceDB and WorkOS FGA, plus Cerbos, Permit.io and Oso Cloud). PermDock tries to be an in-process library with typed references over Standard Schema resources, one condition format that reaches SQL and RLS, structured decisions, and first-class MCP, AI SDK and AuthZEN surfaces.

permix

permix v4.1.2 is the closest library in adapter breadth: React, Vue, Solid, Svelte, Next.js App Router (per-request instance via React cache()), TanStack Start, Node, Express, Hono, Fastify, Elysia, tRPC, oRPC, Effect and Drizzle, all as subpath exports of one 2.64 kB gzip package, with llms.txt and agent skills. Its typing is template-literal keys (check('post.create')), its core is a mutable global setup(), hydration collapses function rules to booleans, there is no condition AST (so no SQL or RLS), no explain, no Standard Schema, no OpenAPI, no MCP, and React Native is undocumented. Twenty-five pull requests asking for these were closed or left open; PermDock is the clean-room answer (0001, permix lessons).

CASL v7

CASL (@casl/ability 7.0.1, about 1.4M weekly downloads) is the only mature in-process library that compiles conditions to Prisma and Mongoose where clauses and does field-level permissions. v7 (May 2026) modernised it: rulesToCondition, frozen rule arrays, reason on rules, relevantRuleFor. Typing is declared [Actions, Subjects] tuples rather than inferred from a definition; subject detection needs classes or subject() wrappers; there is no Standard Schema, no SSR hydration API beyond packRules, no Next.js, React Native, MCP or OpenAPI story, a dual CJS/ESM build, and no llms.txt. PermDock adopts CASL's rule-as-data and one-AST-many-interpreters architecture and drops subject detection and the shared can name (CASL v7 deep-dive).

Kilpi v1

Kilpi 1.1 is server-first: policies are functions returning Grant(subject) or Deny(), authorize() is async, protected queries redact fields post-fetch, an audit plugin batches decision events, an RSC plugin offers Access components, and @kilpi/client fetches decisions from an endpoint with caching and batching. It has 89 stars and no commits since December 2025. It has no Standard Schema (Zod internally), no data-layer where, no React Native, MCP or OpenAPI, zod and superjson in core, and $-prefixed instance members. PermDock keeps Kilpi's discriminated decision, subject narrowing through assert, layered unauthorized handlers and batched client decisions, and drops the rest (Kilpi v1 deep-dive).

@zap-studio/permit

@zap-studio/permit 2.0.1 is the only authorization library built on Standard Schema: resources are validators, rule parameter types are inferred, allow / deny / when compose policies, evaluation is fail-closed, and it is about 2.8 kB minified with structured errors and OpenTelemetry hooks. It ships no adapters, no hydration, no OpenAPI and no MCP, validates on every call, requires a resource object for every action, and makes @opentelemetry/api a required peer. PermDock adopts the Standard Schema resources, fail-closed evaluation and span-per-check, and makes validation a boundary concern (zap-studio/permit deep-dive).

Better Auth access control

Better Auth's createAccessControl(statement) plus newRole() gives typed RBAC from an as const statement, with async hasPermission on server and client, a synchronous checkRolePermission for static roles, and dynamic roles stored in the database. It is RBAC only (no conditions), has no UI helpers, hydration or data layer, and is coupled to Better Auth. PermDock layers on top through the better-auth provider: Better Auth resolves the session and roles, PermDock owns permissions, conditions and decisions (Better Auth provider).

Hosted and policy-language PDPs

Cerbos (YAML policies with CEL, embedded WASM PDP, query plan to Prisma and Drizzle, React hooks, AuthZEN support), Permit.io (OPA/OPAL-backed PDP, an MCP Gateway that proxies agent traffic), OpenFGA (Zanzibar ReBAC, listObjects for filtering, an MCP authorization guide), SpiceDB (Zanzibar with CEL caveats, gRPC SDK) and Oso Cloud (Polar rules, listLocal SQL fragments, "Oso for Agents" monitoring) all evaluate policies outside your TypeScript code. Their SDKs take strings and return booleans; none infer types from your model; MCP support is a proxy or a guide, not something you embed in your server. PermDock is embedded and typed, and interoperates with all of them through AuthZEN: its decision endpoint is an AuthZEN PDP and its pdp provider is an AuthZEN client (0011). It also offers what these vendors sell, without moving the decision: PermDock Cloud is an optional hosted AuthZEN Authorization Decision Service plus approval inbox and decision log that implements the same interfaces the open-source package ships with in-process defaults, so a Go service or an API gateway can enforce a TypeScript-authored policy while the TypeScript app keeps deciding locally (0021). Zanzibar-scale relation graphs remain a non-goal; bridge to OpenFGA or SpiceDB through a provider. The sections below on Cedar, OPA and the Zanzibar family go into the engines that were only named here.

ZenStack v3

ZenStack v3 (3.9.3) puts @@allow / @@deny policies in a Prisma-compatible ZModel schema and compiles them to SQL through Kysely, so reads are filtered and writes rejected at the query layer, with an auto-generated CRUD API and TanStack Query hooks. v3 removed the DB-free check() that v2 offered, so there is no way to ask "can this user?" in the UI without a database round-trip, policies are a DSL rather than TypeScript, and there is no llms.txt. PermDock keeps checks in-process and portable to both UI and SQL, and does not own the ORM.

@ai-sdk/policy-opa

Vercel's reference policy adapter for AI SDK 7 tool approvals evaluates Rego (via WASM or an HTTP OPA) inside toolApproval, narrows the tool list with opaCapabilityMiddleware, and offers a shadow mode. It fails open on unrecognised decisions (vercel/ai#19978) and requires writing Rego with no link to the application's permission model. permdock/ai-sdk fills the same hooks from the app's typed policy, maps the three-outcome Decision to approved / denied / user-approval, never returns not-applicable, and re-checks approvals against a replay-safe token (AI SDK adapter).

Cedar and Amazon Verified Permissions

Cedar is an Apache-2.0 policy language from AWS implemented in Rust, with @cedar-policy/cedar-wasm (4.12.0 on npm, published 2026-07-28) providing WASM bindings and TypeScript types for the engine's API. A policy is permit or forbid over a principal, action, resource scope with when / unless conditions; the default is deny and any matching forbid overrides every permit, the same combining rule as PermDock's deny-overrides-allow invariant (policy syntax). A schema declares entity types, their attributes and parent relations, and which actions apply to which principal and resource types with what context shape; the validator type-checks policies against it at authoring time rather than at evaluation, and the cedar-policy-symcc crate verifies properties about policy sets with concrete counterexamples (repo). Principals, resources and their attributes are passed to the authorizer as an entity slice with each request.

Amazon Verified Permissions hosts Cedar policies in policy stores (one per application or tenant, with an optional schema that rejects invalid policies), evaluates them through IsAuthorized, BatchIsAuthorized (up to 30 requests that share a principal or a resource) and IsAuthorizedWithToken (principal taken from a Cognito or OIDC JWT), returns ALLOW or DENY with the determiningPolicies that produced the decision, and offers an Express middleware integration. AVP states that it currently uses Cedar version 4.7.

How PermDock relates: Cedar's determiningPolicies is the closest published analogue to PermDock's matched and denials, and its schema-validated policies are the model for permdock collect --check and permdock doctor catching references to permissions that do not exist. The differences are the authoring format and the type story. Cedar policies are a separate language with their own schema; PermDock policies are TypeScript data whose types come from the Standard Schema you already have, so permissions.post.update is a reference the compiler checks, whereas Action::"updatePhoto" is a string the validator checks later. Cedar evaluates over an entity slice you assemble; PermDock's portable conditions also compile to where clauses and RLS so the database does the filtering. Neither Cedar nor AVP has an approval outcome. AVP is a hosted PDP with its own request shape rather than AuthZEN, so pairing it with permdock/pdp needs an AuthZEN-speaking front or a dedicated mapping; that is not in the plan today.

Open Policy Agent and Rego

Open Policy Agent is a CNCF-graduated, general-purpose policy engine: policies are written in Rego over arbitrary JSON input and data, and OPA is queried by microservices, Kubernetes admission control, CI pipelines, API gateways and Envoy external authorization alike. It runs as a daemon or sidecar with a REST API (POST /v1/data/<path> with an input document returns result and a decision_id for decision logs), as a Go library, or compiled to WebAssembly (opa build -t wasm) and loaded with @open-policy-agent/opa-wasm (loadPolicy, setData, evaluate; 1.10.0, last published November 2024). Its Compile API performs partial evaluation and, with targetDialects such as ucast+prisma, sql+postgresql and sql+mysql, turns a Rego policy plus known input into data filters for your own database, which is the nearest thing among external engines to PermDock's where.

The trade is generality against integration. Rego is domain-agnostic, so nothing ties a rule to the application's resource types; the SDK is JSON in and JSON out with no inference from your model; deny-overrides-allow is a convention you write, not a property of the engine; and the deployment story assumes an OPA process or a WASM bundle you build separately. The docs already discuss OPA in agent form: @ai-sdk/policy-opa (above) evaluates Rego inside AI SDK tool approvals and fails open on unrecognised decisions. PermDock treats OPA as a peer PDP: with an AuthZEN front it becomes a remotePdp target (pdp); infrastructure policy (admission, gateways, CI) stays OPA's domain and is outside PermDock's scope.

The Zanzibar family: OpenFGA, Auth0 FGA, SpiceDB, WorkOS FGA

These systems store relationship tuples (user:anne is viewer of document:roadmap) and compute permissions by walking a graph defined in a schema, following Google's Zanzibar paper. They answer "does this user have this relation to this object?" and its inverses at a scale an in-process library cannot, and PermDock does not try to (roadmap).

  • OpenFGA is the CNCF-owned open source engine. The model DSL declares types and relations (type document with define viewer: [user]), tuples are written through the API, and the query endpoints are Check, BatchCheck, Read, Expand, ListObjects (all objects of a type a user has a relation with, for access-aware filtering of small collections) and ListUsers. Conditions attach Google CEL expressions with typed parameters to tuples, evaluated with request context and capped at an evaluation cost of 100 by default; contextual tuples cover the rest of ABAC. SDKs exist for JavaScript, Go, Java, .NET and Python; Postgres, MySQL and SQLite are the production datastores. The MCP server authorization pattern models a tool type with a can_call relation, checks it on every request and filters the tool list with list-objects.
  • Auth0 FGA is the managed service built on OpenFGA, with the same model, tuples and check API, documented at docs.fga.dev; Auth0 Lab records the product graduating as Okta FGA, so both names refer to the same service.
  • SpiceDB (AuthZed) uses a .zed schema of definition, relation and permission with union, intersection, exclusion and arrow (parent_folder->read) operators, CEL caveats on relationships, and gRPC APIs CheckPermission, CheckBulkPermissions, LookupResources, LookupSubjects, WriteRelationships and a Watch API with ZedTokens for consistency. Its list-endpoint guide documents the three filtering strategies: LookupResources ids into WHERE id = ANY(...) when the accessible set is small, CheckBulkPermissions over a page of candidates otherwise, and Materialize (early access) for a denormalised local copy. AuthZed ships an MCP server and LangChain integrations.
  • WorkOS FGA is often grouped with this family but its current docs describe something narrower and deliberately DSL-free: resource types configured in the Dashboard, resources registered with a parent, roles and permissions scoped to a resource type that may include child-type permissions, and assignments that propagate down the hierarchy. Subjects are organization memberships and groups. The access check endpoints are check, listEffectivePermissions, listResourcesForMembership and listMembershipsForResource, called from @workos-inc/node as workos.authorization.check(...); organization-scoped roles ride in the AuthKit JWT so org-wide checks need no API call.

How PermDock relates: permdock/pdp plans openfga and spicedb presets that wrap check / listObjects and CheckPermission / LookupResources behind the provider interface, so filter returns the permitted ids and where compiles to in(row.id, ids), which is exactly the vendors' own documented list-endpoint pattern. PermDock stays the typed PEP in front (references, boundary validation, Decision, Problem Details, MCP refusals, approvals) and never stores or models tuples. Note that the Zanzibar answer to agents is to model the agent as another principal with its own tuples; PermDock's two-principal subject and approval-required outcome are additive to that, not a replacement.

Casbin and node-casbin

Casbin is an Apache-licensed authorization library with implementations in Go, Java, Node.js, PHP, Python, .NET, Rust and more. Access control is expressed as a .conf model file in the PERM metamodel (Policy, Effect, Request, Matchers) plus a policy file or database adapter: r = sub, obj, act defines the request, p = sub, obj, act, eft the policy shape, m = r.sub == p.sub && r.obj == p.obj && r.act == p.act the matcher, and the effect expression combines matches, so some(where (p.eft == allow)) is allow-if-any while some(where (p.eft == allow)) && !some(where (p.eft == deny)) gives deny-override (how it works). Eighteen model families ship as examples, including RBAC with domains and tenants, ABAC through attributes such as r.obj.Owner, RESTful path matching, priority and deny-override. node-casbin (casbin 5.51.1 on npm) exposes newEnforcer(model, policy), enforce(sub, obj, act) and enforceSync, a Management API and an RBAC API (getRolesForUser), adapters for persistence and watchers for multi-node consistency; the in matcher operator is not yet available in Node-Casbin.

Casbin and PermDock differ on almost every axis that matters here. Casbin's request, policy and matcher fields are runtime strings with no TypeScript inference; the matcher is an expression string evaluated at runtime; deny-override is something the model author opts into with the effect expression, whereas PermDock makes it an invariant; there is no data-layer compilation, no UI or SSR story, and no agent surface. Casbin's strengths are polyglot parity, a model that can express Bell-LaPadula or priority policies PermDock does not attempt, and a runtime policy-management API. If several services in different languages need to enforce one policy, Casbin (or a PDP) fits and PermDock does not.

accesscontrol

accesscontrol (3.1.0, MIT, ESM) is a chainable role-and-attribute library: ac.grant('user').createOwn('video'), ac.can('admin').updateAny('video').granted, glob-notation attribute lists (['*', '!password', 'profile.*']) with filter() for field-level output, .extend() inheritance with deny-overrides, and possession (any versus own) enforced when a policy.ownerField or resolver is configured. v3 added a policy engine: .where('$.order.value <= 100000') conditions with a canonical JSON form, require() gates that can only restrict, custom actions through .action() / .do(), groups and categories, defineCondition for async custom checks, an access audit event stream, tryCan() that never throws, snapshot() / restore() for persistence, prototype-pollution-safe name handling and an official NestJS integration.

Its priorities overlap with PermDock's invariants (fail closed, deny wins, prototype safety, audit events) and it is the most complete pure in-process RBAC and ABAC engine in the survey. The gaps are the ones PermDock exists for: roles, resources and actions are strings, there is no Standard Schema or inferred instance type, conditions evaluate in memory only and never reach SQL or RLS, there is no snapshot for the client, no framework adapters beyond an Express example and the Nest package, and no MCP, AI SDK or AuthZEN surface.

Feature matrix

Legend: yes, partial (with a note), no, planned (PermDock, not implemented). Hosted PDPs are summarised as a group; see the paragraphs above for which vendor does what.

CapabilityPermDockpermixCASL v7Kilpi v1zap/permitBetter Auth ACHosted PDPsZenStack v3ai-sdk/policy-opa
Typed references (not strings)plannedno (template-literal keys)no (declared tuples)partial (inferred keys, Proxy paths)no (string actions)no (as const keys)non/a (DSL)no (Rego)
Standard Schema resourcesplannednononoyesnononono
Portable conditions to SQL / RLSplanned (Drizzle, Prisma, Kysely, RLS)nopartial (Prisma, Mongoose where)nononopartial (Cerbos query plan, Oso listLocal, OpenFGA listObjects)yes (compiled SQL)no
Structured decisions / explainplanned (Decision with reasons, alternatives)nopartial (reason, relevantRuleFor)partial (Deny message, audit plugin)partial (structured errors)nopartial (audit logs, policy tests)nono
Snapshots carrying conditionsplannedno (booleans only)partial (packRules)no (fetch per decision)nonononono
React Server Components / Next.jsplanned (Cache Components, use cache: private)yes (per-request cache())nopartial (RSC Access)nopartial (via Better Auth Next SDK)nonono
React Nativeplanned (persisted snapshot)undocumentedworks, DIYnonopartial (Expo via Better Auth)partial (fetch clients)nono
MCP tool authorizationplanned (scopeChallenge, filtered list_tools)nononononopartial (Permit gateway proxy, OpenFGA guide)nono
AI SDK approvalsplanned (toolApproval, capability middleware, needsApproval)nononononononoyes (fails open on unknown)
OpenAPI security emissionplanned (3.2 with 3.1 fallbacks)nononononononono
AuthZENplanned (endpoint and client, certification target)nononononopartial (Cerbos)nono
RLS import into app permissionsplannednononononononono
llms.txt / skillsplanned (both, plus docs MCP)yes (both)nonollms.txtllms.txtllms.txt (Cerbos, OpenFGA, SpiceDB, Oso)non/a
Core sizebudget about 3 kB gzip2.64 kB gzipabout 6.17 kB min+gzipnot measuredabout 2.8 kB minpart of Better AuthSDKs 2.6–5.6 MB unpacked (Permit, SpiceDB)ORM-sizedWASM or HTTP

By engine

The second table lists every product named on this page, including the policy engines the first table groups as "Hosted PDPs", against the seven axes that decide most adoption conversations. "Typed references" means the compiler checks a permission reference against your definition; "Agent approvals" means a distinct third outcome rather than allow or deny; "not verified" means the vendor's docs consulted for this page neither confirm nor deny.

ProductTyped referencesStandard SchemaConditions compile to SQL / RLSAgent approvals (approval-required)AuthZENEmbedded vs hostedPolicy language
PermDockplannedplannedplanned (Drizzle, Prisma, Kysely, RLS generate and import)planned (with replay-safe token and a pluggable ApprovalStore)planned (PDP via permdock/authzen, PEP via permdock/pdp, hosted ADS via PermDock Cloud)embedded; hosted AuthZEN ADS optional, never required to decideTypeScript data (allow / deny grants with portable conditions)
permixno (template-literal keys)nonononoembeddedTypeScript booleans and closures
CASL v7no (declared tuples)nopartial (Prisma, Mongoose where)nonoembeddedJSON rules with MongoDB-style conditions
Kilpi v1partial (inferred keys)nonononoembedded, server-firstTypeScript functions returning Grant / Deny
zap/permitno (string actions)yesnononoembeddedTypeScript (allow, deny, when)
Better Auth ACno (as const keys)nonononoembedded, coupled to Better Authas const statement plus roles
ZenStack v3n/a (DSL)noyes (compiled SQL via Kysely)nonoembedded in the ORMZModel @@allow / @@deny
@ai-sdk/policy-opanononoyes (fails open on unrecognised decisions)noembedded WASM or HTTP OPARego
Cedar (cedar-wasm)no (User::"alice" entity literals)nonot documented (evaluates over a supplied entity slice)nonot verifiedembedded (Rust crate or WASM)Cedar, schema-validated
Amazon Verified Permissionsnononononot verifiedhosted (AWS)Cedar, policy stores with templates
Open Policy Agentnonopartial (Compile API data filters to SQL and UCAST / Prisma; no RLS)nonot verifiedembedded (Go library, WASM) or daemon / sidecarRego
OpenFGAnonopartial (ListObjects ids)no (agents modelled as principals)not verifiedself-hosted serverFGA model DSL plus relationship tuples, CEL conditions
Auth0 FGA (Okta FGA)nonopartial (ListObjects ids)nonot verifiedhosted (built on OpenFGA)FGA model DSL plus tuples
SpiceDB / AuthZednonopartial (LookupResources ids, CheckBulkPermissions, Materialize)nonot verifiedself-hosted or AuthZed Cloud.zed schema plus relationships, CEL caveats
WorkOS FGAnonopartial (listResourcesForMembership ids)nonot verifiedhostednone; resource types, roles and permissions configured in the Dashboard
Casbin / node-casbinnononononoembeddedPERM .conf model plus CSV or adapter policy
accesscontrolnononononoembeddedchainable API with JSON grants and .where() expressions
Cerbosnonopartial (query plan to Prisma, Drizzle)noyesembedded WASM, sidecar or HubYAML with CEL
Permit.ionononononot verifiedhosted PDP (OPA / OPAL)UI-managed, OPA underneath
Oso Cloudnonopartial (listLocal SQL fragments)nonot verifiedhostedPolar

Vocabulary map

The same idea has a different name in each engine. The table maps PermDock's terms to the four engines most often evaluated alongside it; a blank cell means the engine has no direct equivalent in the docs consulted.

PermDockCedar / AVPOPA / RegoOpenFGA / SpiceDBCasbin
permissions.post.update (typed reference)Action::"updatePhoto" entity literal, declared in the schemaa rule name such as allow, chosen by conventiona relation or permission name (can_view, edit) on an object typeact field of the request
resource(Schema, ...)entity type with attributes and parents in the schemainput shape, unconstrainedtype document with relations / definition documentobj field, a string
role('member', [...])principal in Group::"..." or a principal is User scopea rule over input.user.rolesrole#assignee, group#member usersetsg = _, _ role definition with g, alice, data2_admin grouping policies
allow(...) / deny(...)permit / forbid (forbid always wins)separate rules; deny-override written by handcomputed sets with union, intersection, exclusion (- in SpiceDB)p.eft allow / deny with effect expression some(where (p.eft == allow)) && !some(where (p.eft == deny))
where: { authorId: subject.id } (portable condition)when { resource.owner == principal }Rego expression over input and dataCEL condition on a tuple (OpenFGA) or caveat (SpiceDB)matcher expression such as m = r.sub == r.obj.Owner, or eval(p.sub_rule) over a rule stored in the policy
decide() with matched / denialsIsAuthorized decision plus determiningPoliciesresult plus decision_idCheck boolean; Expand for the userset treeenforce boolean
simulate([...])BatchIsAuthorized (up to 30, shared principal or resource)one query per decisionBatchCheck / CheckBulkPermissionsBatch API
filter / whereCompile API data filters (SQL, UCAST / Prisma)ListObjects / LookupResources ids
approval-required
snapshot for the clientWASM bundle evaluated in the browserMaterialize (SpiceDB, early access) for services

Pick by use case

Single Next.js app with RBAC. If roles are static, there are no per-row conditions and Better Auth or Clerk already issues the session, their built-in access control is enough and adding a library is overhead. The moment a rule reads the row (authorId, orgId, published), or the same check has to run in a Server Component, a client component during an instant navigation and a route handler, PermDock's typed references, snapshot and permdock/next are the fit; permix covers the same frameworks with booleans only. Keep Better Auth or Clerk as the provider rather than replacing them.

Supabase or RLS-heavy app. Choose between owning the policy in the database or in the application. Hand-written RLS or ZenStack's compiled ZModel policies keep the database authoritative but leave the UI guessing. PermDock's answer is one portable condition that permdock rls generate turns into CREATE POLICY statements for Supabase, Neon or a GUC dialect, import reads back from pg_policies into definePermissions(), and verify proves parity against fixtures (RLS adapter, Supabase provider). If your policies already use CASE, multi-join subqueries or custom functions, expect them to import as opaque and stay database-only.

Relationship graphs at scale. Google-Drive-style sharing, nested groups, folder inheritance across millions of objects: use OpenFGA, Auth0 FGA or SpiceDB, and put the model and tuples there. PermDock's portable conditions cannot express arrow permissions or recursive membership and will not try. PermDock can still be the PEP: permdock/pdp with the planned openfga or spicedb preset makes decide call check, filter call listObjects / LookupResources, and where compile to in(row.id, ids), while references, boundary validation, Problem Details and approvals stay typed in the application (pdp). WorkOS FGA's hierarchy model fits the same slot once its API is wrapped, but there is no preset planned.

Enterprise central PDP. When a security team owns policy on its own release cycle, in Cedar (AVP), Rego (OPA) or a certified AuthZEN PDP such as Cerbos, Topaz or Keycloak, keep policy there. Any AuthZEN speaker is a remotePdp target in the current permdock/pdp design: PermDock builds the subject, action, resource request from typed references, folds the boolean answer into a Decision, fails closed on anything unrecognised, and still applies local deny grants and delegation intersection. AVP and OPA need an AuthZEN front or a dedicated mapping first. In the other direction, permdock/authzen exposes a PermDock policy as an AuthZEN PDP so gateways and other services can ask it.

Agent tool authorization. MCP servers, AI SDK 7 tools and Claude Agent SDK hooks all need the same three answers: may this actor call this tool for this user, which tools should it even see, and which calls need a human first. The Zanzibar pattern (a tool type with can_call, list-objects for the visible set) and @ai-sdk/policy-opa (Rego in toolApproval) each answer the first two with a boolean. PermDock's MCP, AI SDK and Claude Agent adapters answer all three from the same typed policy: approval: 'human' grants produce approval-required with a token bound to permission key, resource id, subject and actor, the actor never exceeds the delegating user, and unknown decisions are denials, never executions (approvals, delegation).

Browser agents (WebMCP). A page exposing tools through document.modelContext.registerTool() has to decide which tools this user may trigger before an in-page agent sees them. None of the engines above has a client-side story; a hosted PDP would need a network round-trip per registration. permdock/webmcp registers one tool per action the current snapshot allows and unregisters when the snapshot changes, and the server-side route the tool calls re-checks with the same policy.

When not to use PermDock

  • You need shipped code today. PermDock is Phase 0; CASL, permix, accesscontrol, Casbin and every engine on this page are installable now.
  • Policy is authored and deployed by people outside the application's release cycle. Cedar with AVP, OPA and Cerbos give you a policy store, a separate deploy pipeline and a language a security team can own; PermDock's policies are TypeScript that ships with the app.
  • Several backends in different languages must enforce one policy. PermDock is TypeScript only. OPA, Cedar, Casbin and the Zanzibar servers have Go, Java, Python and .NET clients.
  • Authorization is a relationship graph. Sharing trees, nested groups and inheritance over large object counts belong in OpenFGA, Auth0 FGA or SpiceDB; PermDock's pdp bridge covers only the enforcement half.
  • You need formal analysis of the policy set. Cedar's validator and symbolic compiler can prove properties and produce counterexamples; PermDock offers simulate and matrix tests, not proofs.
  • The rules are pure RBAC with no conditions and you already run Better Auth, Clerk or WorkOS. Their built-in checks are sufficient and add no dependency.
  • The database should be the single point of enforcement and the UI can afford a round-trip. ZenStack's compiled policies or hand-written RLS achieve that without an application-side model.
  • The policy is about infrastructure rather than application resources: Kubernetes admission, gateway routing, CI gates. That is OPA's domain.

What PermDock does not try to do

  • Replace authentication: sessions, tokens and users come from Better Auth, Clerk, Supabase, Convex or your own code.
  • Require a network call to decide, or a policy language; policies are TypeScript data evaluated in-process. PermDock Cloud is an optional hosted AuthZEN ADS, approval inbox and decision log built on interfaces that ship with in-process defaults (0021).
  • Model Zanzibar-scale relation graphs; use OpenFGA or SpiceDB through a provider.
  • Issue tokens or verify signatures in core; those live in adapters and the token layer.

See the roadmap for phases and the landscape survey for the per-library detail and gap matrix these paragraphs are drawn from.

Sources

Facts about the engines added to this page were read from their official documentation, repositories and npm pages on 2026-09-06. The TypeScript libraries are sourced in the landscape survey and the deep-dive pages.

On this page