PermDock
Getting started

Naming

The PermDock naming convention, the use* and get* duality, reserved words, and what each adapter's createPermDock returns.

PermDock has one naming rule: the brand is the noun. The decision object is a PermDock, the variable is permdock, the factory is createPermDock, and the import path, not the identifier, says which framework you are in. Every package, docs page, skill, error message and example follows this page.

The three core names

NameKindMeaning
PermDocktypeThe immutable, request-scoped decision object returned by createPermDock. Methods: can, decide, assert, explain, filter, pick, where, simulate, snapshot, on, actions; tenancy: tenant, team (derived instances), derive (the same subject with other customRoles, approvalPolicies or relations), memberships, tenants, heldRoles, audiences, assignableRoles, assignablePermissions (read-only), decideRoleChange; graph: loadRelations, whoCan. Vocabulary trees: permissions, roles, plans.
permdockvariableThe conventional name for a PermDock instance, and the npm package name. Also the CLI binary (permdock collect).
createPermDockfunctionExported by core and by every server and agent adapter. Core returns a PermDock; adapters return a framework-shaped object.
import { createPermDock } from "permdock"; // core: PermDock
import { createPermDock } from "permdock/next"; // Next.js: { getPermDock, getPermission, getSnapshot, requireAccess, PermDockProvider, permdockHandler }
import { createPermDock } from "permdock/hono"; // Hono: { permdock, protect }
import { createPermDock } from "permdock/mcp"; // MCP: { protectServer }

There is no NextDock, HonoDock, createNextPermDock or createMcpPermDock. When two adapters are used in one file, alias at the import: import { createPermDock as createHonoPermDock } from 'permdock/hono'.

use* on the client, get* on the server

React reads permissions synchronously from a snapshot; servers resolve them asynchronously per request. PermDock follows the duality next-intl uses for useTranslations / getTranslations and useExtracted / getExtracted:

Client (permdock/react)Server (createPermDock from permdock/next)Returns
usePermDock()await getPermDock()A PermDock (snapshot-backed on the client, full policy on the server)
usePermission(permission, data?)await getPermission(permission, data?){ allowed, status } on the client; the same shape awaited on the server
<Protected>await requireAccess({ permission, data?, tenant? })Renders children or a fallback on the client; on the server, the granted Decision, or forbidden() / unauthorized()
usePermissions([...], data?), useFilter(permission, rows)(await getPermDock()).decide per reference, .filterOne entry per reference; the rows the subject may act on
useTenant(), useMemberships(), useRoles(), useAssignableRoles(), useAssignablePermissions()(await getPermDock()).tenants(), .memberships(), .heldRoles(), .assignableRoles(), .assignablePermissions()The active tenant with switchTo; the membership list; Role[] held in a tenant; Role[] the subject may hand out; Permission[], the custom-role ceiling the subject may hand out
useApproval(decision), useSubject()the ApprovalStore and the subject on the serverClient-side request-access flow; the snapshot's principal, actor and delegation
<PermDockProvider snapshot endpoint tenant><PermDockProvider> from the factory resultClient context; the server variant loads the snapshot once per request and serialises it

The client hooks and <Protected> are direct exports with no factory: the permission reference carries all the types, so usePermission(permissions.post.update, post) is fully typed without a generic wrapper. Only the server side needs a factory, because only the server holds the policy. describe(decision) is a framework-free core export used by every UI adapter and by Problem Details (UI). Vue keeps the use* composable names. Svelte does not: it exports setPermDock / getPermDock and readable stores (permission, permissions, filter, tenant, …) because Svelte has no React-style hooks. Solid uses accessor functions. The UI parity table lists them.

Definition and policy vocabulary

WordUsed forNever used for
definePermissionsBuild the reference tree from resources and groupsRules
renamed, formerKeys, renamedFromThe definePermissions option mapping former keys to current keys, the function that lists a leaf's former keys, and the catalog field that carries themKeeping an old leaf alive; aliases, legacyKeys, deprecated
defineRoles, definePlansBuild the typed Role and Plan trees (permdock.roles.admin, permdock.plans.pro)Grants; Postgres roles
Role, PlanFrozen vocabulary leaves { key, on?, assignable, meta } and { key, meta }. A role named only by string becomes a synthesised Role leaf with assignable: falseThe grant-list type; that is RoleBinding
RoleMeta, audienceA role's meta: ActionMeta plus audience, the surface its holders use ('staff', 'portal', 'platform'); permdock.audiences() and snapshot.audiences list the distinct values of the roles held in the active tenantA per-surface role list; a hard-coded role name in a layout
min, max, transferOnly, assigns, forRole ownership options: holders kept per scope instance, holders allowed, a holder count that only moves by transfer, the roles a holder may assign and revoke, the membership kinds (via) that may hold the role (ownership)minHolders, canAssign, allowedVia; activation (reserved for time-boxed roles)
decideRoleChange, RoleChange, RoleChangeDecisionpermdock.decideRoleChange({ kind: 'assign' | 'revoke' | 'transfer', role, scope, id, within?, target, holders? }), or scope: 'global' with no id for a global role, and assignableRoles({ scope: 'global' }): the ownership rules for one change, answered as granted (with the authorising role) or denied with denials; the subject is always the actorA write; an actor or subject taken from the change
resourceOne resource node: schema, id field, actions, collection, optional parent (which may be the resource itself), optional relations, optional version, optional restricted, optional disclosureAnything without a schema-or-actions shape
name (resource option)The resource name leaves, tables, relations and AuthZEN types use when the last path segment collidesChanging the key; alias, table
disclosureThe resource() option 'hide' | 'reveal' (default 'reveal'): with 'hide', a denied check on a loaded row answers 404 /not-found, the body a missing row gets, and the reason stays in auditvisibility, secret, a per-grant flag
restricted, ResourceRestricted, stopsThe resource() option naming a boolean column: a row where it is true is reached only by grants on itself, never by relations held on its ancestors or through its links; { field, stops } names the paths it closes (parent and link names) (relationships)A deny; the reserved role(..., { restricted }) option
FieldRelation, EdgeRelation, PrincipalRelation (edge, object, subject, expiresAt, principal, period, startsAt)The three relation shapes on a resource: a row field holding the principal id ({ field, memberOf? } or a string), an edge table ({ edge, object?, subject?, expiresAt? }), and a principal column with an optional validity period ({ principal, period?: { startsAt?, expiresAt? } })A tuple store; a relation declared on a grant
through, depthrelation(resource, name, { through: 'parent', depth }): follow the row's parent chain upward to resource, then its self-parent for up to depth hops (default 16, at most 32)recursive, levels; walking a memberOf relation
RelationSource, RelationChain, RelationHolder, memoryRelations, relationsThe object-graph interface (ancestors, related), its answers, its in-process default over rows and edges, and the createPermDock option that takes itA module-level cache; a decision
loadRelations, whoCan, WhoCan, Holder, HoldingViaawait permdock.loadRelations(permission, rows) fills the instance's relation cache for an async source; await permdock.whoCan(permission, row) lists holders and how (role, relation, share) with completeA grant; a partial list presented as complete
related, RelatedCondition, RelationGrantee, RestrictedAncestors, restrictedAncestors, passRestrictedThe condition node a graph relation compiles to, and the relation grantee type with through / depthA hand-written where
permdock_closure, permitted_<resource>_ids, permdock_closure_<resource>, permdock_restricted_<resource>The closure table, the per-resource helper keyed by relation name and the trigger-maintained refresh function permdock rls generate emits for graph grants, and the helper listing the rows a closed link does not reach belowA table PermDock writes at runtime; service_role
actionsThe resource() option listing instance-level actions (can(permissions.post.update, post)), and the instance verb permdock.actions(permissions.post, post) returning one result per leaf of a resourceType-level checks
collectionType-level actions (can(permissions.post.create))Instance checks
crud, readable, writableOption factories for resource(): conventional instance/collection split and default metaConstructors; grants; a default of resource()
mergePermissions, listPermissions, findPermissionRegistry helpers; functions so they never collide with resource namesMethods on the tree
parseCatalog, rowConditionKeys, catalogPathpermdock/catalog reads and freezes a permissions.catalog.json and lists its rowConditions: true keys; permdock/cli resolves the file path the way collect doesloadCatalog, readCatalog; a reader that returns null instead of throwing PermDockValidationError
compileWhere, CompiledWherepermdock/compile lowers a portable condition, for one subject, to the tree the ORM toWhere compilers renderA policy-time compile with no subject; RLS and PowerSync compile the condition themselves
createServerKernel, ServerKernel, ServerKernelOptions, tenantScope, TenantScope, TenantOptionpermdock/server: the kernel an HTTP adapter builds on. It hands out no instance, so it is not createPermDock. tenantScope resolves an adapter's tenant option against its framework context.createPermDock for an adapter, which would return no instance
isPermissionThe structural guard for a permission leaf (string key, scope, resource, action and an object meta), which also accepts a leaf that crossed JSON and lost its non-enumerable kindA registry lookup; identity stays by key
definePolicyBind vocabulary, scopes, grants, principal, context, validate to a definitionDefining permissions
roleA RoleBinding: named array of grants with an optional scope ({ on: <declared scope name> | resource reference, assignable }, typed against definePolicy({ scopes })), the ownership options min, max, transferOnly, assigns, for, exclusiveWith and a RoleMeta meta; activation and restricted are reserved; same-named bindings merge; first argument may be a Role leafPostgres roles; the only grantee
to, anyone, authenticated, relation, plan, actor, assuranceGrantee selectors on allow / deny. anyone() is the only selector that evaluates for principal: null; an array in to means every selector must matchA second permission check; hasRole
scopesThe definePolicy option declaring named scopes in order, each with its row key and an optional parent within ({ organization: { key: 'organization_id' }, customer: { key: 'customer_id', within: 'organization' } }) (named scopes)Permission scopes (.scope is the colon string form)
withinA scope's parent in definePolicy({ scopes }), and on a Membership the ids of its ancestor scopesparent (that is the resource chain)
scope, id, onThe two shapes of a Membership (an instance of a named scope, a resource role) and the on option of roleHard-coded tenant and team kinds in core
tenant, teamAliases of the first and second declared scope (in role on, memberOf, and the { tenant, team } membership input form); principal.tenant is the active instance of the first scopeScopes named after a provider's vocabulary when the policy's own names differ
Membership, CustomRole, CustomRoleGrantThe wire types for a scoped role assignment, a tenant-defined role (includes declared roles, grants of declared permission keys, optional scope and id) and one of its { permission, effect? } entriesClasses; a condition, approval or limit on a custom-role grant; all are plain JSON
customRoleClaimBuilds the compact memberships[].grants map (key, -key, @role per custom role name) that RLS helpers read in jwt mode with --custom-rolesA claim PermDock trusts without subjectFromJwt; a per-provider helper
custom_role_permissions, custom_role_includes, permdock_ceiling, permdock_custom_keysThe SQL objects permdock rls generate --custom-roles emits next to role_permissions and the helpersTables PermDock writes at runtime; a way around the ceiling
resolveCustomRole, validateCustomRoleThe one resolver of a custom role against the ceiling of assignable declared roles ({ grants, dropped }), and its summary { ok, permissions, dropped } for a save action and permdock doctor PD023 (custom roles)Widening a role; resolving a name a declared role already uses
CustomRoleDrop, CustomRoleDropReasonWhy a custom-role entry was left out: unknown-permission, outside-ceiling, condition-not-allowed, unknown-level (with level), or { role, reason: 'unknown-role' } for an includeDenial reasons; a drop is never a Decision
meta.readOnly, meta.destructive, meta.idempotentAction metadata the MCP adapters turn into readOnlyHint, destructiveHint and idempotentHint for tools whose author set no annotationA decision input; tags: ['destructive']
meta.x, MetaValueApplication-owned JSON on an action's metadata; PermDock carries it and never reads itA decision input; meta.custom, meta.extra
x (definition option), AppData, AppDataSchema, DefinePermissionsOptions, VocabularyOptions, PolicyAppDataSchemasOne Standard Schema per kind of app data: definePermissions(…, { x: { permission, resource } }), defineRoles(…, { x }), definePlans(…, { x }), definePolicy(…, { x: { grant, membership } }); AppData is the default plain JSON objectA decision input; declare module augmentation; extensions, custom
ResourceMeta, GrantMetaA resource's { title?, description?, x? } and a grant's { description?, x? }; carried to decisions, events, snapshots and the catalogPolicy fingerprint input
ResourceXTree, RESOURCE_XThe type definePermissions adds to a tree so getResource types meta.x; RESOURCE_X is a type-only key with no runtime valueA runtime property
Membership.xApplication-owned JSON on a membership, from a trusted source; dropped with an on('auth') schema event when invalid; never in claimsA condition input; metadata, attrs (that is the token claim)
obligations, AppObligation, ObligationInputallow(p, { obligations }) and the { kind: 'app', name, detail? } entries it puts on a granted decisionA new kind per app need; obligations on a deny
DenialDetailsThe map from a denial reason to its typed detail (limit, inactive-grant, deny), read through Denial<R>A free-form detail per reason
onDenied (adapter option), OnDenied, OnDeniedEventThe HTTP adapter hook that receives { decision, problem, request } for a refusal and returns Problem Details, a Response or nothing; it cannot grantonForbidden, errorFormatter; a hook that turns a denial into a grant
OnDeniedText, DeniedTextEventThe MCP and AI SDK onDenied hook, which may replace only the refusal textA hook that changes isError or structuredContent
context (adapter option), RequestContextThe adapter hook that returns server-derived JSON merged into subject.context for the requestA subject, membership, tenant or actor source; locals, extra
wrap, wrapPermDock, PermDockWrap, PermDockOverridesThe adapter option that wraps every request's instance after otel, and the helper that overrides methods and keeps tenant(), team() and derive() wrappedA plugin system; middleware, decorate
field, explain (protect options)The per-route field check and decision trace passed to decideA field mask; a trace on the wire
defaults, PermDockDefaultsProvider-wide content for the <Protected> slots a component leaves outfallbackComponent; a global mutable default
useDescribe, getDescribedescribe with the provider's messages; getDescribe in SvelteA second description format
meta.manageRolesOn a Role or a permission leaf: holding the role, or being granted the permission, makes the full ceiling assignable instead of only what the subject holdsA grant; a bypass of RoleSource.assignable
SnapshotAssignableOne { tenant, roles, permissions } entry of a snapshot's assignable listA second snapshot format
SnapshotScopeOne { name, key, within?, resources? } entry of a snapshot's ordered scopes listA tenant-and-team pair of keys
Scope, ScopeDeclaration, PolicyScopesInput, PolicyScopes, ScopeNamesA declared scope { name, key, within? }, one definePolicy({ scopes }) entry { key, within? }, the whole option, Policy.scopes in order, and the names on accepts (declared names plus the tenant / team aliases) (named scopes)A scope kind enum
GrantScope, RoleScopeWhere a grant applies ('global', a scope name, or { resource }) and what role(..., { on }) acceptsHard-coded 'tenant' | 'team'
MembershipSource, RoleSourceThe two subject-input interfaces (membershipsFor, rolesFor / assignable) passed as memberships and customRoles to every createPermDockStores; PermDock never writes a membership or role
SubjectResolverThe generic type of every subjectFrom* function: verified input in, Subject out, never throwsA class hierarchy
memoryRoleSourceThe in-process RoleSource over a static CustomRole[]Production storage
memoryMembershipSourceThe in-process MembershipSource over memberships keyed by principal idProduction storage
memorySnapshotSourceThe in-process SnapshotSource over one snapshot; set replaces it and calls each subscriberProduction storage
customRoleSource, CustomRoleReader, CustomRoleSourceOptionsA RoleSource over rolesOf(tenant), a read of every custom role of the tenant; read: 'held' skips the read when the subject holds only declared rolesA source that returns only the held roles; rolesForUser
globalRoles, scope: 'global'The RoleSource method returning platform custom roles, and the custom role scope that marks oneA tenant role without a tenant; platformRoles, systemRoles
ApprovalPolicySource, ApprovalPolicy, memoryApprovalPolicies, approvalPolicies, validateApprovalPolicy, ApprovalPolicyProblemApproval requirements kept as data, the entry shape, the in-process default, the createPermDock option, and the check that names why an entry does not load (unknown-permission, invalid, where-on-collection, stale-on-without-version); entries only add stagesGranting or relaxing an approval; approvalRules, workflows
composeMemberships, claimsFirstOne MembershipSource over several (merged, de-duplicated, list and version combined); a source that keeps the verified token's memberships until the token says they were truncated. An array passed as memberships is composedA store; a cache with its own TTL
MemberEntry, list, version, claimsFirstThe optional MembershipSource members: every member of one scope instance ({ principal: { id }, membership }), the principal's authorization version, and the claims-first flagmembers, epoch, revision
EntitlementSource, entitlementsFor, entitlements, memoryEntitlementSource, fromStripeEntitlements, testEntitlementSourceThe subject-input interface for plan names per tenant, its createPermDock option, the in-process default, the Stripe Entitlements reader over a structural client, and its conformance runnerA permdock/stripe entry; plans as an option name
fresh, stale, stale-credentials, authzVersion, authz_verThe definePolicy option listing permissions that need an up-to-date token, the subject flag, the denial reason, the permdock/supabase version reader and the claim it comparessensitive, strict, stale-token
onStaleThe claimsFirst option for a token whose authorization version is behind: 'deny' (default) denies fresh permissions, 'reread' reads the memberships from the sources insteadA re-read on every request
managedBy: 'idp', isExternallyManaged, externally-managedA membership the identity provider owns, its predicate, and the decideRoleChange reason that refuses to change itreadonly, locked, scim: true
fromSupabasePostgres, SupabasePostgresctx.postgres or ctx.postgresAdmin from @supabase/server's withPostgresClient as a SqlQuery, through its queryRawA Postgres client; a new connection
fromTable, fromJunction, SqlQuery, SqlMembershipSource, RoleThrough, supabaseMembershipsBudgetThe permdock/supabase SQL membership sources, the statement runner they take, their type, a role or user column read through another table, and the default claim budget in bytesA query builder; an ORM adapter
postgrestSources, subject_for, authz_version_for, members_of, SupabaseRpcClient, SupabaseRpcCaller, SubjectRecordThe permdock/supabase sources over PostgREST: one subject_for(p_user) call per user, generated next to the hook, gives memberships, customRoles(principal) and subject(userId) for a backend that only has supabase-js, authz_version_for(p_user) gives memberships.version without the full read, and members_of(p_scope, p_id) gives memberships.list; SupabaseRpcCaller is the client parameter, loose enough for a supabase-js client typed with a generated DatabaseA client-callable function; a subject from a request body
RoleSourceFactory(subject) => RoleSource | undefined, the per-subject form of every customRoles option, called once per instance with the resolved subjectA source that remembers the last principal it served
permittedIdsThe instances of a scope where the subject holds a permission with no row condition, minus denied ones, or with conditioned: true where a conditioned allow applies: the in-process mirror of permitted_<scope>_ids_by_permission; a listing, never a decisionA decision; a check of one row
countHoldersThe live holders of a role in one scope instance, from MembershipSource.list, for decideRoleChange's holdersA holder count from a second, unfiltered query
operationPermissions, operationPermissionsFromOpenApiOne declaration of each API operation's permission, "METHOD /path/{param}" to a permission, read by an HTTP gate (forRequest) and by permdock/mcp's permissionFor (forOperation)A regular-expression route table; a second mapping for the MCP tools
oauthScopes (MCP tool config, protect options, an operation entry, withPermDock config), oauthScopesFor, oauthScopesForRequest, oauthScopesForOperation, operations, ScopeGuardAn operation's own OAuth scopes in place of those derived from its permission, narrowing which coarse scope reaches each MCP tool or REST route that shares a permission, or the only gate of a route that checks no permission (protect(null), ScopeGuard); operations on permdock/server reads them from operationPermissionsA second permission only to tell two tools apart
supabaseApprovalStore, rls.approvals, approval_requests, permdock_approval_*The generated approval store (a table and one function per ApprovalStore method, executable by no client role) and the permdock/supabase store over itAn approval table a client role can read or write
rls.jsonSchema, approval_requests_body_schema'auto' | boolean: a pg_jsonschema check constraint that each approval body matches approval-request-v1.jsonA remote $ref; format assertions
APPROVAL_POLICY_UNAVAILABLEThe approval-policy-unavailable denial detail when an ApprovalPolicySource fails, as a constantA second spelling of the detail
supabaseClaims, SupabaseClaims, SupabaseMembershipClaim, SupabaseClaimsSchema, extendThe permdock/supabase Standard Schema of the hook's claim contract (loose, no validation library), its output types, and the method that merges an app's own claim schema over itsupabaseClaimsSchema, extendClaims; a strict object that rejects other claims
oauthScopes, PolicyOAuthScopeCoarse OAuth scopes an authorization server issues (mcp:read), each covering permissions; expanded into the token delegation and named in scope challengesscopeAliases, aliases, scopeMap
ClientNames, clients, actor.client, to: { kind, client }Stable names for OAuth clients whose ids differ per environment or come from dynamic registration: the subject resolver maps a verified client id to a name, and a policy delegation names the client instead of the idaliases, clientAlias, a client id in the policy
actorOf, delegationOf, SupabaseActor, SupabaseActorResultThe permdock/supabase rules that turn act / client_id into the acting party (oauth-client, support, impersonation, by act.kind) and scope into delegated scopes; { ok: false, reason: 'invalid-chain' } must denygetActor, parseActor; returning null for a malformed chain; admin or impersonator as an actor kind
anonymousSignInsSupabaseSubjectOptions option: 'deny' maps a token with is_anonymous: true to the anonymous subject, as rls.anonymousSignIns does in RLSallowAnonymous, a boolean
liveSessionSupabaseSubjectOptions option and Subject field: true once the caller checked the session against the Auth server for this request; never read from claimsfresh, which fresh permissions already name; sessionValid
supabaseClaimVectorspermdock/testing export: supabaseClaimFixtures as a supabase/sdk conformance vector file, { feature: "auth.session.get_claims", cases: [{ name, input, expected }] }supabaseConformance, which names no feature; a JSON file outside the package
requiresSupabase manifest field: { matrix, capabilities }, the capability-matrix release tag and the feature ids the setup depends oncapabilities at the top level, which reads as what PermDock offers
pageApprovals, approvalPageSize, encodeApprovalCursor, decodeApprovalCursor, listAllApprovals, ApprovalCursorPositionThe list-paging helpers the built-in approval stores use, exported from permdock/approvals for a custom ApprovalStore (approvals)A cursor format of the store's own; paginate, listAll
{ subject: { session: { live: true } } }The where form for a grant that needs a live session; normalised to { "op": "liveSession" }A principal.sessionLive ref, which would read a claim
Actor.readOnlytrue narrows every policy delegation to that actor to its read-only permissions (readOnlyHint); a Supabase support session sets it from act.read_onlyreadonly, writable: false
supabaseTenantClaimThe default claim (tenant_id) for the active tenant, shared by the hook, subjectFromSupabase and the RLS helpersA per-app tenant name; a column name
SupabaseHookManifest, SupabaseManifestMembership, SupabaseManifestRls, SupabaseManifestHelper, SupabaseManifestValue, SupabaseManifestColumn, supabaseHookManifestFixture, permdock supabase inspect, permdock.manifest.jsonThe version: 1 manifest of the generated hook and SQL helpers (helper names and signatures, tenant claim, budget, claims written, membership sources, RLS mode, deciding columns, marker majors), its types in permdock/supabase, its fixture in permdock/testing, and the file inspect --out writesapiVersion; a second hook generator
SupabaseManifestRole, SupabaseManifestThroughA manifest membership's role: a column, fixed roles, or a column with the roles table (table, id, column) it references; a membership's user takes the same throughapiVersion; a second hook generator
permdock supabase hook generateThe CLI command that compiles the SQL sources into the Custom Access Token Hookpermdock hook, permdock rls hook
attrs (supabase.hook.attrs, the claim)Allow-listed server-owned columns and app_metadata.<key> entries the hook writes for attribute conditions (principal.claims.attrs.<key>)profile, user_metadata, claims as a config key
supabase.hook.apiThe PostgREST wrappers <prefix>subject_for, <prefix>members_of and <prefix>authz_version_for (default schema public, prefix permdock_) that hook generate writes for a backend reaching Postgres through the Data API; postgrestSources({ api }) takes the same setting. The one place the hook grants to service_role, and only when the option is setA hand-written wrapper per function; a grant to authenticated
supabase.hook.beforeSchema-qualified functions (event jsonb) returns jsonb the generated hook calls first, in order; a result with error is the hook's answerA second access token hook; a check that writes the claims PermDock owns
supabase.hook.validateCheck the claims the hook wrote against supabase-claims-v1.json with pg_jsonschema and drop them all on a mismatchRaising an Auth error on a bad claim; validating claims another package owns
LimitStore, memoryLimitStore, limitsThe quota counter for limit grants, its in-process default, and the createPermDock option (core, every HTTP and agent adapter)Auto-installing a store; a thenable consume as granted
GrantLimit, mode, alertAtThe limit grant option { count, per, mode?, alertAt? }: mode is 'hard' (default) or 'soft', alertAt a fraction of count in (0, 1] (limits)A soft limit that grants when the store is unreachable
Quota, quota{ remaining, resetsAt } on a granted decision under a limit; resetsAt is Unix secondsA snapshot field; a denial
LimitDetail{ count, window, resetsAt }, the detail of a limit denial: the grant's count, its window in seconds and its reset in Unix seconds; HTTP adapters render it as 429 with Retry-After, RateLimit and RateLimit-Policyquota on a denied decision; a store read in the renderer
not-entitled, requiredPlans, SnapshotNotEntitledThe denial reason when a held role's grant fails only on its plan grantee; requiredPlans(decision) lists the plan keys when every denial is not-entitled; describe kind upgrade with plans; 403 /not-entitled with plans; the snapshot's notEntitled entries { permission, role, to }no-plan, upgrade-required, missing-entitlement; a 402
Obligation, obligations, over-limit, near-limitWhat a granted decision says the caller owes, as { kind } objects; the kinds so far are over-limit (a soft limit past its count) and near-limit (usage reached alertAt)A fourth outcome; an obligation on denied or approval-required
pdpThe HTTP adapter and kernel option taking createPermDock from permdock/pdp, so protect decides delegated permissions remotelyMaking the request-scoped instance async
ProtectOptions, trustedThe third argument of every protect ({ trusted: true } skips validating what the loader returns)A per-route validate mode
scope (decide option)The DecideOptions field naming a declared scope whose memberships alone answer a check (can(permission, undefined, { scope: 'organization' })); global roles still apply (scopes)scope(...) as a derived instance, tenantOnly, firstScope
requestThe permdock/trpc and permdock/orpc option mapping a context to its Web Request, and the permdock/nest option mapping a non-HTTP ExecutionContextReading a subject from it directly
problemFromErrorThe permdock/server function turning a thrown PermDock error into its Problem Details Response, undefined for anything elseAn error class; a catch-all that maps non-PermDock errors
ormParity, OrmParityScenarioThe permdock/testing runner comparing filter() in memory with the rows a database returns for toWhere(where()), and its scenario shapeA database the runner opens itself
testClientParity, ClientParityCaseThe permdock/testing runner asserting that fromSnapshot never grants what the server denies, per membership and tenant, and its case shapeDeriving expectations from the snapshot itself
testClientStore, ClientStoreFactoryThe permdock/testing runner for client snapshot stores and the factory it calls (scenario testing)A DOM or a framework renderer
WebMcpToolCallThe { input, token } argument a permdock/webmcp handler receivesPositional input arguments
listFields, requiredFieldstoWhere options: scalar-list fields whose contains is element membership (Prisma, Kysely), and Prisma's non-nullable fields, where a null branch folds awayInferring either from the Prisma client at runtime
prismaModelFields, PrismaModelFields, modelThe permdock/prisma function reading required and list fields from schema.prisma text or a DMMF datamodel, its result, and the toWhere / toPredicate option taking itLoading the schema file itself
toPredicateThe permdock/prisma compiler for Prisma 8 field-proxy predicatesA second toWhere overload
checkRow, RowCheckThe single-row check of permdock/drizzle, permdock/kysely and permdock/prisma, and its { found: false } | { found: true, granted } resultA not-found outcome on Decision
resolveRelatedThe permdock/prisma step replacing related nodes in a where() result with ids read through a raw queryA compiler; it returns a WhereResult
relations (compiler option), RelationsMappingWhere the graph tables live for Drizzle and Kysely toWhere and resolveRelated: tables and closureThe createPermDock relations source, which answers facts in process
match, groups, includes, linksRelationship declarations: an edge table's fixed column filter, its group holders, relations implying this one, and named to-one references walked by through: [...]Tuple rewrites; through naming a field
MemberOfParentA memberOf parents entry: a keyed { field, resource } or a bare field nameWalking a parent chain with a query
WhereResult.subjectThe non-enumerable subject a where() result carries so toWhere compiles memberOf without a second argumentA serialised field; it never reaches JSON
testAuthZen, AuthZenVectors, authzenTodoPolicy, authzenTodoVectorsThe permdock/testing AuthZEN interop runner, its vector layout (the interop harness files unchanged) and the interop Todo domain (authzenTodoPermissions, authzenTodoUsers, authzenTodoData)Vendoring the official vectors
testHttpAdapter, HttpMountedThe permdock/testing HTTP scenario runner and the value a mount returns (fetch, call, close)A server the runner starts itself
ArazzoSimulateInput, ArazzoPlan, arazzoFindingsThe simulate({ arazzo, openapi }) argument and result, and the resolution-only helper the CLI usesA permdock/arazzo package; a subject on the call
webBotAuth, verifyWebBotAuth, discoverViaSignatureAgentThe HTTP createPermDock option for RFC 9421 signatures, the verifier it calls ((request) => verifyWebBotAuth(request, options), so an app without it bundles no verification) and its Signature-Agent directory lookupA permdock/web-bot-auth package; trusting a host that is not on allow
InvalidSignatureErrorThrown by permdock(request) when a claimed Web Bot Auth signature fails; protect returns the Problem Details response insteadDowngrading a failed signature to an anonymous actor
allow, denyThe two grant constructors; both accept one reference or an array and a to granteeChecking
principalThe condition ref builder (principal.id) and the definePolicy option that maps a user to principal values. There is no subject ref builder; subject stays accepted as the definePolicy option nameThe RFC 8693 Subject (that stays subject on the instance)
contextThe condition ref builder for values loaded by the definePolicy context function (context.teamIds)Request-scoped state; the ctx a closure receives
principal, actor, delegationThe three parts of a subjectSynonyms for user or role
granted, denied, approval-requiredThe three Decision.outcome valuesnot-applicable, allow, deny
where, checkConditions on the current row and on the next rowQuery building outside conditions
sqlFunctionNamed SQL function plus a portable twin the evaluator, filter, where and snapshots run; rls generate emits the callA runtime database round-trip; a closure; inlining the function body as the only representation
opaqueImported SQL with a fingerprint and no twin; evaluates to falseA portable grant
fields, pickSchema keys on a grant and the redaction helper permdock.pick(permission, row)CASL globs; a permission per field
validFrom, validUntil, validityThe grant options bounding when a grant applies (RFC 3339 or Unix seconds), and the normalised { from?, until? } on Grant and a snapshot grant; an inactive allow denies with inactive-grantexpiresAt (that is a membership's end), notBefore, window, a clock operator in where
groupThe grant option naming a condition group in generated SQL: the grant key is <permission>#<group> instead of a positional #nA positional key in hand-written SQL; a group per role
requiresThe allow option naming a permission, or a list of them, the subject must also hold through a role, globally or on the row's scope instance, for the grant to countpermission() as a grantee; a where on roles
inherit(permission, { through }), RelationSource.rowThe grantee that holds on a row when the subject holds permission on the row a link list or the parent points to, decided by every grant of that permission; row reads that row by idfrom, via, a relation per way the target becomes readable
key, scopeThe dotted (post.update) and colon (post:update) string forms of a permissionPublic API arguments
subjectFrom<Provider>Turning verified material into a Subject: subjectFromJwt, subjectFromIntrospection, subjectFromSupabase, subjectFromSupabaseSession, subjectFromClerk, subjectFromBetterAuth, subjectFromMcp, subjectFromCapability, subjectFromCiOidc, subjectFromApiKey; each provider mapper takes a schema option (authentication)Verifying, logging in, or anything that can throw
JwtPrincipalThe base principal type permdock/jwt exports (id from sub, issuer from iss, assurance, RFC 9068 roles and groups)A session type; claims stay on principal.claims
SupabasePrincipalThe base principal type permdock/supabase exports (id from sub, roles and tenant from hook claims, memberships, assurance.acr from aal)A session type; user_metadata is never read
supabaseRls, authorizeSqlRLS compile hints for permdock rls and the authorize() function SQL with an optional tenant parameterA policy builder; it does not decide
suspension (RlsSuspension, SupabaseSuspension, RlsSuspendedScope, SupabaseSuspendedScope, RlsSuspendedMembership, SupabaseSuspendedMembership)The rls config key, supabaseRls and authorizeSql option naming the users and scope-instance tables (RlsActiveRow / SupabaseActiveRow: table, id, disabledAt, status, active) the generated helpers, authorize() and the token hook read to drop suspended rows, and per scope keep, the permissions a suspended instance's members still hold, which a membership carries as Membership.keep; memberships.keep is the same list for a membership whose table's disabledAt column is set (RlsMembershipTable, fromTable columns.disabledAt, fromJunction disabledAt) (RLS)A createPermDock option; in process, suspension is a MembershipSource concern
permdock_has, permitted_<scope>_ids, member_<scope>_ids, role_permissionsThe SQL objects permdock rls generate emits: permdock_has(p_grant) for global roles, one permitted_<scope>_ids(p_grant) per declared scope (permitted_tenant_ids and permitted_team_ids for a policy that declares none), over the seeded role_permissions (role, permission, grant_key, scope, effect), and one membership-only member_<scope>_ids() per declared scope, plus member_<scope>_ids_for(p_user) on Supabase for a scope with a membership source, executable by supabase_auth_admin only, and in database mode permdock_has_for(p_user, p_grant) and permitted_<scope>_ids_for(p_user, p_grant), executable by no client role; permdock_has_permission(p_permission), permitted_<scope>_ids_by_permission(p_permission) and grant_keys(p_permission, p_scope, p_effect) take a permission key and answer with its unconditional allows minus any deny; a grant key is the permission key or permission#n (RLS)Per-row checks; anything a client calls with another user's id; <scope>_ids_for_user or member_<scope>_ids(p_user) for the per-user helper; permitted_<scope>_ids() with no argument or <scope>_ids_for_member for the membership-only helper
permdock_role_permissions, permdock_permission_keysThe role and catalog readers permdock rls generate emits: permdock_role_permissions(p_role, p_scope, p_tenant, p_scope_id) lists the permission keys a role holds with their effect, permdock_permission_keys() every declared key and permdock_permission_keys(p_scope) those some declared role is allowed on a scope, both executable by authenticated (RLS)Hand-written joins over role_permissions; role_permission_keys or permission_keys
permdock_trusted_role_permissions, rls.trustedReadersThe role reader for server code without the caller check, and the Postgres roles granted execute on itA flag on the member-facing reader; service_role hard-coded
permitted_<scope>_permission_keys, permitted_<scope>_permission_keys_forEvery permission key the caller (or a named user) holds on one scope instance, as the permission-key helpers answer each key, in one call (RLS)A loop of permdock_has_permission calls; held_permissions
permitted_<resource>_rows, permitted_<resource>_rows_for, rowHelpersThe per-resource row helper rls.rowHelpers turns on: the row ids the caller (or a named user) may act on with one permission, as the generated policies decide it (RLS)permitted_<resource>_ids (that is the graph helper keyed by relation); allowed_rows
rls.membershipSourcesThe fromTable / fromJunction sources the database-mode helpers read for a scope rls.memberships maps no table for; default supabase.hook.membershipsA second membership mapping per scope
permdock_can_assign, permdock_can_assign_for, permdock_holders_<scope>, permdock_transfer_only_<scope>The ownership objects permdock rls generate adds when a role declares a rule: permdock_can_assign(p_role, p_scope_id) for the application's policies on its membership tables (a null p_scope_id for a global role), permdock_can_assign_for(p_user, p_role, p_scope_id) for trusted SQL acting for a stored user in database mode, the deferred holder-count trigger (min, max) and the transfer-only statement triggers on each scope's membership table (ownership)A trigger that writes; a check that trusts the caller's role claim over the membership table
<table>_visible, <table>_visible_fields, permdock_key, --fields views, --revoke-columns, rls.fields, rls.revokeColumnsThe field views permdock rls generate --fields views emits: a security_invoker view per table with field-limited read grants whose restricted columns are case when <permitted> then col end, and with --revoke-columns the owner-rights companion it joins on permdock_key while the table keeps only unrestricted columns readable; rls import reads them back as the fieldViews export, rlsParity({ fieldViews }) and rls verify compare them with pick, doctor PD030 flags columns still readablePostgres column privileges per app role, a permission per field, or a view that reads past RLS
secretKeys, SupabaseSecretKeyThe permdock/supabase/middleware option mapping a named Supabase secret key (authKeyName from withSupabase) to a service principal with { id, tenant, roles, permissions }, as a service credentialA list of accepted keys; withSupabase's auth: 'secret:<name>' decides which keys verify
withPermDockThe @supabase/middleware entry permdock/supabase/middleware's createPermDock returns: requires jwtClaims upstream, contributes ctx.permdock; withPermDock({ protect, data }) short-circuits with Problem DetailsA PermDock naming pattern; with* is the pipeline's convention and appears elsewhere only as withSubject and withOtel
createJwtSubjectResolverThe cached, reusable form of subjectFromJwt, one per issuer (JWT adapter)A second createPermDock
createClerkSubjectResolverThe subjectFromClerk resolver that caches each user's memberships: 'all' list for cache.ttl (Clerk adapter)A token or session cache
verifyDpopProofThe exported DPoP check permdock/jwt also runs when sender: 'dpop'; replay (a ReplayStore, memoryReplayStore() from permdock/jwt) refuses a reused proof jtiA second verifier; it only checks the proof
TokenVerifier, TokenSignerThe two JOSE interfaces core declares as types (extension interfaces)Anything that throws; anything that decides
joseTokenVerifier, joseTokenSignerThe permdock/jwt implementations of the two interfaces over the optional jose peerExports of core
verifier, signerThe option names for a TokenVerifier (subjectFromJwt, permdock/ssf) and a TokenSigner (permdock.snapshot, approvalsHandler, permdock/cloud)jwt, jose, crypto as option names
discovery, acceptThe permdock/jwt options for OpenID Connect Discovery (an issuer URL that supplies jwks_uri and issuer) and for which token kind a resolver accepts ('access-token' default, 'id-token')Guessing the token kind from its claims
subjectFromIntrospectionAn RFC 7662 or RFC 9767 introspection response in, Subject outAn HTTP client; the call is yours
<provider>RoleSourceA provider's RoleSource implementation (betterAuthRoleSource)A subject mapper
authorizationProviderpermdock/better-supabase: better-supabase's authorization config value (AuthorizationProvider, apiVersion: 1), built from the manifest and catalog; approver sets canApprovePermDock's own decision path
bucketPolicy, topicPolicypermdock/better-supabase: the access policy of defineBucket and defineTopic, mapping operations to Permission referencesrls.storage and rls.realtime, which permdock rls generate writes
apiKeyClaimOptionspermdock/better-supabase: the claim and tenantClaim options of better-supabase's apiKeyClaims() and apiKeyResolver(), from rls.apiKeysVerifying a key
subjectFromBetterSupabasepermdock/better-supabase: a Subject from better-supabase's AuthSession, with apiKeys for apiKey sessions and plans for the features claimVerifying the session; better-supabase did that
toolPolicypermdock/better-supabase: the authorize and visible hooks of better-supabase's createMcp, for tools whose meta is a PermissionTools on the official MCP SDK (permdock/mcp)
credentialGuardpermdock/better-supabase: a better-supabase CredentialProvider whose token uses PermDock decides firstStoring or refreshing tokens
describedescribe(decision) returning { kind, title, detail, alternatives } for tooltips and Problem DetailsLogging; it is pure
SnapshotThe JSON type of permdock.snapshot(); format major is the v fieldA versioned type name (SnapshotV2); majors live in v
parseSnapshotRead snapshot JSON; rejects unknown majors and forbidden keys (__proto__, constructor, prototype)Evaluating; it is a reader, not createPermDock
fromSnapshotBuild a client PermDock from a Snapshot so portable grants evaluate locallyA second policy; closures stay on the server
snapshotForThe synchronous, deterministic snapshot builder for app-owned 'use cache: private' functions (snapshots)A function that adds a cache directive, reads headers or awaits a source
snapshotPromisePermDockProvider prop on permdock/react: an unawaited Promise<Snapshot> from a Server Component; hooks answer pending until it resolves, or suspend through use() with suspendAwaiting the snapshot in a layout
cacheLifeForpermdock/next: { stale } for cacheLife() from a snapshot's expiresAt and issuedAt, clamped to 30..300 secondsA cache directive; the app calls cacheLife
mayAccessmayAccess(policy, user, permission, { tenant }): optimistic check for a proxy; false only when the declared roles provably lack the permissionA decision; the page still calls can / decide
mayUsemayUse(permdock, permission): whether some grant in the instance's snapshot could match for its subject, active tenant and delegation; the listing hint permdock/mcp, permdock/a2a and hosts with their own MCP server (better-supabase createMcp) use to filter toolsA decision; the call still runs can / decide
approvalHeaders{ 'PermDock-Approval': token } for a retried mutationMinting a token; it only wraps the one decide returned
ApprovalStore, memoryApprovalStore, approvalsHandler, cancelApprovalsThe pluggable store behind approval-required, its in-process default, the Fetch routes for approvers, and the helper that rejects pending requests for a session (permdock/approvals)Deciding; a store never influences decide
consume, consumedAt, consumeApproval, resumeDecision, storedApprovalTokenThe atomic single-use step on ApprovalStore, the field it sets, and the helpers that apply a resume token, or find the stored one for a recomputed decisionused, redeemed, a consumed status
ApprovalError, isApprovalError, assertApproverThe error (with a closed code list), its guard (matches by name and code, so a duplicated module copy still maps) and the approver check (by, escalation once open, no repeat) a custom ApprovalStore applies in resolvePlain Error from a store, which approvalsHandler maps to 500
permdockApprovalThe context key the ai-sdk and openai adapters read an explicit resume token from; every agent adapter also recomputes the token and finds the record in its store without itapproval, token (collide with application context)
APPROVAL_META_KEY'dev.permdock/approval', the MCP request _meta key permdock/mcp reads a resume token fromA token in tool arguments (model-controlled)
approval: 'human' or { by, distinct, staleOn, quorum, ttl, escalation }Grant option that yields approval-required; by is a grantee selector; distinct (default true) refuses the principal as approver, and distinct: false is the explicit opt-out doctor PD024 reports; staleOn: 'resource-change' binds the approval to the row's version; quorum (default 1) is the number of distinct approvers; ttl is a duration that caps how long the request stays open; escalation: { after, to } widens the approvers after a durationA fluent approver builder; allowSelf; approvers on the grant (that is the request's copy); threshold, minApprovals
holder, anyOf, allOf, PermissionApprover, AnyOfApprover, approverPermissions, permdockFor, verdict.permissionsApprovers who hold a permission in the request tenant (custom roles included), an any-of group, an explicit all-of list, and the verdict facts that holder() is checked throughhasPermission(), oneOf, or()
mode, stages, user, UserApprover, ApproverThe approval mode ('any', 'all', 'sequential'), the ordered approver sets it takes, the approver for one named person, and the union of approver kindsA workflow engine; steps, chain, levels, person
approverRelations, approverRelationKey, relations (verdict)The handler step that reads which relation approvers an approver satisfies, the key each one is stored under, and the verdict field that carries those keys to the storeFacts from a request body
ApprovalEscalation, ApprovalSignature, approvalsThe normalised escalation on a grant, one recorded approval { by, at }, and the request field that lists them oldest firstsignatures, votes; a count without who gave it
applyApprovalVerdict, approvalQuorum, escalationOpenAtThe helpers a custom ApprovalStore uses in resolve: the next request after a verdict (quorum and escalation applied), the approvers a request needs, and the instant its escalation opensA store re-implementing the quorum rules
vouchApproval, vouchedA verdict the application's own approval rules decided, recorded with the rule's name: it resolves the request at once without the approvers eligibility checks and keeps the separation checksA verdict from a request body; a flag that also skips the actor and requester checks
approver-repeatedThe ApprovalError code for a principal who already approved the request; approvalsHandler maps it to 409duplicate-approval; counting the second approval
versionThe resource() option naming the row field (updatedAt, a revision) that a staleOn: 'resource-change' approval binds to; also on catalog resourcesA schema version; the snapshot or catalog format major
stale-approvalThe denial reason for a resume token that approved an earlier version of the rowapproval with a detail
exclusiveWithRole option listing roles that must not be held together; doctor PD018, separationConflicts and decideRoleChange (conflicting-role)An evaluation deny
DecisionSink, memorySinkThe pluggable destination for on('decision') events and its in-process default; memorySink({ signer }) signs each writeBlocking a decision; sinks are fire-and-forget
signDecisionBatchSign a batch of sink events as a permdock-decisions+jwt JWSFeeding a signed batch back into decide; evidence only
Capability, CapabilityInput, CapabilityRedeemer, parseCapabilityThe v1 share-link object { v, id, holder, on, roles, permissions?, redeemer?, once?, expiresAt }, the signCapability input (permission and resource references, not keys), who may redeem it ('anyone', 'signed-in', { user }, { scope, id }), and the strict own-property parser (link capabilities)ShareLink, LinkToken, audience (that is the JWT aud)
signCapability, capabilitySubjectSign a capability as a permdock-capability+jwt; build the link subject a verified capability acts asIssuing without a guard; a link that holds a scope or global role
subjectFromCiOidc, CiOidcSubjectOptions, CiOidcPrincipal, CiOidcProviderpermdock/jwt: verify a CI job's OIDC token (provider: 'github' | 'gitlab' | 'buildkite', required audience, optional issuer and schema) and return a workload principal with repository, ref and environment; on('auth') source ci-oidcA user principal from a CI token; a long-lived CI secret
subjectFromCapability, CapabilitySubjectOptions, CapabilityFailureCausepermdock/jwt: verify a capability and return the link subject, never throwing; options issuer, audience, linkPolicy, revoked, replay, viewer; causes redeemer-mismatch, link-policy, capability-revoked, capability-replayed next to the token causesAccepting a capability in subjectFromJwt; a capability as an access token
LinkPrincipal, kind: 'link', via: 'link'The principal a capability resolves to (its id is the link id, with the verified capability attached) and the via of its one resource membershipA user id; a principal a model or request body can name
LinkPolicy, LinkPolicyViolation, linkPolicyViolation, linkPolicyA scope instance's rules for links on its resources (maxLifetime, redeemers, once), the broken rule (lifetime, redeemer, once), the check, and the option on subjectFromCapability and signCapability that applies themA rule that widens; a policy read from the token
holder'link', or the reserved 'key' for a future stateless key the resolver refuses; API keys are opaque pdk_ keys, not capabilitiesA third holder kind
Credential, CredentialKind, CredentialPermission, parseCredentialThe v1 record an API key stands for { v, id, kind, principal, tenant?, roles?, permissions, createdBy, createdAt, expiresAt?, name? }, 'user' or 'service', one { permission, ids? } entry, and the strict own-property parser (API keys)ApiKey for the record; the secret or its hash in the record
CredentialPrincipal, via: 'credential'A principal resolved from a key, with the verified credential attached; the via of a service key's one tenant membershipA principal a request body can name
credentialSubject, credentialDelegationThe subject a verified credential acts as (a user key's owner narrowed by delegation, a service key's service principal) and the delegation aloneCopying the owner's rights into the key
decideCredential, CredentialRequest, CredentialPermissionInput, CredentialDecision, DecideCredentialOptionsDecide whether the subject may create a key: granted with the Credential to store, approval-required with a bound token (resume with approved), or denied; options settings, approved, nowStoring the key; a key that mints keys
CredentialPolicy, CredentialPolicyViolation, credentialPolicyViolationA tenant's API-key rules (maxTtl, kinds, approval, allowNoExpiry), the broken rule (kind, no-expiry, ttl) and the checkA rule that widens
SettingsSource, TenantSettings, memorySettings, settingsPer-tenant settings (settingsFor(tenant) returns { credentials? }), its in-process default and the option on decideCredential and subjectFromApiKeySettings that grant; a module-level settings object
CredentialVerifier, apiKeyVerifier, memoryCredentials, verifier{ verify(key) → Credential | null }, the verifier over an application table (find(id) returns { credential, hash }), the optional touch(id, at) for lastUsedAt, the in-process store (issue, rotate, revoke, list, lastUsedAt) and the option naming a verifierVerifying keys in core; scanning every row
subjectFromApiKey, ApiKeySubjectOptions, ApiKeyFailureCausepermdock/server: a SubjectResolver for pdk_ keys, never throwing; options verifier, permissions, owner, revoked, settings, sink, sample; causes malformed, unknown-credential, invalid-claims, expired, credential-policy, credential-revoked, owner-unavailableAn API key in subjectFromJwt; a key as an actor
generateApiKey, hashApiKey, parseApiKey, StoredCredentialA fresh pdk_<id>_<secret><checksum>, its base64url SHA-256, the { id, secret } split, and the { credential, hash } row a verifier readsStoring the key itself
CredentialEvent, credentialEvent, dev.permdock.credentialA credential sink event (created, used, rotated, revoked; used carries sample), its builder and CloudEvents typeA per-operation CloudEvents type
exchangeCapability, ExchangeCapabilityOptionspermdock/supabase: a verified link subject in, a short-lived role: 'anon' Supabase access token with a capability claim outservice_role; a sub claim; a security-definer RPC per resource
rls.anonymousSignIns'deny' keeps a Supabase token with is_anonymous: true out of every RLS branch but the anyone() grants'A boolean; a separate role for anonymous sign-ins
rls.anonExecutetrue grants anon usage on the helper schema and execute on the helpers, for hand-written policies that apply to public or anonA grant to service_role; a way to let anon read rows
rls.tenants'active' (default) narrows the generated helpers and memberOf checks to the tenant claim when the token carries one; 'all' admits every tenant the subject is a member of, for apps whose tenant comes from the URLA boolean such as narrowTenant; an organisation-slug header
permdock_trusted_replace_custom_role_grants, permdock_trusted_rename_custom_role_grants, permdock_trusted_delete_custom_role_grantsThe generated custom-role writes for a trusted caller (a migration, a job, a backend role granted execute): the definition checks without the caller checks; no client role may execute themservice_role grants in generated SQL; an admin or bypass flag on the member functions
permdock_replace_global_rolesThe generated rls.roles write that sets one user's global roles in one call, as the invoker so the assignment trigger still judges each row; execute for authenticated onlyA security definer variant that skips the ceiling; replace_roles without the scope word
rls.customRoleWrites.requires, manage-rolesThe permissions (or 'manageRoles') a caller must hold before the generated custom-role writes run the hand-out check, and the hint when it holds noneA role name list; a check that only runs on save
rls.customRoleWrites.roles, permdock_cascade_custom_roleThe application's table of custom roles (table, key, tenant, scope, id, skip) and the generated trigger function that carries a role's grants through renames, moves and deletes of its rowA second store of custom role names; a trigger that writes without the write functions' checks for a signed-in caller
rls.customRoles.from, --backfill-out, -- permdock:backfill v1The application's existing custom-role tables (permissions, includes) and the flag that writes the idempotent data migration copying them through the trusted replace function, with its marker lineimport, adopt or sync for a one-off copy; a copy that overwrites a role edited since
rls.triggers, rls.auditTriggers the application owns on generated tables (name, when, events, level, function, args), and one call per generated table to an audit module that registers it by regclassA permdock_ trigger name for an app trigger; a hand-written migration that has to run after the generated file
rls.migrate.tables, refresh, --retire-out, -- permdock:retire v1The materialised permission tables rls migrate retires, the functions that refresh one, the flag that writes the drop migration once nothing reads them, and its marker linecascade; drop, remove or cleanup for the flag
rls.assignments, rls.assignments.ownRole, permdock_assignment_<table>, permdock_can_assign_custom_role, permdock_can_assign_custom_role_forAssignment triggers on the membership tables, the global-roles table and listed tables such as invitations: a client role may write only the roles it may assign, by assigns or, for a custom role, by what it may hand out, and with ownRole: 'refuse' none on its own rows; other writers are trusted, and the _for form re-checks a stored userA setting a client could flip to bypass the check; a trigger that trusts the role claim
rls.ownershipTriggersfalse, or { <scope>: false }, leaves out the holder-count and transfer-only triggers of min, max and transferOnlyA way to turn the rules off in decideRoleChange
permdock_can_assign_any, permdock_can_assign_any_forOne assignment check for a declared or a custom role, (p_role, p_tenant, p_scope, p_scope_id), written when a role declares assigns; p_scope 'global' assigns a platform role, and the _for form takes the stored user firstA caller-side branch on whether a role is declared; a check that trusts the role claim
rls.readOnlyActorsRestrictive policies, {table}_{op}_read_only_actors, that refuse writes from a token whose act.kind is a listed actor kind (true: support and impersonation) unless act.read_only is falseA permissive policy that widens access; a session flag a client could set
rls.realtime.topics, permdock_realtime_<pattern>_<op>Private Realtime channel policies on realtime.messages: a :-separated topic pattern with one {<scope>} segment, read (select) and write (insert) permission referencesA policy keyed on the row's topic column; a public channel
rls.storage.buckets, permdock_storage_<bucket>_<op>Storage policies on storage.objects for one bucket: scope, folder (1-based, default 1), read, write (insert and update) and delete permission referencesOwner-only policies on owner_id; a bucket-wide grant
rls.apiKeys, permdock_api_key_allows(p_grant)An API key passed in a claim (api_key by default) caps every allow at its scopes (permission keys), and a key with a tenant and no subject is a service principal holding roles (or serviceRoles) in that tenantA wildcard scope; a key that widens its owner's rights; a key set by a client
permdock_user_id()The Supabase helper every generated helper, policy, field view and trigger reads the subject through: what auth.uid() returns, and null for an empty subauth.uid() in a policy a token with an empty sub reaches; a user id the caller passes
permdock_session_live()The Supabase helper a { subject: { session: { live: true } } } condition compiles to: the token's session_id is in auth.sessions for the caller and not past not_afterauth.uid() alone, which a revoked session's token still passes
permdock_capability_ids, --capabilities, rls.capabilitiesThe RLS helper that reads the capability claim, and the flag and config key that emit it with one anon policy per resource-scoped grantA policy that trusts a capability the server did not exchange
store, sinkOptions for an ApprovalStore and a DecisionSink; a SnapshotSource is the source option of permdock/react-nativeAnything else; store is never a database handle
policiesThe option every adapter's createPermDock accepts for a PolicySource; read once per instanceA module-level policy the source swaps; a per-check fetch
hostabledefinePolicy option listing the permission subtrees or leaves hosted grants may touch; default nonehosted, editable, remote; a grant option
PolicySource, memoryPolicySource, testPolicySourceThe channel for hosted grants (current(), refresh()), its in-process default and its permdock/testing runnerA decision path; current() is synchronous and never fetches
PolicyDocument, HostedGrant, parsePolicyDocument, mergeHostedGrantsThe v: 1 hosted-grant document, one grant in it, its reader (rejects unknown majors and forbidden keys) and the pure merge createPermDock runsPolicyDocumentV2; majors live in v
HostedGrantRef, matched.hosted{ document, grant } on a grant and a Decision a hosted grant matched: the document fingerprint and the grant idA role name; hosted grants still name declared roles
hosted-grant-dropped, HostedGrantDroppedThe on('error') value for a hosted grant that failed a merge rule; reason is not-hostable, unknown-permission, unknown-grantee, non-portable, weaker-approval or invalidA denial reason; a dropped grant never reaches Decision
CLOUD_EVENT_TYPES, CloudEventType, CatalogEventDataThe closed CloudEvents type list (dev.permdock.decision, .approval, .directory, .membership, .catalog) and the catalog payload { kind, fingerprint, previous?, findings? }A new type without a wire-format change
CatalogFinding, CatalogFindingCodeOne drift finding { code, permission, grant? }; code is permission-removed, not-hostable, grantee-removed or approval-tightenedFree-text finding lines
coveredByDelegationpermdock: the delegation coverage check (scopes, RAR authorizationDetails, GNAP access) decide uses; returns undefined, no-delegation or not-delegatedisDelegated, checkScopes, a boolean that hides the reason
delegations, DelegationInput, PolicyDelegation, DelegationTargetThe definePolicy option for standing delegations { from, to, permissions, validFrom?, validUntil? }, its input type, the normalised entry { from, to: { kind, id? }, permissions: string[], validity? } on policy.delegations, and the actor it is foragents, grantsToActors; a grant with an actor grantee (that gives the actor access of its own)
delegatedPermissions, delegatedpermdock: the ceiling of permission keys the policy's delegations give the subject's actor right now (undefined when none applies), and the snapshot field carrying it sorteddelegationScopes (these are permission keys, not OAuth scopes); a field on the subject
delegation-removed, delegation-narrowedpermdock diff breaking kinds for a policy delegation that disappeared or lost permission keys or validitydelegation-changed for a narrowing
catalogFingerprintpermdock: the catalog v1 fingerprint (base64url SHA-256 of canonical JSON without generatedAt, generator, fingerprint and usages) the CLI writes and the Cloud recomputesA hash of the pretty-printed file; a CLI-only helper
WireDenial, WireDecisionpermdock: a denial as it leaves the process ({ role, reason, to?, detail? }, detail a JSON value, never a closure cause or validation error) in decision events, Problem Details, MCP and WebMCP refusals and the decision endpoint; WireDecision is a Decision with those denialsSerializedDenial, PublicDenial; sending Denial as is
toCsvRow, CSV_COLUMNSpermdock: one RFC 4180 CSV row for a decision event and the pinned header (time, principal, actor, tenant, permission, outcome, matched.role, via, denials.reason, token)A configurable column list; a Cloud-only exporter
toOcsf, OCSF_VERSION, OcsfAuthorizeSessionpermdock: the pure projection of a decision event onto OCSF Authorize Session, pinned to one OCSF versionA second, Cloud-only projection
verifyWebhook, parseCloudEvent, PermDockCloudEventpermdock/cloud: verify a signed Cloud webhook delivery (never throws, no unsigned mode) and validate one CloudEventA shared-secret or unsigned webhook mode
PermDockEnvpermdock/hono: the Env type the permdock() and protect() middleware declare, so c.var.permdock and c.var.permdockData are typedA declare module augmentation of ContextVariableMap
memberships, customRoles, tenantThe three tenancy options every adapter's createPermDock accepts: a MembershipSource, a RoleSource, and how the active tenant is resolved from the requestReading a tenant from an unsigned header or a model argument
cloudcloud({ url, key, environment, verifier }) from permdock/cloud, returning approvals, sink, snapshots, policies, the environment URL as issuer and its jwks URLA factory for a PermDock; there is no decide on it; an app audience option
cloudEndpointscloudEndpoints({ url?, environment? }) from permdock/cloud: the environment URL (issuer) and JWK Set URL (jwks) with cloud()'s fallbacks, for building a verifier before cloud()A network call; discovery
scimHandlerThe RFC 7644 Fetch handler from permdock/scim writing into a DirectoryStore (SCIM adapter)A PermDock; it decides nothing and reads no tenant from a body
DirectoryStore, memoryDirectoryStoreThe repository interface SCIM provisioning writes to (users, groups, groupsFor), every method tenant-first, and its in-process defaultA MembershipSource; the store is the write side, the source below is the read side
directoryMembershipSourcedirectoryMembershipSource(store): the MembershipSource that turns synced groups into tenant memberships { tenant, roles, via: 'group:<id>' } and yields nothing for an inactive userReading display names; group ids only
ReplayStore, memoryReplayStoreThe pluggable jti replay store for permdock/ssf and its in-process defaultA decision input; SETs and logout_tokens never reach decide
connection, Connection, ConnectionOptionsThe kernel and HTTP adapter method that opens a long-lived connection for a stream or socket, and its frozen result (permdock, signal, check, filter, close)A mutable instance; stream, socket or subscribe as the kernel method name
RevocationFeed, RevocationEvent, memoryRevocationFeedThe interface that tells open connections a subject changed (subscribe, revoke; events session-revoked and changed) and its in-process defaultA decision input; a feed ends or revalidates, never grants
revocationsThe option carrying a RevocationFeed on the kernel, the HTTP adapters, permdock/ssf and permdock/scimrevocationFeed, feed
PermDockRevokedError, RevokedCodeThe reason of an aborted connection signal; code is session-revoked, expired, denied or subject-changedA denial reason; a check after the abort is no-grant with detail: 'connection-revoked'
socket, sse, SseOptionspermdock/hono helpers: socket(conn, events) wraps upgradeWebSocket events, sse(conn, stream, source, options) writes an async iterable to streamSSEA with* or Dock name; helpers for other adapters
ElysiaSocket, NestSocketThe structural socket types connection accepts in permdock/elysia and permdock/nestAn import of the framework's socket class
ApprovalListQuery, ApprovalPageThe ApprovalStore.list argument (ApprovalListFilter plus limit and cursor) and its result (items, next)An offset, or a page number
ApprovalHint{ at?, hint? }: the approval option on the server kernel, HTTP adapters and permdock/terminal, emitted as the approval member of approval-required Problem DetailsA terminal-only field, a URL built from the token by PermDock
KeyringEntry, storage.keyringThe Entry shape from @napi-rs/keyring and the permdock/terminal storage option that takes itA bundled keychain binding, keytar
remotePdppermdock/pdp helper that implements DecisionProvider over an AuthZEN evaluation URLA local decide; unknown or unreachable remote answers deny
openfga, spicedb, RelationMappermdock/pdp presets that implement DecisionProvider over OpenFGA check / list-objects and the SpiceDB HTTP gateway CheckPermission / LookupResources, and their one-callback-per-permission tuple mapA tuple store, a model DSL or a per-vendor entry
DecisionProvider.permittedThe optional listing member: the ids of one resource type a subject may act on, used by the PDP instance's filter and whereA grant; null denies every row
PermDock-ApprovalThe HTTP request header carrying an approval token on a retried requestAnything else on the wire
membershipEventPure builder for a membership sink eventA Decision; it is audit only

Strings appear only as .key and .scope on the wire, in audit events and in catalogs. The public API takes references.

The subjectFrom<Provider> pattern names the source of the material (a JWT, a Supabase claims object, a Clerk auth object, a Better Auth session, MCP authInfo), always returns a Subject (anonymous when the material cannot be trusted) and never throws, so every provider reads the same way at the call site: createPermDock(policy, subjectFromClerk(await auth())). See Authentication and PermDock.

Reserved words

These never appear as public identifiers, for the reasons given:

WordWhy it is reserved
dockIt reads oddly next to PermDock; only permdock is used for the instance.
abilityCASL's noun; using it would imply CASL semantics (subject detection, manage/all) that PermDock does not have.
can as a definerCASL uses can both to define and to check rules, which its own cookbook documents as confusing (less-confusing-can-api). PermDock defines with allow / deny and checks with can.
$-prefixed membersKilpi prefixes every instance member with $ to avoid clashing with policy names in a Proxy tree. PermDock keeps methods on the instance and the tree separate, so no prefix is needed.
Can, Guard, AccessComponent names used by CASL and Kilpi; the PermDock gate is <Protected>. The only other component is <PermissionBoundary> in permdock/next/client, permdock/vue, permdock/svelte and permdock/solid: an error boundary for thrown PermDock errors, never a gate.
org, organization, workspace, account, group as PermDock identifiersEvery provider picks a different noun for the same thing. PermDock uses tenant and team in its own API and lets the subjectFrom* mapper translate (Clerk organization to tenant, SCIM group to team).
useOrganization, useTeam, TenantProviderThe tenant is a property of the one snapshot, not a second provider; useTenant and useMemberships read it.
hasRole, isAdmin, roleGuardRole checks in application code bypass the permission model; check a permission, and read roles only for display (useRoles) or assignment (useAssignableRoles, useAssignablePermissions).
whoReserved for a directory-backed "who may X this row" verb; not exported.
SnapshotV2The format major lives in v; the type is Snapshot.
scope(...) as a derived instanceDerived instances are tenant(id) and team(id), one method per scope kind.
permdock.with(...)with* names the Supabase middleware pipeline steps, withSubject and withOtel; an instance with other sources is permdock.derive({ customRoles, approvalPolicies, relations }).
@permdock/cli, @permdock/testing, any @permdock/* packagepermdock is the only published name, owned by the scaledockhq npm organisation. The CLI is its bin plus permdock/cli, permdock/unplugin and permdock/next/plugin; the runners are permdock/testing. One package keeps core, adapters, CLI and runners on the same version.
permdock/cloud-authThere is no such entry: PermDock Cloud identity is subjectFromJwt({ discovery }) against the Cloud issuer.

What each adapter's createPermDock returns

Import pathReturnsNotes
permdockPermDockawait createPermDock(policy, user, { tenant?, memberships?, customRoles?, actor?, delegation? }); sync when the policy declares no context and no async source
permdock/next{ getPermDock, getPermission, getSnapshot, requireAccess, PermDockProvider, permdockHandler }, plus the standalone cacheLifeFor(snapshot, { min, max, now }), snapshotTag and snapshotHeaders shared with permdock/serverOptions: subject, tenant, memberships, customRoles, store, sink, otel, wrap; never a cache directive (getSnapshot sets cacheLife and cacheTag inside the app's 'use cache: private' function, and its tags add to snapshotTag); requireAccess, not requirePermission or authorize, because it maps a decision to the forbidden() / unauthorized() interrupts
permdock/next/clientPermissionBoundary (props denied, approval: a node or a PermissionBoundaryFallback function of the state) and usePermissionBoundary() returning { outcome, permission, token?, retry }A 'use client' entry built on catchError from next/error; the boundary never decides, it renders a fallback for an error a check already threw
permdock/server{ permdock, getSnapshot, protect, problem, openapi, permdockHandler }, plus the standalone problemFromError(error), snapshotHeaders(snapshot, { tags }), cacheLifeFor and snapshotTagThe Fetch kernel every HTTP adapter wraps. Options: subject, tenant, memberships, customRoles, store, sink, limits, pdp, otel, webBotAuth, actor, and InstanceOptions (approvalPolicies, relations, entitlements, policies)
permdock/hono{ permdock, protect, permdockHandler }permdock() is middleware; protect(permission, load?, options?) guards a route
permdock/express, permdock/fastify, permdock/elysia, permdock/node{ permdock, protect, permdockHandler }, plus errorHandler and withPermDock(fn) (Express) and send (Node)Same shape as Hono over the shared kernel
permdock/nest{ PermDockModule, PermDockGuard, Protect, InjectPermDock, permdockHandler }, plus decorateMethodPermDockModule.forRoot({ guard: 'global' }) registers PermDockGuard as APP_GUARD; permdockHandler({ path }) sets the controller path
permdock/terminal{ permdock, protect, filterCommands, format, exitCode }; EX_OK, EX_USAGE, EX_TEMPFAIL, EX_NOPERM, EX_CONFIG; options yes and dryRun (--yes / -y, --dry-run), interactive: { confirm, typed }For your own CLI; protect wraps a command action, filterCommands hides or annotates denied commands. Not the permdock binary or permdock/cli
permdock/trpc{ permdock, protect, permdockHandler }protect is procedure middleware; denials throw TRPCError
permdock/orpc{ permdock, protect, permdockHandler }, permissionOfprotect is procedure middleware; denials throw ORPCError through the contract's declared constructor when it has one
permdock/mcp{ protectServer }protectServer(server).registerTool(name, { permission, data?, longRunning?, ... }, handler), plus registerResource and registerPrompt; protectServer(server, { enforce: 'procedure', permissionFor }) lists by permission and leaves the decision to the procedure; options: subject, requireAuthInfo, resource (RFC 8707), store, approval ({ at, hint }), stepUp ({ at }), requestState (an SDK createRequestStateCodec); also exports subjectFromMcp, APPROVAL_META_KEY, McpPrincipal
permdock/ai-sdk{ toolApproval, capabilityMiddleware, needsApproval }Options: subject, actor, tools
permdock/claude-agent{ canUseTool, permissionRequestHook, permdock }Options: subject, tools, store, mcpSources (trusted MCP server sources, default ['sdk'])
permdock/eve{ approval, approvalFor, permdock }approval is Eve's { request, response } pair for defineTool (EveApprovalPair, contexts EveApprovalContext and EveResponseContext, principals EvePrincipal); options: tools, store, approvers (EveApprovers); also exports subjectFromSession, actorFromSession, rolesOf
permdock/openai{ needsApproval, guardTools, resolveInterruptions, permdock }Options: subject, actor, tools, store
permdock/authzen{ permdockHandler }Serves evaluation, evaluations, search and .well-known
permdock/ssf{ receiver }RFC 8935 push and RFC 8936 poll receiver for CAEP events; receiver.logout for OIDC Back-Channel Logout
permdock/pdp{ createPermDock } wrapping a local policy plus optional remotePdpOpt-in AuthZEN client; fail-closed on unknown remote decisions
permdock/convex{ withPermDock, snapshotQuery }Convex query/mutation wrappers; no Convex SDK peer (structurally typed); options subject, tenant, actor
permdock/scimno createPermDock; exports scimHandler, memoryDirectoryStore, directoryMembershipSource and the DirectoryStore typeThe handler is a Fetch handler you mount; the source is a value you pass as memberships to a factory
permdock/a2a{ agentCard, extendedAgentCard, protectSkill, sign }Options: skills
permdock/otelno factory; instrument / withOtelSubscribes to on('decision'); optional @opentelemetry/api

These entries deliberately do not export createPermDock:

  • permdock/react and permdock/react-native export PermDockProvider, usePermDock, usePermission, usePermissions, useFilter, useTenant, useMemberships, useRoles, useAssignableRoles, useAssignablePermissions, useApproval, useSubject, useDescribe and <Protected> directly. permdock/react-native adds subjectId (required, null when signed out), the storage wrappers memoryStorage, secureStoreStorage and mmkvStorage, the subscriptions appStateForeground and netInfoOnline, the Expo Router guards usePermissionGuard and useSnapshotReady, and the snapshot sources localSnapshot and powersyncSource. permdock/vue exports permdockPlugin plus the same composable names and <Protected>. permdock/svelte and permdock/solid keep the same surface under each framework's idiom.
  • permdock/better-auth, permdock/clerk and permdock/supabase export their subjectFrom* mapper and, where the provider stores roles, a <provider>RoleSource; both are values you pass to a factory.
  • permdock/webmcp exports registerTools(document.modelContext, group, { permdock }) because it consumes a client PermDock rather than creating one. permdock/drizzle, permdock/prisma and permdock/kysely export toWhere. permdock/prisma also exports permdockExtension, resolveRelated, toPredicate and prismaModelFields. All three also export withSubject and checkRow. permdock/jwt exports subjectFromJwt, createJwtSubjectResolver, verifyDpopProof, joseTokenVerifier and joseTokenSigner because it produces subjects, verifiers and signers for a factory, not an instance. Structurally typed providers (permdock/supabase, permdock/clerk, permdock/better-auth, permdock/convex, permdock/prisma, permdock/mcp, permdock/react-native) declare no peer dependency: they duck-type the SDK you already installed. permdock/supabase/middleware is the exception on the Supabase side: it calls defineMiddleware, so @supabase/middleware is its optional peer, while @supabase/server's JWTClaims is still duck-typed.
  • permdock/approvals exports memoryApprovalStore and approvalsHandler, and permdock/cloud exports cloud; both produce values you pass to a factory (store, sink, policies) or a client provider (source) rather than a PermDock.
  • permdock/testing, permdock/testing/saas and permdock/testing/saas/permissions export the test runners (describePolicy, the test<Interface> runners, rlsParity, ormParity, testClientParity, testHttpAdapter, testAuthZen) and the shared SaaS domain. Test files import them; application entries never do, and Vitest is an optional peer.
  • permdock/cli exports defineConfig (for permdock.config.ts) and run (the permdock binary as a function) plus the config types, and parseHookMarker / parseGrantsMarker with their result types SupabaseHookMarker / SupabaseGrantsMarker. It is a build-time entry: no runtime entry imports it.
  • Build hooks: permdock/next/plugin exports createPermDockPlugin and permdock/unplugin exports createPermDockUnplugin (Vite, Rollup, webpack, Rspack, esbuild through unplugin). Both run collect at build time and never wire runtime API. The create prefix and the PermDock noun follow the factory rule; the suffix names the host (Plugin for Next's config API, Unplugin for the bundler family).

Names shared on purpose

  • PermDock methods and snapshot fields share a name when the field is the method's result serialized: permdock.actions(resource) and snapshot.actions, permdock.audiences() and snapshot.audiences, permdock.snapshot() and a snapshot option or prop, permdock.tenant(id) and principal.tenant. The method is on the instance; the field is the wire form a client hydrates from.
  • One name can be exported by several entries with that entry's meaning, like createPermDock. PermDockPluginOptions is the build plugin's options in permdock/next/plugin and permdock/unplugin, and the Vue app plugin's options in permdock/vue.
  • Every adapter returns <Adapter>PermDock from createPermDock and takes <Adapter>PermDockOptions, rather than <Prefix><Thing>, so the adapter is the first word wherever both appear (recorded in the repository's docs/decisions/0019-adapter-suffix-and-no-dollar-members.md).

Spec names

PermDock's subject vocabulary is TypeScript, camelCase and typed; the specifications it consumes are JSON claims with short registered names. subject fixes the rule: where a spec name exists, PermDock either uses it verbatim or maps to it exactly once, in the table below and on subject; PermDock never coins a second spelling for a concept a specification already names.

Specification nameSourcePermDock nameRule
subRFC 7519, OIDC Coreprincipal.idOpaque string, compared byte for byte
issRFC 7519, OIDC Coreprincipal.issuerAlways set by subjectFrom*; identity is issuer + id
audRFC 7519audience optionVerified, not stored
expRFC 7519subject.expiresAt, Membership.expiresAt, ApprovalRequest.expiresAt, snapshot expiresAtNumericDate (Unix seconds) everywhere, the same unit as exp; the JWS envelope's exp copies it
iat, nbf, jtiRFC 7519none on the subject; iat, jti on signed outputsVerified, then dropped
actRFC 8693actorInnermost act.sub is actor.id; the nesting is delegation.chain
client_id, azpRFC 9068, OIDC Coreactor.id when the client acts under a delegation; verified otherwiseactor.kind 'oauth-client' or 'mcp-client'
Signature-Agent keyidWeb Bot Auth / RFC 9421actor.id when webBotAuth is setactor.kind 'web-bot-auth'
scopeRFC 6749, RFC 9068delegation.scopes; permission.scopeSpace-separated string on the wire, array in TypeScript
authorization_detailsRFC 9396delegation.authorizationDetails; permission.authorizationDetailsOne RAR type per permission
accessRFC 9635, RFC 9767delegation.accessObjects and reference strings kept verbatim
cnf (jkt, x5t#S256, jwk, kid)RFC 7800, RFC 9449, RFC 8705binding with the same member names (reshaped)FAPI's "sender-constrained" gives the field its name; the members are the cnf members so it round-trips
acr, amr, auth_timeOIDC Core, RFC 8176principal.assurance.acr, .amr, .authTimeProvider aal maps into acr; RFC 8176 amr values include pwd, otp, swk, hwk, mfa
verified_claims, verification, trust_framework, claimsOpenID Connect for Identity Assurance 1.0principal.assurance.verified[] with verification and claims kept under the spec's namesThe collection is verified because assurance already scopes it; the fields inside keep the spec's snake case
aal (aal1, aal2)Supabase Auth JWTprincipal.assurance.acrVerified by getClaims(); aal2 is MFA
fva / factorVerificationAgeClerk session tokenRecipe into principal.assurance; not mapped by subjectFromClerkSecond element >= 0 means a second factor was verified
twoFactorEnabledBetter Auth user rowRecipe into principal.assurance; not mapped by subjectFromBetterAuthA session exists only after the two-factor plugin verifies
sidOIDC Front- and Back-Channel Logoutsubject.sessionThe join key for logout_token and CAEP events
roles, groups, entitlementsRFC 9068principal.roles; principal.memberships (team from groups); principal.plans from entitlementsSCIM value only
userName, externalId, active, displayName (SCIM User)RFC 7643DirectoryUser.userName, .externalId, .active, .displayName verbatimexternalId is the IdP's identifier and id is the store's; active: false yields no memberships; displayName is never an identifier
members[].value, displayName (SCIM Group)RFC 7643DirectoryGroup.members[].value verbatim; via: 'group:<id>' on the membershipGroup id is the group identifier; display and displayName are labels only
urn:permdock:scim:schemas:extension:roles:1.0 (roles)RFC 7643 section 3.3 extensionThe Group extension carrying the declared role names for a group; read by directoryMembershipSourceUnknown or non-assignable role names are dropped, never invented
type, source, subject (CloudEvents)CloudEvents 1.0dev.permdock.decision, dev.permdock.approval, dev.permdock.directory, dev.permdock.membership, dev.permdock.catalog, dev.permdock.access.started / .ended / .revoked; the emitting service; the permission key, resource id or principal idThe envelope for every webhook and queue sink (wire formats)
jwks_uri, issuer (Discovery)OIDC Discovery, RFC 8414discovery option; jwks and issuer optionsDiscovery supplies both
typ (at+jwt, JWT, logout+jwt, secevent+jwt)RFC 9068, RFC 7519, OIDC, RFC 8417accept option; permdock-snapshot+jwt, permdock-approval+jwt, permdock-decisions+jwt, permdock-policy+jwt, permdock-capability+jwt on outputsExplicit typing, RFC 8725 section 3.11
invalid_token, insufficient_scope, insufficient_user_authenticationRFC 6750, RFC 9470on('auth') reason invalid-token; Decision reasons not-delegated / no-delegation and insufficient-user-authenticationHyphenated in PermDock, underscored on the wire; the HTTP adapter renders without a second table
Subject, Resource, Action, ContextAuthZEN 1.0principal (+ actor, delegation, tenant under context), resource, action, contextThe evaluation mapping on AuthZEN

Files and folders

  • The server factory lives in src/permdock/server.ts in every example. The definition is src/permissions.ts; the policy is src/policy.ts; generated definitions are src/permissions.generated.ts.
  • Example apps are apps/examples/<adapter>; the docs page is /docs/adapters/<adapter>; the source folder is packages/permdock/src/<adapter>. The same short name is used in all three.
  • Errors are PermDockDeniedError, PermDockApprovalRequiredError and PermDockValidationError. Problem Details type URIs end in /denied, /approval-required and /validation. The first two carry digest (React's name for the property that survives the Server Component boundary): PERMDOCK_DENIED;<permission> and PERMDOCK_APPROVAL_REQUIRED;<permission>;<token>, read with parsePermDockDigest.

Renamed before 0.1.0

OldNew
CreatePermDockOptionsPermDockOptions
CreatePermDockPluginOptionsPermDockPluginOptions
ServerPermDock.handler, AuthzenPermDock.handlerpermdockHandler, like every other HTTP adapter
ExpressPermDock.handler(fn)withPermDock(fn), like permdock/convex
SsfAdapter, SsfOptionsSsfPermDock, SsfPermDockOptions
SupabaseMiddlewareOptionsSupabaseMiddlewarePermDockOptions
A2AAgentCard, A2APermDock and the other A2A* typesA2aAgentCard, A2aPermDock, … (A2a like Ssf and Mcp)
dock, pd, server, factory for the instance in examplespermdock; a cloud() result is permdockCloud

Last updated on

On this page