@@ -46,8 +46,8 @@ import { AuthzStoreUnavailableError } from './authz-store-unavailable.js';
4646// second copy of a security-relevant classifier exist. See `tryFind`'s catch.
4747//
4848// [#13667 / #11663 L5] `resolveTenancyPosture` is the REQUESTED tenancy posture. It
49- // is read at exactly one place below (§6b ) to key the LEGACY unscoped- grant anchor
50- // to the same walled postures the BOOT-side detector already keys on — the same
49+ // is read at exactly one place below (§6 ) to key the grant anchor of platform standing
50+ // ({ @link grantAnchorsPlatformStanding}) to the same walled postures the BOOT-side detector already keys on — the same
5151// module, the same function, the same fail-stricter direction that detector reads.
5252// It asks the ENVIRONMENT, never the engine, so the gate adds no query.
5353import { isMissingTableError , resolveTenancyPosture } from '@objectstack/types' ;
@@ -109,7 +109,7 @@ export interface ResolvedAuthzContext {
109109 /**
110110 * [ADR-0095 D2/D3] The monotonic posture rung this principal resolves to,
111111 * DERIVED once here from held capability grants (never a better-auth role):
112- * `PLATFORM_ADMIN` (an unscoped `admin_full_access` grant, or — since #11663
112+ * `PLATFORM_ADMIN` (the anchoring `admin_full_access` grant, or — since #11663
113113 * L2 — a VERIFIED `sys_user.email` on the deployment's declared
114114 * administrator list) > `TENANT_ADMIN`
115115 * (`organization_admin`) > `MEMBER` (the authenticated floor). `EXTERNAL` is
@@ -895,6 +895,52 @@ function grantOrganizationOf(row: unknown): string | null {
895895 return String ( value ) ;
896896}
897897
898+ /**
899+ * [ADR-0131 D5] Does a stored grant ANCHOR platform-admin standing — the one
900+ * predicate, with the posture as its parameter, that §6b asks of every
901+ * `admin_full_access` grant:
902+ *
903+ * - a walled posture (`group` / `isolated`): no grant anchors standing; it is
904+ * config-derived (`OS_PLATFORM_OWNER_EMAIL`, §6b-config) and nothing else
905+ * (#11663 L5);
906+ * - `single`: the grant the first-user promotion writes, which is owned by the
907+ * Default Organization ({@link readDefaultOrganizationId}), and the
908+ * organization-less row that promotion wrote before it was.
909+ *
910+ * Any other organization's grant of the set confers the set, never standing.
911+ */
912+ function grantAnchorsPlatformStanding (
913+ grant : unknown ,
914+ anchor : { walled : boolean ; defaultOrganizationId : string | undefined } ,
915+ ) : boolean {
916+ if ( anchor . walled ) return false ;
917+ const organizationId = grantOrganizationOf ( grant ) ;
918+ return organizationId === null
919+ || ( anchor . defaultOrganizationId !== undefined && organizationId === anchor . defaultOrganizationId ) ;
920+ }
921+
922+ /**
923+ * [ADR-0131 D3/D5] The deployment's Default Organization: the one with
924+ * `slug = 'default'`, else the only organization there is; `undefined` when
925+ * neither exists (several organizations, none of them `default`, name none).
926+ * The definition plugin-auth's boot invariant (`default-organization-invariant.ts`)
927+ * creates the organization by, read-only here — and the one plugin-security's
928+ * promotion resolves the owner of the grant it writes by, so the row it writes
929+ * is the row this resolver reads as the anchor.
930+ *
931+ * A composition that registers no organization object holds none, and is not
932+ * read: the engine refuses an unregistered name as "object not found".
933+ */
934+ async function readDefaultOrganizationId ( ql : any ) : Promise < string | undefined > {
935+ if ( ! registryHoldsObject ( ql , SystemObjectName . ORGANIZATION ) ) return undefined ;
936+ const idOf = ( row : any ) : string | undefined =>
937+ row ?. id === undefined || row ?. id === null || String ( row . id ) === '' ? undefined : String ( row . id ) ;
938+ const bySlug = await tryFind ( ql , SystemObjectName . ORGANIZATION , { slug : 'default' } , 1 ) ;
939+ if ( idOf ( bySlug [ 0 ] ) ) return idOf ( bySlug [ 0 ] ) ;
940+ const all = await tryFind ( ql , SystemObjectName . ORGANIZATION , { } , 2 ) ;
941+ return all . length === 1 ? idOf ( all [ 0 ] ) : undefined ;
942+ }
943+
898944/** The activation ledger's catalog types the resolver honours (ADR-0126 §3 regime C, as ADR-0131 D6 amends it). */
899945const LEDGER_CATALOG_TYPES = [ 'position' , 'permission' ] as const ;
900946
@@ -1426,7 +1472,42 @@ export async function resolveUserAuthzGrants(
14261472 // positions' — never a catalog row's `active` column.
14271473 // (read in Leg 1 above)
14281474 const upsRows = upsRowsAll . filter ( ( r ) => isGrantActive ( r , nowMs ) ) ;
1429- const applyingGrants = upsRows . filter ( ( r ) => grantAppliesInTenant ( grantOrganizationOf ( r ) , tenantId ) ) ;
1475+ // [ADR-0131 D5 / #11663 L5] The platform-standing ANCHOR of a stored grant — one
1476+ // derivation site, with the posture as its parameter ({@link grantAnchorsPlatformStanding}):
1477+ //
1478+ // - WALLED: no grant anchors standing. The walled half of the legacy anchor is
1479+ // retired, so on a walled rig platform standing is config-derived
1480+ // (`OS_PLATFORM_OWNER_EMAIL`, §6b-config) and nothing else.
1481+ // - `single` — the DEFAULT, what a deployment that configured no tenancy at all
1482+ // resolves to: the `admin_full_access` grant `bootstrapPlatformAdmin` mints for
1483+ // the first human user, which D5 has OWNED BY THE DEFAULT ORGANIZATION (#11979;
1484+ // maintainer ruling A on #18707 keeps the zero-config promotion and moves only the
1485+ // row's owner), and the organization-less row that promotion wrote before it was —
1486+ // the row D10's ceremony attributes to that same organization. ⛔ Reading only the
1487+ // organization-less row would leave every promoted `single` rig with NO platform
1488+ // administrator once its grant is the Default Organization's.
1489+ //
1490+ // The REQUESTED posture is the input, not the effective one — the same source and the
1491+ // same fail-stricter direction the boot side reads: a deployment that ASKED for a wall
1492+ // stays walled here even while running degraded (`OS_ALLOW_DEGRADED_TENANCY=1`).
1493+ // `resolveTenancyPosture()` asks the ENVIRONMENT, so the posture costs no query; the
1494+ // Default Organization is read only for a principal that holds an organization-owned
1495+ // `admin_full_access` grant under `single`, so a principal holding none issues exactly
1496+ // the reads it issued before (the batch-equivalence goldens are unchanged).
1497+ const walled = postureEnforcesWall ( resolveTenancyPosture ( ) ) ;
1498+ const defaultOrganizationId =
1499+ ! walled && upsRows . some ( ( r ) => grantSetNameOf ( r ) === ADMIN_FULL_ACCESS && grantOrganizationOf ( r ) !== null )
1500+ ? await readDefaultOrganizationId ( ql )
1501+ : undefined ;
1502+ const platformAnchor = { walled, defaultOrganizationId } ;
1503+ const anchorsPlatformStanding = ( grant : unknown ) : boolean =>
1504+ grantSetNameOf ( grant ) === ADMIN_FULL_ACCESS && grantAnchorsPlatformStanding ( grant , platformAnchor ) ;
1505+ // An anchoring grant applies in whichever organization the principal acts in, as the
1506+ // organization-less row it replaces always did: platform standing is the deployment's,
1507+ // never one tenant's.
1508+ const applyingGrants = upsRows . filter (
1509+ ( r ) => grantAppliesInTenant ( grantOrganizationOf ( r ) , tenantId ) || anchorsPlatformStanding ( r ) ,
1510+ ) ;
14301511 const catalog = securityCatalogReaderOf ( ql ) ;
14311512 const grantSetNames : string [ ] = [ ] ;
14321513 for ( const grant of applyingGrants ) {
@@ -1436,30 +1517,6 @@ export async function resolveUserAuthzGrants(
14361517 // Position-bound sets (§6a), by name, in the order the held positions name them.
14371518 const boundSetNames : string [ ] = [ ] ;
14381519 let disabled = { positions : new Set < string > ( ) , permissions : new Set < string > ( ) } ;
1439- // [#11663 L5] …and under a WALLED posture it no longer is. This is the EXIT of
1440- // the migration window L4 opened: the walled half of the legacy unscoped anchor
1441- // is RETIRED, so on a walled rig platform standing is config-derived
1442- // (`OS_PLATFORM_OWNER_EMAIL`, §6b-config) and nothing else. Read ADR-0131 D5 as
1443- // amended 2026-09-17 (#18413) before widening this: the anchor retires here for
1444- // the WALLED half only.
1445- //
1446- // ⛔ The `single` posture is deliberately NOT gated, and this is the whole of why
1447- // the flag exists rather than a plain deletion. Under `single` — the DEFAULT, what
1448- // a deployment that configured no tenancy at all resolves to — `bootstrapPlatformAdmin`
1449- // MINTS this very row for the first human user, and that promotion is ruled correct
1450- // and unchanged (Choice 4A, #11974; maintainer 2026-09-08, recorded in ADR-0131's 2026-09-17 amendment, verbatim: "The
1451- // rest of Choice 4A (#11974, 2026-08-25) stands: retiring the walled write must not
1452- // retire the `single` one"). A development environment started for a moment cannot be
1453- // asked to declare an administrator first, so deleting the row route for every posture
1454- // would leave every zero-config rig with NO platform administrator at all. The
1455- // `single` half's own disposition is Choice 4B (#11979) — ⛔ a different card.
1456- //
1457- // The REQUESTED posture is the input, not the effective one — the same source and the
1458- // same fail-stricter direction the boot side reads: a deployment that ASKED for a wall
1459- // stays walled here even while running degraded (`OS_ALLOW_DEGRADED_TENANCY=1`).
1460- // `resolveTenancyPosture()` asks the ENVIRONMENT, so this gate issues no query and the
1461- // pinned batch-equivalence query multiset cannot move whichever way it answers.
1462- const legacyGrantAnchorRetired = postureEnforcesWall ( resolveTenancyPosture ( ) ) ;
14631520 let hasPlatformAdminGrant = false ;
14641521
14651522 // 5b. [ADR-0090 D5] Audience anchor: every AUTHENTICATED member implicitly
@@ -1526,28 +1583,23 @@ export async function resolveUserAuthzGrants(
15261583 if ( ! name ) continue ;
15271584 const body = await catalogSetBody ( catalog , name ) ;
15281585 if ( ! body ) continue ;
1529- const ownGrant = grantOrganizationOf ( grant ) !== null ;
15301586 const active = ! disabled . permissions . has ( name ) ;
15311587 take ( body , active ) ;
1532- // platform_admin (ADR-0068 D2) is DERIVED from an UNSCOPED admin_full_access
1588+ // platform_admin (ADR-0068 D2) is DERIVED from the anchoring admin_full_access
15331589 // USER grant — the single source of truth (no trusted stored boolean). The
1534- // set is the catalog's one `admin_full_access` (ADR-0131 D4); an
1535- // organization's grant of it confers the set, never platform standing.
1590+ // set is the catalog's one `admin_full_access` (ADR-0131 D4); a grant the
1591+ // anchor does not read (any other organization's, and every grant under a
1592+ // wall) confers the set, never platform standing.
15361593 //
15371594 // [ADR-0049] A DEACTIVATED set confers no standing either: a deactivated
15381595 // `admin_full_access` must not keep conferring PLATFORM_ADMIN.
15391596 //
1540- // [#11663 L5] `legacyGrantAnchorRetired` gates the ANCHOR, not the grant: the
1541- // set's own name and capabilities are pushed below exactly as for any other
1542- // held set, on every posture. What a walled rig stops deriving from the
1543- // grant is PLATFORM_ADMIN STANDING — the rung, the built-in
1544- // `platform_admin` position and everything downstream of them.
1545- if (
1546- ! ownGrant
1547- && body . name === ADMIN_FULL_ACCESS
1548- && active
1549- && ! legacyGrantAnchorRetired
1550- ) {
1597+ // [#11663 L5] The posture gates the ANCHOR, not the grant: the set's own name
1598+ // and capabilities are pushed below exactly as for any other held set, on
1599+ // every posture. What a walled rig stops deriving from the grant is
1600+ // PLATFORM_ADMIN STANDING — the rung, the built-in `platform_admin` position
1601+ // and everything downstream of them.
1602+ if ( anchorsPlatformStanding ( grant ) && active ) {
15511603 hasPlatformAdminGrant = true ;
15521604 }
15531605 }
@@ -1582,8 +1634,8 @@ export async function resolveUserAuthzGrants(
15821634 // ADDITIVE, never subtractive: nothing above is revoked here (design §5
15831635 // step 3). A deployment that has declared no administrators resolves
15841636 // exactly as it did — `platformAdminConfig.emails` is empty and the branch
1585- // short-circuits before it looks at any row. Under `single` the legacy grant
1586- // read above then remains the only anchor; under a WALLED posture [#11663 L5]
1637+ // short-circuits before it looks at any row. Under `single` the grant anchor
1638+ // read above (§6) then remains the only anchor; under a WALLED posture [#11663 L5]
15871639 // that read is retired, so an undeclared walled rig has NO platform
15881640 // administrator — which is the fail-closed answer the boot-side backstop in
15891641 // `plugin-security` announces at startup.
@@ -1622,7 +1674,7 @@ export async function resolveUserAuthzGrants(
16221674
16231675 // 6d. [ADR-0095 D2/D3] Resolve the posture rung ONCE, from held CAPABILITY
16241676 // grants — never from a better-auth role. `PLATFORM_ADMIN` from the
1625- // unscoped `admin_full_access` grant (the same `viewAllRecords`/
1677+ // anchoring `admin_full_access` grant (the same `viewAllRecords`/
16261678 // `modifyAllRecords` evidence the superuser bypass trusts); `TENANT_ADMIN`
16271679 // from the `organization_admin` grant (auto-provisioned from the better-
16281680 // auth owner/admin role at §3 above — a provisioning source, not an
@@ -1668,7 +1720,9 @@ export async function resolveUserAuthzGrants(
16681720 *
16691721 * ADR-0068 D2 defined PLATFORM standing as one thing: an UNSCOPED
16701722 * (`organization_id = null`) `sys_user_permission_set` grant on the
1671- * `admin_full_access` set, held **now**. Since #11663 L2 there is a SECOND
1723+ * `admin_full_access` set, held **now** — under `single` since ADR-0131 D5 the
1724+ * grant owned by the Default Organization, beside that legacy row (§6,
1725+ * {@link grantAnchorsPlatformStanding}). Since #11663 L2 there is a SECOND
16721726 * anchor beside it — a `sys_user` row whose VERIFIED email is on the
16731727 * deployment's declared administrator list (`OS_PLATFORM_OWNER_EMAIL`) — and
16741728 * this predicate answers for both, for free, because it is a projection rather
@@ -1679,7 +1733,7 @@ export async function resolveUserAuthzGrants(
16791733 *
16801734 * It is a PROJECTION of {@link resolveUserAuthzGrants}, never a second
16811735 * derivation: the answer is the `PLATFORM_ADMIN` rung of the posture ladder,
1682- * and that rung is derived from the unscoped -grant evidence and nothing else.
1736+ * and that rung is derived from the anchoring -grant evidence and nothing else.
16831737 * Everything that governs those grants therefore applies here by construction
16841738 * and cannot drift from it — the ADR-0091 validity window (§6), the ADR-0049
16851739 * `active` flag on the catalogue row (§6b), the system-identity read, and the
0 commit comments