docs: record module authorization architecture
Adds ADR 0014 recording the durable architecture Phase 5A established across both passes: the shared contracts-layer PermissionDeclaration, manifest version 3 replacing version 2's bare permission strings, the reserved core.* namespace and the structural guarantee that a module-vs-Core collision always surfaces as ModulePermissionCompositionError rather than the generic PermissionRegistryError duplicate path, deterministic registry composition, startup ordering, repository-root/package-version resolution, registry injection into apps/api's enforcement and explicit-grant HTTP API, the static-snapshot/historical-record preservation guarantees, and Doctor/modules:validate integration. Indexes it in the ADR README, cross-references it from ADR 0006 (only its manifestVersion 2 / permission-string decision is superseded; the rest of ADR 0006 remains accepted, and its historical text is left untouched) and ADR 0013 (module permission registration, deferred there, is what ADR 0014 implements), and links it from the relevant development documentation. Corrects two comments discovered during a direct source audit that overstated where a module-vs-Core permission collision is actually caught (core/src/modules/permissions.ts, apps/api/src/platform-permissions.ts) — comment-only, no behavior change, already covered by existing tests. Also corrects a documentation claim in docs/development/authorization.md that incorrectly implied removing a module blocks explicit revocation of its historical grants; grants-routes.ts's revoke handler never consults the permission registry, so revocation remains unaffected.
This commit is contained in:
@@ -45,15 +45,24 @@ export class ModulePermissionCompositionError extends Error {
|
||||
*
|
||||
* Declaration order is deterministic and meaningful for duplicate detection: Core's own
|
||||
* declarations first, then each module in the exact order it appears in `modules`, and within a
|
||||
* module its declarations in manifest-array order. Duplicate exact permission ids — including a
|
||||
* module colliding with a Core-owned id or with another module's — fail via the same
|
||||
* `PermissionRegistryError` `createPermissionRegistry` already throws for any duplicate; this
|
||||
* function does not reimplement or duplicate that detection.
|
||||
* module its declarations in manifest-array order. Duplicate exact permission ids fail via the
|
||||
* same `PermissionRegistryError` `createPermissionRegistry` already throws for any duplicate;
|
||||
* this function does not reimplement or duplicate that detection.
|
||||
*
|
||||
* `core.*` is reserved for Core's own permissions. A discovered module contributing any
|
||||
* `core.`-prefixed permission id — regardless of that module's own id, and regardless of whether
|
||||
* it happens to exactly collide with an existing declaration — fails composition with
|
||||
* `ModulePermissionCompositionError` rather than silently registering or silently colliding.
|
||||
* `ModulePermissionCompositionError` rather than silently registering or silently colliding. This
|
||||
* check runs before a declaration is ever added to the list `createPermissionRegistry` sees, so it
|
||||
* is also what actually catches a module attempting to collide with a Core-owned id specifically:
|
||||
* every Core-owned id is itself `core.`-prefixed, module ids may never contain a dot (so only a
|
||||
* module whose own id is literally `"core"` can produce a `core.`-prefixed permission at all), and
|
||||
* a genuine module-vs-module collision is structurally unreachable through real discovery — module
|
||||
* ids are unique (discovery itself rejects a duplicate) and every permission id is namespaced by
|
||||
* its own module's id. `PermissionRegistryError` therefore never fires for a real, validly
|
||||
* discovered installed-module set; `createPermissionRegistry`'s duplicate rejection remains here as
|
||||
* a defensive, unconditional guarantee regardless, and is exercised directly with deliberately
|
||||
* conflicting test data rather than through a scenario real discovery could ever produce.
|
||||
*/
|
||||
export function composePlatformPermissionRegistry(
|
||||
modules: readonly DiscoveredModule[],
|
||||
|
||||
Reference in New Issue
Block a user