Skip to content

Commit 0f63965

Browse files
claude[bot]claude
andauthored
docs(comments): correct four docblock sites that still describe the retired platform-admin elevation gate (#13907)
The #11663 platform-admin re-anchor (legs L2 and L4) retired the walled platform-admin elevation gate: standing is now derived per request, from an env-configured verified email OR the legacy unscoped grant row. Four comment sites still described the retired mechanism. - packages/types/src/email-verified.ts: the docblock named the elevation gate as one of exactly two consumers. It ships in the package's .d.ts, so the wrong consumer set reaches consumers. Replaced with the live consumer set, which now includes the authorization derivation itself. - packages/plugins/plugin-auth/src/last-admin-guard.ts, the docblock above PERMISSION_SET_STANDING_KEYS: three consequences of the same dead premise — "un-makes every platform admin at once", "never from the capabilities it carries", and "UNSCOPED sys_user_permission_set grants only". Comment-only. `active` deliberately stays in PERMISSION_SET_STANDING_KEYS and the corrected prose now says why: it is a safety-side over-approximation, and removing it would be a behaviour change. Claude-Session: https://claude.ai/code/session_016ZC5rNQj3WEet5HAmmAkMs Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5887805 commit 0f63965

2 files changed

Lines changed: 59 additions & 22 deletions

File tree

‎packages/plugins/plugin-auth/src/last-admin-guard.ts‎

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,21 @@ export const GRANT_STANDING_KEYS = [
634634
* to say so: `resolveAuthzContext` derived `platform_admin` from the name
635635
* alone and read no flag. It now drops a DEACTIVATED set before any
636636
* derivation, so `active: false` on `admin_full_access` un-makes every
637-
* platform admin at once — the same end state as renaming or deleting the
638-
* row, reached by a payload that touches neither. Enforcing the flag without
637+
* GRANT-anchored platform admin at once — the same end state as renaming or
638+
* deleting the row, reached by a payload that touches neither. ⚠️ Not
639+
* "every platform admin": since the #11663 re-anchor (L2) standing has a
640+
* SECOND anchor this write cannot reach — a config-anchored administrator
641+
* (a declared `OS_PLATFORM_OWNER_EMAIL` address on a VERIFIED `sys_user`
642+
* row) is derived at `resolve-authz-context.ts` §6b-config without
643+
* consulting the set row or its `active` flag at all, and carries the
644+
* shipped `ADMIN_FULL_ACCESS_CAPABILITIES` envelope rather than the stored
645+
* set's. That is deliberately NOT a reason to drop `active` from this
646+
* list: the write can still empty the GRANT anchor, which on every
647+
* deployment that has declared no administrator emails is the whole
648+
* population. Listing it is an over-approximation in the SAFE direction —
649+
* it can cost an enumeration on a write that turns out to change no count,
650+
* never the reverse — and taking it out would be a behaviour change, not a
651+
* comment fix. Enforcing the flag without
639652
* listing it here would have left exactly one unguarded route to an
640653
* installation-wide lockout: the action is offered on every row with no
641654
* visibility or condition guard, the seeders deliberately never reconcile
@@ -645,13 +658,16 @@ export const GRANT_STANDING_KEYS = [
645658
* Everything else a permission-set write touches (`label`, `description`, the
646659
* four permission JSON blobs, provenance) is still invisible to "who is an
647660
* administrator" — `resolveAuthzContext` derives `platform_admin` from the NAME
648-
* of an ACTIVE set, never from the capabilities it carries — so those writes
649-
* still cost this guard no reads at all. Adding `active` does not walk that
650-
* back: the projection is FACETS ONLY and deliberately never re-flips a
651-
* record's on/off switch (`permission-set-projection.ts`, #4669), so every
652-
* projection pass, every `os meta resync` and every ordinary Setup edit still
653-
* misses this list entirely. What now pays for an enumeration is the write that
654-
* actually toggles the switch — which is the write this list exists to judge.
661+
* of an ACTIVE set or, since the #11663 re-anchor (L2), from the
662+
* deployment-config anchor, and from the capabilities of neither (the config
663+
* arm's envelope is the shipped `ADMIN_FULL_ACCESS_CAPABILITIES` declaration,
664+
* not the stored row) — so those writes still cost this guard no reads at all.
665+
* Adding `active` does not walk that back: the projection is FACETS ONLY and
666+
* deliberately never re-flips a record's on/off switch
667+
* (`permission-set-projection.ts`, #4669), so every projection pass, every
668+
* `os meta resync` and every ordinary Setup edit still misses this list
669+
* entirely. What now pays for an enumeration is the write that actually
670+
* toggles the switch — which is the write this list exists to judge.
655671
*
656672
* `id` is deliberately NOT here even though the enumeration reads it. On this
657673
* engine `data.id` on an update ADDRESSES the row (it is what
@@ -661,9 +677,12 @@ export const GRANT_STANDING_KEYS = [
661677
*
662678
* `sys_position` gets no analogous list because it has no route into this
663679
* enumeration to guard: platform-admin standing is read from UNSCOPED
664-
* `sys_user_permission_set` grants only (a position-bound `admin_full_access`
665-
* never conferred it, in the resolver or here), and org-administrator standing
666-
* is read from `sys_member.role`. Deactivating a position cannot empty either.
680+
* `sys_user_permission_set` grants and from the deployment-config anchor (a
681+
* declared `OS_PLATFORM_OWNER_EMAIL` address on a VERIFIED `sys_user` row,
682+
* which `USER_STANDING_KEYS` below guards) — a position-bound
683+
* `admin_full_access` reaches neither, in the resolver or here — and
684+
* org-administrator standing is read from `sys_member.role`. Deactivating a
685+
* position cannot empty any of them.
667686
*/
668687
export const PERMISSION_SET_STANDING_KEYS = ['name', 'active'] as const;
669688

‎packages/types/src/email-verified.ts‎

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,34 @@
1010
* column as verified would re-open the exact hole this predicate closes for
1111
* every row that predates the column.
1212
*
13-
* ONE resolution, two consumers, by design (#12751): the walled
14-
* platform-admin elevation gate (`plugin-security`
15-
* `bootstrapPlatformAdmin`, where the check REFUSES an unverified owner
16-
* match) and the walled owner-verification boot diagnostic (`plugin-auth`
17-
* `walled-owner-verification-path.ts`, where the check decides whether the
18-
* declared owner's account is already past needing a verification path).
19-
* Those two must answer "is this row verified?" identically — a drift means
20-
* a boot warning that forecasts a refusal the gate will not make, or stays
21-
* silent about one it will. `@objectstack/types` is the shared home both
22-
* packages already resolve `OS_PLATFORM_OWNER_EMAIL` from (`env.ts`).
13+
* ONE resolution, several consumers, by design (#12751) — and since the
14+
* #11663 platform-admin re-anchor (leg L4) the walled platform-admin
15+
* ELEVATION GATE this paragraph used to name first is RETIRED: under a
16+
* walled posture `bootstrapPlatformAdmin` writes no grant row and elevates
17+
* nobody, it reports. Standing is derived PER REQUEST instead — from a
18+
* config-anchored verified email, or the legacy unscoped grant row — so the
19+
* consumer set now includes the authorization derivation itself:
20+
*
21+
* - `matchesConfiguredPlatformAdmin` (`@objectstack/core`
22+
* `security/platform-admin.ts`), read at the one derivation site
23+
* (`resolve-authz-context.ts` §6b-config), where an UNVERIFIED account
24+
* holding a declared address confers nothing — and, through it,
25+
* `plugin-auth`'s last-admin guard, whose administrator enumeration must
26+
* answer the same question the resolver does;
27+
* - `resolvePlatformAdminStanding` (`plugin-security`
28+
* `platform-admin-service.ts`), the read-only standing/audit answer the
29+
* walled boot reports from, and `isVerifiedPlatformOwnerRow` beside it
30+
* (`platform-owner-wall-bypass.ts`), the Layer 0 wall bypass;
31+
* - the walled owner-verification boot diagnostic (`plugin-auth`
32+
* `walled-owner-verification-path.ts`, where the check decides whether the
33+
* declared owner's account is already past needing a verification path).
34+
*
35+
* They must all answer "is this row verified?" identically — a drift is no
36+
* longer just a boot warning forecasting a refusal that will not be made, it
37+
* is a diagnostic, an audit surface or a guard disagreeing with who actually
38+
* resolves PLATFORM_ADMIN on the next request. `@objectstack/types` is the
39+
* shared home every one of those packages already resolves
40+
* `OS_PLATFORM_OWNER_EMAIL` from (`env.ts`).
2341
*/
2442
export function isEmailVerifiedUserRow(row: unknown): boolean {
2543
const v = (row as { email_verified?: unknown } | null | undefined)?.email_verified;

0 commit comments

Comments
 (0)