catalog
Export the permission catalog as JSON, JSON Schema or Markdown, and the shape of the catalog document.
Status: planned Phase: 2
permdock catalog turns the merged permission definition into documents that people, CI and agents read without running the app. It is the read-only sibling of collect: collect scans source and writes permissions.catalog.json; catalog renders that catalog (or the runtime definition directly) in the requested format.
Usage
permdock catalog --format json > permissions.catalog.json
permdock catalog --format schema > permissions.schema.json # JSON Schema of the catalog document
permdock catalog --format markdown > docs/permissions.md
permdock catalog --from ./src/permissions.ts # render straight from the definition module, skipping collect
permdock catalog --include post --include billing.invoice # scope to a subtreeWhere the data comes from
Permission leaves are plain JSON and the definition is a runtime value (0003, 0008), so the catalog is produced by importing the definition module and walking listPermissions(permissions). Resource schemas are exported through Standard JSON Schema (~standard.jsonSchema.output(...)) when the validator supports it (Zod, Valibot and ArkType do); resources whose validator does not implement Standard JSON Schema get schema: null and a warning.
Usage sites and defining files are only present when the catalog was produced by collect, because they need the source scan.
Catalog JSON shape
{
"$schema": "https://permdock.dev/schemas/catalog-v1.json",
"version": 1,
"generatedAt": "2026-09-06T10:00:00Z",
"generator": "@permdock/cli@0.2.0",
"resources": {
"post": {
"id": "id",
"schema": { "type": "object", "properties": { "id": { "type": "string" } } },
"definedIn": "features/posts/permissions.ts"
}
},
"permissions": [
{
"key": "post.update",
"scope": "post:update",
"resource": "post",
"action": "update",
"arity": "instance",
"meta": { "title": "Update post", "description": "Edit a post you own" },
"usages": [
{ "file": "app/posts/[id]/page.tsx", "line": 42, "call": "assert" },
{ "file": "features/posts/policy.ts", "line": 12, "call": "allow" }
]
},
{
"key": "post.create",
"scope": "post:create",
"resource": "post",
"action": "create",
"arity": "collection",
"meta": {},
"usages": []
}
]
}Notes on the shape:
permissionsis a flat array sorted bykey; nesting (billing.invoice.pay) is expressed in the dotted key, andresourceholds the dotted resource path.arityisinstanceorcollection(0004).metais whatever the definition attached to the action (title,description,tags, or any JSON); it is what MCP tool descriptions and Markdown output use.schemais a JSON Schema document; thetarget(draft-2020-12,openapi-3.0) is selectable with--schema-target.- The catalog never contains grants, roles or conditions. Those belong to the policy, which is server-only, and are reported by usage and rendered by
permdock rls. - The
$schemaURL is the published JSON Schema of the catalog format, which is the same document--format schemaemits, so agents can validate a catalog offline. The exact hostname is fixed when the docs site launches.
Markdown output
--format markdown renders one section per resource with a table of actions, arity, scope and metadata, and (when present) usage counts. It is intended to be committed under docs/ or pasted into a PR so reviewers can see the permission surface change without reading TypeScript.
Consumers
- openapi reads the catalog to emit
securitySchemesscopes andx-permdock-permissions. - The MCP adapter uses
metafor tool descriptions andarityto decide whether adataloader is required. - The docs MCP server and
llms.txtexpose the Markdown output so agents can ask "which permissions exist?". @permdock/testingcompares a policy matrix against the catalog to find permissions with no test.