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:
@@ -1,24 +1,29 @@
|
||||
# Authorization
|
||||
|
||||
Phase 4C establishes Core's authorization foundation and, in a second slice, wires it into
|
||||
`apps/api` over HTTP. This page covers both: the schema/evaluator/store/registry Core owns, the
|
||||
platform-owner bootstrap operation, and the enforcement/endpoints `apps/api` exposes on top of it.
|
||||
See [Identity](identity.md) for the identity foundation this builds on, [API](api.md) for the
|
||||
HTTP transport it is mounted into, and [ADR
|
||||
0013](../architecture/decisions/0013-core-authorization-and-owner-bootstrap.md) for the durable
|
||||
architectural decisions this page describes in more implementation detail.
|
||||
Phase 4C establishes Core's authorization foundation and wires it into `apps/api` over HTTP. Phase
|
||||
5A extends that same system so an installed module's declared permissions are enforced and
|
||||
grantable through the identical mechanism as Core's own. This page covers all of it: the
|
||||
schema/evaluator/store/registry Core owns, the platform-owner bootstrap operation, the
|
||||
enforcement/endpoints `apps/api` exposes, and how module manifests contribute to the one platform
|
||||
permission registry. See [Identity](identity.md) for the identity foundation this builds on,
|
||||
[API](api.md) for the HTTP transport it is mounted into, [Module development](../module-development/README.md)
|
||||
for the manifest shape that declares module permissions, and
|
||||
[ADR 0013](../architecture/decisions/0013-core-authorization-and-owner-bootstrap.md) /
|
||||
[ADR 0014](../architecture/decisions/0014-module-permission-declarations-and-platform-registry.md)
|
||||
for the durable architectural decisions this page describes in more implementation detail.
|
||||
|
||||
## What this is, and is not
|
||||
|
||||
After this slice, an operator can bootstrap exactly one platform owner for a deployment, a
|
||||
platform owner (or anyone they delegate to) can create, list, and revoke *explicit* authorization
|
||||
grants over HTTP, and every enforced route fails closed on infrastructure trouble instead of
|
||||
silently allowing or denying. It is **not** possible to manage roles, groups, teams, or tenants;
|
||||
there is no owner-management HTTP endpoint (revoking or re-granting platform-owner status is not
|
||||
exposed at all in this slice); there are no service accounts or API tokens, so only
|
||||
cookie-authenticated browser callers can reach the mutation routes; and relationship-derived
|
||||
grants (for example, automatic access from a roster assignment) cannot yet be created by anything
|
||||
in this repository — the generic grants API structurally cannot create or revoke them either.
|
||||
An operator can bootstrap exactly one platform owner for a deployment; a platform owner (or anyone
|
||||
they delegate to) can create, list, and revoke *explicit* authorization grants over HTTP for any
|
||||
registered permission, Core-owned or module-owned; and every enforced route fails closed on
|
||||
infrastructure trouble instead of silently allowing or denying. It is **not** possible to manage
|
||||
roles, groups, teams, or tenants; there is no owner-management HTTP endpoint (revoking or
|
||||
re-granting platform-owner status is not exposed at all); there are no service accounts or API
|
||||
tokens, so only cookie-authenticated browser callers can reach the mutation routes; a module cannot
|
||||
yet mount its own HTTP routes; and relationship-derived grants (for example, automatic access from
|
||||
a roster assignment) cannot yet be created by anything in this repository — the generic grants API
|
||||
structurally cannot create or revoke them either.
|
||||
|
||||
## Core authorization schema and store
|
||||
|
||||
@@ -132,11 +137,16 @@ registry Core's own `core.authorization.grants.manage` permission is registered
|
||||
module-owned permission and Core's own are enforced, and grantable, through the identical
|
||||
mechanism — there is no module-specific enforcement path. Removing a module from the next
|
||||
startup's discovered inventory makes its permission unknown to new authorization checks and new
|
||||
grants (a well-formed but unregistered permission denies exactly as before); it never deletes,
|
||||
revokes, or otherwise modifies a historical grant or audit row already recorded for that
|
||||
permission — a previously granted, still-unrevoked explicit grant for a now-uninstalled module's
|
||||
permission remains visible through `GET /api/v1/grants` and simply stops being something anyone
|
||||
can newly grant, revoke, or have authorized against.
|
||||
grants (a well-formed but unregistered permission denies exactly as before, including on
|
||||
`POST /api/v1/grants`, which checks the registry explicitly before calling the store); it never
|
||||
deletes, revokes, or otherwise modifies a historical grant or audit row already recorded for that
|
||||
permission. A previously granted, still-unrevoked explicit grant for a now-uninstalled module's
|
||||
permission remains visible through `GET /api/v1/grants` and simply stops being something a
|
||||
`restricted`-baseline authorization check can newly succeed against. Listing and explicit
|
||||
revocation (`POST /api/v1/grants/:grantId/revoke`) are unaffected either way: neither route
|
||||
consults the registry for a grant that already exists — revocation is keyed to the grant's own id
|
||||
and source kind, not to whether its permission is currently registered — so a manager can still
|
||||
revoke a historical grant for a now-unregistered permission.
|
||||
|
||||
Module HTTP route registration (a module owning its own mounted endpoints) and relationship-derived
|
||||
grant creation both remain deferred, unrelated slices — this work only makes a module's *declared*
|
||||
@@ -477,8 +487,10 @@ pnpm test:migrations:integration # requires Docker
|
||||
|
||||
## Next phase
|
||||
|
||||
Phase 5A, pass 3: deferred from this slice — module HTTP route-registration contracts (a module
|
||||
mounting its own endpoints), a module runtime-context authorization service, relationship-derived
|
||||
grant creation itself (a roster or similar module operation, not the generic grants API),
|
||||
roles/groups/teams/tenants, an owner-management HTTP endpoint, and service accounts/API tokens for
|
||||
non-browser automation.
|
||||
Deliberately deferred, unrelated slices — see
|
||||
[ADR 0014](../architecture/decisions/0014-module-permission-declarations-and-platform-registry.md#deferred)
|
||||
for the complete list: module HTTP route-registration contracts (a module mounting its own
|
||||
endpoints), a module runtime-context authorization service, relationship-derived grant creation
|
||||
itself (a roster or similar module operation, not the generic grants API), reinstall/reactivation
|
||||
policy for a permission whose owning module was previously removed, roles/groups/teams/tenants, an
|
||||
owner-management HTTP endpoint, and service accounts/API tokens for non-browser automation.
|
||||
|
||||
Reference in New Issue
Block a user