namespaces and imports are the two halves of that:
namespaces. Prefixes you own. You define these keys, and you publish them.imports. Prefixes you reference. Another application defines them; you name the slice you interface with, and wire it to your own resources.
src/authz/catalog.ts
zoom.host is a typed key, alfiz-verify checks call sites that use it, and a role can bundle it with your own:
Attach the published document
An import resolves against the namespace owner’s published catalog. Fetch it in CI and commit it, exactly as you already commit the output ofcatalog.toDocument():
A region still works. It is grantable, checkable and storable, but it is an approximation, and every approximation errs toward showing less. A revoke overlapping any part of a region suppresses the whole region for visibility checks.
If you must import without a document,
strict: true closes the admission half: the wildcard still declares grantable vocabulary, but can() on a key you cannot name throws rather than being evaluated.
Scope types are yours, not theirs
scopes on an import names scope types your catalog declares:
scopes: ["zoom.meeting"] is a build error even though Zoom really does declare that scope type. The reason is structural: resolving a scope’s ancestor chain requires the resolver of the application that owns the resource, and your application does not have Zoom’s. Scope types you can resolve are the only ones a check here can honestly use.
The default is [], which is grantable at the global scope only. Wire an import to a scope type when your own resources are what narrows it, as docs.folder does above.
Import the narrowest thing that works
A pattern broader than what you imported is rejected:zoom.* is a claim over a namespace you do not own, and forward-inclusive wildcards mean a stored one would absorb every future Zoom permission. Declaring the slice you interface with keeps that from happening by accident, and keeps a role editor from storing it.
Typed keys across the boundary
A concrete import contributes its keys toKeyOf<typeof catalog> as literals, exactly like your own. A wildcard import cannot: a subtree you cannot enumerate has no closed key set, so it widens the union by one template member (`zoom.meetings.${string}`). That is enough to autocomplete the prefix and not enough to catch a typo in the last segment.
Close it with codegen, the same mechanism that types a published catalog anywhere else:
zoom.meetings.reed fails to compile.
Publishing: what you announce vs. what you consume
catalog.toDocument() carries owned vocabulary only. Imported keys are never in it, because publishing them would mean defining keys in a namespace you do not own, which the registry’s namespace ownership exists to prevent.
What you consume publishes separately:
Checking a permission you never declared
A check for a permission in a namespace you neither own nor import is an implicit import. It is a missing line rather than a topology, and both the verifier and the runtime say so. In CI,alfiz-verify reports the implicit-import rule:
importSource defaults to "registry" as soon as your catalog declares any import at all. Override it in alfiz-verify.config.json:
alfiz-verify.config.json
"implicitImports": "off" to suppress the rule entirely, "implicitImportAllow": ["stripe"] to exempt one namespace, or suppress a single call site in place:
UnknownPermissionError unless you opt in:
- A permission under a namespace you own. Your catalog is enumerable, so an unknown key in it is a typo, and no configuration should hide one.
- A permission outside an enumerated import. That import knows its own keys, so it tells you which ones, with a did-you-mean.
snapshot.can() is synchronous, which makes a fetch-on-unknown design impossible there by construction.
A permission admitted this way is declared in no catalog, so a bare global
* grant does not confer it, because * means everything in the declared vocabulary. A grant naming the namespace (zoom.*, or narrower) confers it normally. Without that rule, a typo in a foreign namespace would pass for exactly the broadly-privileged users who review and test the gate, and deny everyone it was written for.Next
The catalog registry
Where a namespace is published, versioned, and tombstoned.
Federated
Cross-application roles, and what composes them.