Skip to content

Commit 23d3545

Browse files
committed
docs: the active switch is the activation ledger; a catalogue row's active no longer decides a grant
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AGgRrdom7nizSbHc5Gws2U
1 parent d0cab9f commit 23d3545

1 file changed

Lines changed: 27 additions & 22 deletions

File tree

‎content/docs/permissions/authorization.mdx‎

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -396,26 +396,32 @@ substrate above.
396396
## Grant lifecycle: the `active` switch (ADR-0049)
397397

398398
Validity windows above date a **user's grant row**. The second lifecycle
399-
control dates the **catalogue row itself**: `sys_permission_set.active` and
400-
`sys_position.active`, the switch behind the Deactivate action on both objects.
401-
It answers a different question — "switch this grant off for everyone, without
402-
deleting it or unwinding the assignments" — and it is enforced in the same
403-
place, by the same discipline: **resolution-time filtering, fail-closed**, in
399+
control switches off the **permission set or position itself**: a
400+
`sys_metadata_activation` row of type `permission` or `position` whose `active`
401+
reads false. The activation ledger is deployment-wide. It answers a different
402+
question — "switch this grant off for everyone, without deleting it or
403+
unwinding the assignments" — and it is enforced in the same place, by the same
404+
discipline: **resolution-time filtering, fail-closed**, in
404405
`resolveAuthzContext`, with no cleanup job involved.
405406

407+
A catalogue row's own `active` column (`sys_permission_set.active`,
408+
`sys_position.active`) no longer decides a grant. That column is what Setup's
409+
Deactivate action writes today, so a Deactivate in Setup does not switch the
410+
grant off.
411+
406412
- A deactivated **permission set** contributes nothing: not its name, not its
407413
`system_permissions`, not its `tab_permissions`. As with an expired grant, a
408414
deactivated unscoped `admin_full_access` no longer derives `platform_admin`
409415
— the flag is applied *before* the posture is derived, not after. A
410416
**configured** administrator is judged elsewhere: that anchor reads the
411417
deployment's configuration and the caller's own `sys_user` row, never the
412-
catalogue row, so the flag does not decide their standing.
418+
ledger, so the switch does not decide their standing.
413419
- A deactivated **position** stops carrying its permission sets, and its name
414420
stops appearing in `positions`, so a permission set that merely shares the
415421
position's name cannot resolve through it either.
416422
- Assignments are untouched. `sys_user_position` and
417-
`sys_user_permission_set` rows stay exactly as they were, and re-activating
418-
the catalogue row restores every grant it carried, at the next resolution.
423+
`sys_user_permission_set` rows stay exactly as they were, and switching the
424+
ledger row back on restores every grant it carried, at the next resolution.
419425

420426
The explain engine reports a deactivated-but-held row with the same dedicated
421427
contributor state the expired case gets (`state: 'deactivated'`, "held —
@@ -429,20 +435,19 @@ already walks: `sys_user_position` rows and direct `sys_user_permission_set`
429435
grants (a deactivated set reached only through an active position's linkage is
430436
not re-derived — that would replicate the resolver's aggregation).
431437

432-
**Absent is ACTIVE.** Only a stored value that really reads false takes a grant
433-
away, so a row that predates the column keeps granting. The same predicate
434-
(`isRowActive`) is used by every reader, including the last-administrator
435-
guard's simulation — a guard that modelled "deactivated" differently from the
436-
resolver would permit exactly the write it exists to refuse.
437-
438-
**Deactivating the break-glass set is refused.** Switching `admin_full_access`
439-
off un-makes every platform admin who holds it *through a grant row*, in one
440-
write — and re-activating it needs the permission just lost. That write is
441-
judged like deleting or renaming the row (ADR-0135 D5.2): it is refused while
442-
it would leave the environment with no administrator who can sign in. The
443-
enumeration behind that judgement counts **both** anchors, so an environment
444-
whose administrators are configured rather than granted is not empty, and the
445-
write is permitted there. Re-activation is never refused.
438+
**Absent is ACTIVE.** Only a ledger row whose `active` reads false takes a
439+
grant away; a permission set or position with no ledger row is in effect. The
440+
last-administrator guard's enumeration still reads the catalogue row's
441+
`active`, so it counts a row-deactivated `admin_full_access` as off and refuses
442+
more than it must, never less.
443+
444+
**Switching the break-glass set off is refused.** A ledger row switching
445+
`admin_full_access` off would un-make every platform admin who holds it
446+
*through a grant row*, in one write — and switching it back on needs the
447+
permission just lost. The last-administrator guard refuses such a ledger
448+
write outright, whatever the administrator population (ADR-0135 D5.2), and
449+
refuses one whose target row it cannot read. Switching it back on is never
450+
refused.
446451

447452
Deactivation is an incident-response control, so what it does **not** touch is
448453
deliberate. Administration surfaces keep listing and editing a deactivated

0 commit comments

Comments
 (0)