Skip to main content
The catalog registry is where each application’s permission catalog becomes an organizational contract. An application publishes its namespace; the registry versions it and enforces ownership, so no application can define another’s keys, and makes the published tree consumable by roles, grants, and requests across the organization. The registry stores what your code declared; it never invents vocabulary of its own.

Publishing a namespace

Publish from CI with a publisher key, a namespace-scoped credential that can call the publish endpoint and nothing else. The payload is the CatalogDocument produced by catalog.toDocument():
scripts/publish-catalog.ts
The body accepts an optional expectedVersion for compare-and-set publishes. The same publish is available from the application page in the Dashboard, and both paths call the same server function.

Versions are monotonic

Versions increase monotonically per namespace, and out-of-order publishes are rejected. A rollback deploy that republishes an older document cannot un-publish newer keys: the registry refuses the stale version, and the newer contract stands. Roles and grants referencing recently-added keys never dangle because a deploy went backwards.

Additions need no coordination

Adding a permission to your catalog requires no registry-side ceremony beyond the publish itself. Forward-inclusive wildcards absorb new keys the moment they are published, so a role granting docs.files.* covers a docs.files.archive permission added a year later, with no role edit and no re-grant.

Removals are tombstones

Removal is never deletion. A key that disappears from a published document is tombstoned: it stops matching checks immediately, but it remains visible in the registry as deprecated, with its history intact. Grant rows referencing it are not silently rewritten. The drift report closes the loop: it names every role and every grant still referencing keys no longer published by any application. You review and migrate them deliberately. The registry surfaces the debt; it does not pay it down behind your back.

Typed consumption across the boundary

A consuming application can hold typed keys for a namespace it does not own. Export the published document and run codegen against it:
The generated types make cross-namespace references, so a role bundling docs.files.* with zoom.host is compile-checked in the consuming codebase, exactly as local keys are. Commit that document and declare the slice you interface with as an import. The consuming catalog then treats those keys as its own for typing, verification, and grants, while toDocument() keeps publishing only what you actually own.

What each application consumes

The drift report above reads roles and grants. It cannot see code, so an application whose catalog still imports a tombstoned key looks clean until its next deploy. Publishing the import manifest closes that gap. It is a separate artifact from the catalog on purpose: what you announce is vocabulary others may write grants against, what you consume is a dependency only others can warn you about. publishImports writes a versioned manifest to your own Application’s storage. It posts nothing to the registry, and needs no network. Alfiz Cloud reads it back over the relay like any other provider operation. The op is optional: it needs a driver implementing putImports / getImports (the AlfizImports model in the Prisma fragment), and capabilities().imports reports whether yours has it.