From 1952b6130d55dece07997e62a63fa3af49e29423 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 01:50:39 +0000 Subject: [PATCH 1/3] docs(identity): re-point the SCIM/identity ADR-0071 citations at the records that mean them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ADR-0071` names two unrelated decisions from this repo's point of view. The record under `docs/adr/0071-*` is *Dataset semantic-layer depth — multi-hop joins*; the identity/SCIM citations mean the enterprise-identity decision taken in `objectstack-ai/cloud`, whose open mechanism half is now mirrored here as ADR-0134 (landed 2026-09-07). Every identity citation therefore resolved to a real page about the wrong subject. Re-points 44 bare identity-meaning citations, per director ruling B as amended: - 43 -> `ADR-0134` — the open mechanism half (SCIM forces the admin plugin on, `active:false` -> ban, the env-side Service Provider, the seven stable SCIM models). ADR-0134 is a local record with anchors into exactly these files. - 1 -> `cloud ADR-0071` — `auth-manager.ts`'s "the paid Identity lifecycle", which names the commercial half that stays in the cloud record. Untouched, deliberately: the 22 dataset-meaning citations (they match the local record), the 6 CHANGELOGs (historical archive), `docs/adr/**` (governed), and `auth-plugin.ts`'s already-qualified `cloud ADR-0071 verification #1`. Bare `ADR-0071` still resolves exactly as before — the qualifier only adds precision, it does not weaken the gate. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude --- .../areas/identity-auth.json | 2 +- .../sys-scim-connection-binding.object.ts | 2 +- .../sys-scim-connection-credential.object.ts | 4 ++-- .../identity/sys-scim-group-member.object.ts | 2 +- .../src/identity/sys-scim-group.object.ts | 2 +- .../sys-scim-identity-tombstone.object.ts | 2 +- .../sys-scim-projection-grant.object.ts | 2 +- .../src/identity/sys-scim-subject.object.ts | 2 +- .../src/identity/sys-scim-user.object.ts | 2 +- .../src/identity/sys-user.object.ts | 2 +- .../plugin-auth/src/auth-manager.test.ts | 18 +++++++++--------- .../plugins/plugin-auth/src/auth-manager.ts | 16 ++++++++-------- .../plugins/plugin-auth/src/auth-plugin.ts | 2 +- .../plugin-auth/src/auth-schema-config.ts | 2 +- .../plugin-auth/src/last-admin-guard.test.ts | 2 +- .../plugin-auth/src/last-admin-guard.ts | 2 +- .../plugin-auth/src/objectql-adapter.ts | 2 +- .../admin-credential-lifecycle.dogfood.test.ts | 2 +- ...min-platform-admin-standing.dogfood.test.ts | 2 +- ...dmin-route-nonadmin-refusal.dogfood.test.ts | 2 +- packages/spec/scripts/lib/default-changes.ts | 4 ++-- .../spec/src/kernel/public-auth-features.ts | 2 +- packages/spec/src/system/auth-config.zod.ts | 8 ++++---- pnpm-workspace.yaml | 2 +- 24 files changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/qa/platform-checklist/areas/identity-auth.json b/docs/qa/platform-checklist/areas/identity-auth.json index 168dca149d..342a3da972 100644 --- a/docs/qa/platform-checklist/areas/identity-auth.json +++ b/docs/qa/platform-checklist/areas/identity-auth.json @@ -478,7 +478,7 @@ "packages/plugins/plugin-auth/src/auth-route-ledger.ts#BETTER_AUTH_MOUNTED_SURFACE (BETTER_AUTH_MOUNTED_SURFACE admin/* rows: list-users, create-user, set-role, remove-user, revoke-user-session(s))", "packages/plugins/plugin-auth/src/admin-user-endpoints.ts#resolvePassword (create-user resolvePassword: explicit password wins over generatePassword — #3031/#3033; leaves sys_user + credential sys_account)", "packages/plugins/plugin-security/src/security-plugin.ts#__referentialFieldClear (§A5 #3023 EXEMPTION: __referentialFieldClear owner_id-null cascade rides a server-derived context, the owner-anchor guard must not veto it) + security-plugin.test.ts '[#3023] … engine referential FK clear … is exempt'", - "packages/spec/src/kernel/public-auth-features.ts#sys_user (admin flag gates the sys_user lifecycle actions; SCIM forces it on — ADR-0071)", + "packages/spec/src/kernel/public-auth-features.ts#sys_user (admin flag gates the sys_user lifecycle actions; SCIM forces it on — ADR-0134)", "packages/qa/dogfood/test/admin-identity-audit-trail.dogfood.test.ts", "packages/plugins/plugin-auth/src/session-of-record.test.ts#body (better-auth answers /get-session with HTTP 200 + a JSON null body when the session is gone — NOT 401; a status-only assertion would pass against a fully revoked session)" ], diff --git a/packages/platform-objects/src/identity/sys-scim-connection-binding.object.ts b/packages/platform-objects/src/identity/sys-scim-connection-binding.object.ts index e03924a276..2a15c27241 100644 --- a/packages/platform-objects/src/identity/sys-scim-connection-binding.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-connection-binding.object.ts @@ -30,7 +30,7 @@ export const SysScimConnectionBinding = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Lifecycle state for a SCIM provisioning connection, including its decommission state machine', diff --git a/packages/platform-objects/src/identity/sys-scim-connection-credential.object.ts b/packages/platform-objects/src/identity/sys-scim-connection-credential.object.ts index 8ad441a7b8..bcbb6f7404 100644 --- a/packages/platform-objects/src/identity/sys-scim-connection-credential.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-connection-credential.object.ts @@ -4,7 +4,7 @@ import { ObjectSchema, Field } from '@objectstack/spec/data'; /** * sys_scim_connection_credential — ObjectStack-owned bearer credentials for - * SCIM provisioning connections (#3653, ADR-0071). + * SCIM provisioning connections (#3653, ADR-0134). * * Stable `@better-auth/scim` stores NO credential of its own: the rc.1 * `/scim/generate-token` endpoint and `scimProvider.scim_token` column are @@ -47,7 +47,7 @@ export const SysScimConnectionCredential = ObjectSchema.create({ // ADR-0010 §3.7 — platform-managed identity table; tenants may not edit schema. protection: { lock: 'full', - reason: 'ObjectStack-owned SCIM credential store (#3653) — see ADR-0071.', + reason: 'ObjectStack-owned SCIM credential store (#3653) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Bearer credentials (one-way digests) that authenticate SCIM provisioning connections', diff --git a/packages/platform-objects/src/identity/sys-scim-group-member.object.ts b/packages/platform-objects/src/identity/sys-scim-group-member.object.ts index b0ee343b09..d3416505ac 100644 --- a/packages/platform-objects/src/identity/sys-scim-group-member.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-group-member.object.ts @@ -25,7 +25,7 @@ export const SysScimGroupMember = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'SCIM group membership rows pushed by the IdP (group ↔ provisioned user)', diff --git a/packages/platform-objects/src/identity/sys-scim-group.object.ts b/packages/platform-objects/src/identity/sys-scim-group.object.ts index 5eaf6b28a4..82e82b5b38 100644 --- a/packages/platform-objects/src/identity/sys-scim-group.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-group.object.ts @@ -29,7 +29,7 @@ export const SysScimGroup = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'IdP-pushed SCIM 2.0 groups, scoped per provisioning connection', diff --git a/packages/platform-objects/src/identity/sys-scim-identity-tombstone.object.ts b/packages/platform-objects/src/identity/sys-scim-identity-tombstone.object.ts index 8334af5c09..48144c41e4 100644 --- a/packages/platform-objects/src/identity/sys-scim-identity-tombstone.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-identity-tombstone.object.ts @@ -25,7 +25,7 @@ export const SysScimIdentityTombstone = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Tombstones of IdP-deleted SCIM identities, kept so a re-provision is recognized', diff --git a/packages/platform-objects/src/identity/sys-scim-projection-grant.object.ts b/packages/platform-objects/src/identity/sys-scim-projection-grant.object.ts index cc5f66d82b..8aec1bffa5 100644 --- a/packages/platform-objects/src/identity/sys-scim-projection-grant.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-projection-grant.object.ts @@ -26,7 +26,7 @@ export const SysScimProjectionGrant = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Role/entitlement grants projected onto platform users by SCIM provisioning', diff --git a/packages/platform-objects/src/identity/sys-scim-subject.object.ts b/packages/platform-objects/src/identity/sys-scim-subject.object.ts index 00a934e678..9954d25bd9 100644 --- a/packages/platform-objects/src/identity/sys-scim-subject.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-subject.object.ts @@ -26,7 +26,7 @@ export const SysScimSubject = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Per-user SCIM provisioning link — one row per user any SCIM connection provisions', diff --git a/packages/platform-objects/src/identity/sys-scim-user.object.ts b/packages/platform-objects/src/identity/sys-scim-user.object.ts index e631aa6eb3..838bde5282 100644 --- a/packages/platform-objects/src/identity/sys-scim-user.object.ts +++ b/packages/platform-objects/src/identity/sys-scim-user.object.ts @@ -30,7 +30,7 @@ export const SysScimUser = ObjectSchema.create({ // ADR-0010 §3.7 — managed by better-auth; tenants may not edit schema. protection: { lock: 'full', - reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0071.', + reason: 'Identity table managed by better-auth (@better-auth/scim) — see ADR-0134.', docsUrl: 'https://objectstack.ai/docs/references/shared/protection', }, description: 'Per-connection projection of an IdP-provisioned user (SCIM 2.0 /Users)', diff --git a/packages/platform-objects/src/identity/sys-user.object.ts b/packages/platform-objects/src/identity/sys-user.object.ts index bd4ad9344b..c1dbfb38eb 100644 --- a/packages/platform-objects/src/identity/sys-user.object.ts +++ b/packages/platform-objects/src/identity/sys-user.object.ts @@ -109,7 +109,7 @@ export const SysUser = ObjectSchema.create({ // `requiresFeature: 'admin'` (#2874) — when the plugin is off the UI // hides them instead of rendering buttons that 404. SCIM deployments // keep them: SCIM forces the admin plugin (and `features.admin`) on - // (ADR-0071). UI surfaces them under the row menu AND the + // (ADR-0134). UI surfaces them under the row menu AND the // record-detail header (`record_header`, overflowing into the ⋯ // "More" menu) so platform admins can manage an account from either // the Users list or an open user record — without dropping to SQL or diff --git a/packages/plugins/plugin-auth/src/auth-manager.test.ts b/packages/plugins/plugin-auth/src/auth-manager.test.ts index aa02c7021e..633ccd9cb8 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.test.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.test.ts @@ -456,7 +456,7 @@ describe('AuthManager', () => { }); // @better-auth/scim mounts the SCIM 2.0 Service Provider so an external IdP - // can auto-provision/deprovision this env's users (ADR-0071). It is opt-in + // can auto-provision/deprovision this env's users (ADR-0134). It is opt-in // via `plugins.scim` (explicit value wins, #13439) or OS_SCIM_ENABLED // (decides where the config leaves it unset), and effective SCIM FORCES // the admin plugin on (active:false → ban runs through admin). @@ -508,7 +508,7 @@ describe('AuthManager', () => { // config leaves the key unset. This is the cloud control plane's case: // its plan-derived `plugins.scim: false` must be authoritative even in a // deployment env that carries an ambient OS_SCIM_ENABLED (cloud#1265). - // The forced-admin coupling (ADR-0071) follows the EFFECTIVE scim value, + // The forced-admin coupling (ADR-0134) follows the EFFECTIVE scim value, // so declining scim also declines the admin plugin it would have dragged // in (unless `admin` is set explicitly). it('should NOT register the scim plugin (nor force admin on) when plugins.scim=false despite OS_SCIM_ENABLED', async () => { @@ -559,7 +559,7 @@ describe('AuthManager', () => { await manager.getAuthInstance(); const ids = capturedConfig.plugins.map((p: any) => p.id); expect(ids).toContain('scim'); - // ADR-0071 — the forced-admin coupling is unchanged: effective SCIM + // ADR-0134 — the forced-admin coupling is unchanged: effective SCIM // still drags the admin plugin in when `admin` is left unset. expect(ids).toContain('admin'); expect(manager.getPublicConfig().features.admin).toBe(true); @@ -572,11 +572,11 @@ describe('AuthManager', () => { // #13816 (maintainer ruling 2026-09-01) — `admin` is tri-state, and the // one incoherent corner is REFUSED at construction: effective SCIM with an - // explicit `plugins.admin: false` contradicts ADR-0071 (SCIM's + // explicit `plugins.admin: false` contradicts ADR-0134 (SCIM's // active:false deprovisioning runs through the admin plugin), so the // manager throws a documented conflict instead of silently honouring the // decline and mounting SCIM with a broken deprovisioning path. - it('REFUSES construction when plugins.scim=true and plugins.admin=false (documented ADR-0071 conflict)', () => { + it('REFUSES construction when plugins.scim=true and plugins.admin=false (documented ADR-0134 conflict)', () => { const prev = process.env.OS_SCIM_ENABLED; delete process.env.OS_SCIM_ENABLED; try { @@ -584,7 +584,7 @@ describe('AuthManager', () => { secret: 'test-secret-at-least-32-chars-long', baseUrl: 'http://localhost:3000', plugins: { scim: true, admin: false }, - })).toThrow(/plugins\.admin[\s\S]*ADR-0071[\s\S]*plugins\.scim: false/); + })).toThrow(/plugins\.admin[\s\S]*ADR-0134[\s\S]*plugins\.scim: false/); } finally { if (prev === undefined) delete process.env.OS_SCIM_ENABLED; else process.env.OS_SCIM_ENABLED = prev; @@ -599,7 +599,7 @@ describe('AuthManager', () => { secret: 'test-secret-at-least-32-chars-long', baseUrl: 'http://localhost:3000', plugins: { admin: false }, - })).toThrow(/OS_SCIM_ENABLED[\s\S]*ADR-0071/); + })).toThrow(/OS_SCIM_ENABLED[\s\S]*ADR-0134/); } finally { if (prev === undefined) delete process.env.OS_SCIM_ENABLED; else process.env.OS_SCIM_ENABLED = prev; @@ -693,7 +693,7 @@ describe('AuthManager', () => { plugins: { scim: true }, }); expect(() => manager.applyConfigPatch({ plugins: { admin: false } })) - .toThrow(/ADR-0071/); + .toThrow(/ADR-0134/); // The refused patch never became current: the advertised admin flag // still reflects the SCIM-forced coupling. expect(manager.getPublicConfig().features.admin).toBe(true); @@ -717,7 +717,7 @@ describe('AuthManager', () => { // appearing between construction and the lazy build must not mount // SCIM with its deprovisioning path silently declined. process.env.OS_SCIM_ENABLED = 'true'; - await expect(manager.getAuthInstance()).rejects.toThrow(/ADR-0071/); + await expect(manager.getAuthInstance()).rejects.toThrow(/ADR-0134/); } finally { if (prev === undefined) delete process.env.OS_SCIM_ENABLED; else process.env.OS_SCIM_ENABLED = prev; diff --git a/packages/plugins/plugin-auth/src/auth-manager.ts b/packages/plugins/plugin-auth/src/auth-manager.ts index a1c5491b20..f9b5c2a60a 100644 --- a/packages/plugins/plugin-auth/src/auth-manager.ts +++ b/packages/plugins/plugin-auth/src/auth-manager.ts @@ -383,7 +383,7 @@ export function resolveScimEnabled(pluginConfig?: Partial): bo * env vars read after boot), loudly, with the remedy in the message. * * SCIM's `active:false` deprovisioning path runs through the better-auth - * `admin` plugin (ban/unban — ADR-0071), so a SCIM-enabled deployment cannot + * `admin` plugin (ban/unban — ADR-0134), so a SCIM-enabled deployment cannot * have the admin plugin declined: effective SCIM with an explicit * `plugins.admin: false` is a contradiction. Previously the resolution * honoured the explicit `false` and mounted SCIM anyway — deprovisioning @@ -399,7 +399,7 @@ export function assertScimAdminCoherence(pluginConfig?: Partial typeof adapter)({ plugins: [admin({ schema: buildAdminPluginSchema() })], diff --git a/packages/plugins/plugin-auth/src/last-admin-guard.ts b/packages/plugins/plugin-auth/src/last-admin-guard.ts index 40daa74e67..cef1b8cc2e 100644 --- a/packages/plugins/plugin-auth/src/last-admin-guard.ts +++ b/packages/plugins/plugin-auth/src/last-admin-guard.ts @@ -14,7 +14,7 @@ * with an OPTIONAL host callback, so since #14360 the ban is landed by * `plugin-auth`'s own `identity.reconcileUser` (`auth-manager.ts` -> * `user-ban-write.ts`) — the same column and the same hook, a different - * author. SCIM still forces the admin plugin on (ADR-0071), which is what + * author. SCIM still forces the admin plugin on (ADR-0134), which is what * supplies the column and the `BANNED_USER` sign-in refusal. * 2. **deleting the `sys_user` row** (#5941) — how every *remove* lands: * better-auth's `/admin/remove-user` and `/delete-user`, an import, a diff --git a/packages/plugins/plugin-auth/src/objectql-adapter.ts b/packages/plugins/plugin-auth/src/objectql-adapter.ts index 1b1d1b5ed1..4a37dc87a2 100644 --- a/packages/plugins/plugin-auth/src/objectql-adapter.ts +++ b/packages/plugins/plugin-auth/src/objectql-adapter.ts @@ -55,7 +55,7 @@ export const AUTH_MODEL_TO_PROTOCOL: Record = { // op via resolveProtocolName. For scim that is the only available route; for // sso it is now a CHOICE (see #8224 — moving it onto the plugin option is an // open architecture question, deliberately not decided here). Off by default - // (OS_SSO_ENABLED / OS_SCIM_ENABLED). See ADR-0024 / ADR-0071. + // (OS_SSO_ENABLED / OS_SCIM_ENABLED). See ADR-0024 / ADR-0134. ssoProvider: 'sys_sso_provider', // (rc.1's one scim model, `scimProvider` → `sys_scim_provider`, retired // under #11757: stable 1.7.x no longer derives that model.) diff --git a/packages/qa/dogfood/test/admin-credential-lifecycle.dogfood.test.ts b/packages/qa/dogfood/test/admin-credential-lifecycle.dogfood.test.ts index 702c6bcd77..2686ba57ae 100644 --- a/packages/qa/dogfood/test/admin-credential-lifecycle.dogfood.test.ts +++ b/packages/qa/dogfood/test/admin-credential-lifecycle.dogfood.test.ts @@ -101,7 +101,7 @@ describe('#9482 C1/C2: admin credential lifecycle, both sides', () => { // `/admin/` 501s unless better-auth's admin plugin is on, and `bootStack` // exposes no auth-plugin override. `OS_SCIM_ENABLED` is the one env knob // that reaches it — `buildPluginList` resolves `admin: pluginConfig.admin ?? - // scimEffective` (ADR-0071). Read at auth-manager construction, so it must + // scimEffective` (ADR-0134). Read at auth-manager construction, so it must // precede boot. Same derivation `admin-identity-audit-trail` uses. priorScim = process.env.OS_SCIM_ENABLED; process.env.OS_SCIM_ENABLED = 'true'; diff --git a/packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts b/packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts index 865518eb06..8fc843b3a4 100644 --- a/packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts +++ b/packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts @@ -300,7 +300,7 @@ describe('#9482: what an ObjectStack platform admin gets from every /admin/ rout // `/admin/` 501s unless better-auth's admin plugin is on, and `bootStack` // exposes no auth-plugin override. `OS_SCIM_ENABLED` is the one env knob // that reaches it — `buildPluginList` resolves `admin: pluginConfig.admin ?? - // scimEffective` (ADR-0071, SCIM forces admin on). Read when the auth + // scimEffective` (ADR-0134, SCIM forces admin on). Read when the auth // manager is constructed, so it must precede boot. priorScim = process.env.OS_SCIM_ENABLED; process.env.OS_SCIM_ENABLED = 'true'; diff --git a/packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts b/packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts index 40db98adb4..45cc713b5f 100644 --- a/packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts +++ b/packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts @@ -468,7 +468,7 @@ describe('#9482 C9: every derived /admin/ route refuses a non-admin', () => { // The `/admin/` surface 501s unless better-auth's admin plugin is on, and // `bootStack` exposes no auth-plugin override. `OS_SCIM_ENABLED` is the one // env knob that reaches it — `AuthManager.buildPluginList` resolves - // `admin: pluginConfig.admin ?? scimEffective` (ADR-0071, SCIM forces admin + // `admin: pluginConfig.admin ?? scimEffective` (ADR-0134, SCIM forces admin // on), the same derivation `admin-identity-audit-trail.dogfood.test.ts` // uses. Read when the auth manager is constructed, so it must precede boot. priorScim = process.env.OS_SCIM_ENABLED; diff --git a/packages/spec/scripts/lib/default-changes.ts b/packages/spec/scripts/lib/default-changes.ts index 97f759c2d4..d82a423f57 100644 --- a/packages/spec/scripts/lib/default-changes.ts +++ b/packages/spec/scripts/lib/default-changes.ts @@ -276,7 +276,7 @@ export const DEFAULT_CHANGES_BY_MAJOR: Readonly z.object({ * Tri-state on purpose (#13816, maintainer ruling 2026-09-01): when left * UNSET, effective SCIM decides — SCIM provisioning forces the admin * plugin on, because SCIM's `active:false` deprovisioning path runs - * through the admin plugin's ban/unban (ADR-0071); without effective + * through the admin plugin's ban/unban (ADR-0134); without effective * SCIM, unset means off. An EXPLICIT `true` mounts the plugin * unconditionally. An EXPLICIT `false` declines the admin surface — and - * because of the ADR-0071 coupling, `false` beside effective SCIM is a + * because of the ADR-0134 coupling, `false` beside effective SCIM is a * contradiction plugin-auth REFUSES loudly at construction time (a * documented conflict instead of silently forcing admin on): accept the * admin plugin, or disable SCIM. */ admin: z.boolean().optional().describe( 'Enable platform admin operations (ban/unban, set-password, impersonate, set-role). ' + - 'Unset: forced on by effective SCIM (ADR-0071), otherwise off. An explicit false beside ' + + 'Unset: forced on by effective SCIM (ADR-0134), otherwise off. An explicit false beside ' + 'effective SCIM is a conflict refused at construction time.', ), /** @@ -148,7 +148,7 @@ export const AuthPluginConfigSchema = lazySchema(() => z.object({ * * Enabling SCIM (from either source) also forces the better-auth `admin` * plugin on when `admin` is left unset — SCIM's `active:false` → - * ban/unban runs through it (ADR-0071). An explicit `admin: false` beside + * ban/unban runs through it (ADR-0134). An explicit `admin: false` beside * effective SCIM is a contradiction refused at construction time (#13816 * — see `admin`). */ diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c554761e93..59ca7a4532 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -91,7 +91,7 @@ onlyBuiltDependencies: # `scimProvider` model, no generate-token endpoint, seven replacement # models: scimUser, scimGroup, scimGroupMember, scimSubject, # scimConnectionBinding, scimIdentityTombstone, scimProjectionGrant), -# because moving it was the ADR-0071 feature migration tracked by #3653 — +# because moving it was the ADR-0134 feature migration tracked by #3653 — # NOT the version bump #3002 did for the rest of the family. That # migration has LANDED (#3653, epic #11632): the seven stable models are # provisioned as platform objects, connections are runtime data resolved From 83675cb90145743dc9613c99dccb381f66dd060c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 01:51:44 +0000 Subject: [PATCH 2/3] docs(spec): regenerate auth-config reference from the re-pointed describe strings `content/docs/references/system/auth-config.mdx` is generated from `packages/spec/src/system/auth-config.zod.ts`'s `.describe()` text; the two admin-flag rows follow the producer to ADR-0134. Regenerated with `pnpm --filter @objectstack/spec gen:schema && gen:docs`, never hand-edited. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude --- content/docs/references/system/auth-config.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/docs/references/system/auth-config.mdx b/content/docs/references/system/auth-config.mdx index e01734b686..89c9cde512 100644 --- a/content/docs/references/system/auth-config.mdx +++ b/content/docs/references/system/auth-config.mdx @@ -102,7 +102,7 @@ Advanced / low-level Better-Auth options | **oidcProvider** | `boolean` | optional (default: `false`) | Enable the OpenID Connect provider plugin (acts as an OIDC IdP) | | **dynamicClientRegistration** | `boolean` | optional | Allow unauthenticated RFC 7591 Dynamic Client Registration (default: follows OS_MCP_SERVER_ENABLED) | | **deviceAuthorization** | `boolean` | optional (default: `false`) | Enable RFC 8628 Device Authorization Grant (CLI / TV-style login) | -| **admin** | `boolean` | optional | Enable platform admin operations (ban/unban, set-password, impersonate, set-role). Unset: forced on by effective SCIM (ADR-0071), otherwise off. An explicit false beside effective SCIM is a conflict refused at construction time. | +| **admin** | `boolean` | optional | Enable platform admin operations (ban/unban, set-password, impersonate, set-role). Unset: forced on by effective SCIM (ADR-0134), otherwise off. An explicit false beside effective SCIM is a conflict refused at construction time. | | **phoneNumber** | `boolean` | optional (default: `false`) | Enable phone-number sign-in (phone + password; OTP sign-in/reset when an SMS service is configured) | | **scim** | `boolean` | optional | Enable the SCIM 2.0 provisioning surface. Unset: OS_SCIM_ENABLED decides (absent = off); an explicit value wins over the env var. Effective SCIM forces the admin plugin on when admin is unset; an explicit admin: false beside effective SCIM is refused at construction time. | | **sso** | `boolean` | optional | Enable enterprise SSO (domain-routed OIDC/SAML sign-in). Unset: OS_SSO_ENABLED decides (absent = off); an explicit value wins over the env var. | @@ -212,7 +212,7 @@ OIDC / Generic OAuth2 provider configuration for enterprise SSO | **oidcProvider** | `boolean` | optional (default: `false`) | Enable the OpenID Connect provider plugin (acts as an OIDC IdP) | | **dynamicClientRegistration** | `boolean` | optional | Allow unauthenticated RFC 7591 Dynamic Client Registration (default: follows OS_MCP_SERVER_ENABLED) | | **deviceAuthorization** | `boolean` | optional (default: `false`) | Enable RFC 8628 Device Authorization Grant (CLI / TV-style login) | -| **admin** | `boolean` | optional | Enable platform admin operations (ban/unban, set-password, impersonate, set-role). Unset: forced on by effective SCIM (ADR-0071), otherwise off. An explicit false beside effective SCIM is a conflict refused at construction time. | +| **admin** | `boolean` | optional | Enable platform admin operations (ban/unban, set-password, impersonate, set-role). Unset: forced on by effective SCIM (ADR-0134), otherwise off. An explicit false beside effective SCIM is a conflict refused at construction time. | | **phoneNumber** | `boolean` | optional (default: `false`) | Enable phone-number sign-in (phone + password; OTP sign-in/reset when an SMS service is configured) | | **scim** | `boolean` | optional | Enable the SCIM 2.0 provisioning surface. Unset: OS_SCIM_ENABLED decides (absent = off); an explicit value wins over the env var. Effective SCIM forces the admin plugin on when admin is unset; an explicit admin: false beside effective SCIM is refused at construction time. | | **sso** | `boolean` | optional | Enable enterprise SSO (domain-routed OIDC/SAML sign-in). Unset: OS_SSO_ENABLED decides (absent = off); an explicit value wins over the env var. | From 8d2dfb3ac1f2432502b5a192a74b5b857b926a9d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 02:06:36 +0000 Subject: [PATCH 3/3] chore(changeset): patch for the identity ADR citation re-point Measured rather than assumed: all three packages ship the changed bytes. `@objectstack/spec` lists `src/**/*.zod.ts` in `files[]` so the describe string is published verbatim, and the generated `json-schema/` bundle carries it too; `plugin-auth`'s `dist/index.mjs` carries the operator-facing refusal message; `platform-objects`' `dist/index.mjs` carries the nine `protection.reason` strings. Patch, not minor: no export, schema or accept/refuse face moves. Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU Co-authored-by: Claude --- .../14361-adr-0071-identity-citations.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .changeset/14361-adr-0071-identity-citations.md diff --git a/.changeset/14361-adr-0071-identity-citations.md b/.changeset/14361-adr-0071-identity-citations.md new file mode 100644 index 0000000000..2ac8a5cff8 --- /dev/null +++ b/.changeset/14361-adr-0071-identity-citations.md @@ -0,0 +1,48 @@ +--- +'@objectstack/plugin-auth': patch +'@objectstack/platform-objects': patch +'@objectstack/spec': patch +--- + +docs(identity): re-point the SCIM/identity `ADR-0071` citations at the records that mean them (#14361) + +From this repository's point of view `ADR-0071` named two unrelated decisions, +and only one of them had a record here. `docs/adr/0071-dataset-semantic-layer-depth.md` +is *Dataset semantic-layer depth — multi-hop joins*. The identity and SCIM +citations mean something else entirely: the enterprise-identity decision taken in +`objectstack-ai/cloud`, whose open mechanism half has been mirrored into this +repo since 2026-09-07 as +[ADR-0134](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0134-env-side-scim-provisioning.md). +So a reader following one of those citations landed on a real page about the +wrong subject — worse than a dangling id, because a plausible-looking record +invites belief rather than a second question. + +44 identity-meaning citations now name the record that holds the decision they +describe. 43 of them read `ADR-0134` (the open mechanism half: effective SCIM +forces the better-auth `admin` plugin on, `active:false` lands as a ban plus +session revocation, the SCIM 2.0 Service Provider mounts in the environment, and +the seven stable `sys_scim_*` models). One reads `cloud ADR-0071` — the +"paid Identity lifecycle" note in `auth-manager.ts`, which names the commercial +half that deliberately stays in the cloud record. + +What actually reaches a consumer of these packages: + +- `@objectstack/plugin-auth` — the **operator-facing construction-time refusal** + raised when SCIM is effective beside an explicit `plugins.admin: false` now + cites ADR-0134 instead of ADR-0071. The condition that triggers the refusal, + its wording otherwise, and the two documented ways out are unchanged; only the + ADR number in the sentence moves. ⚠️ A deployment that greps that message for + the literal `ADR-0071` should grep for `ADR-0134`. +- `@objectstack/spec` — the `admin` flag's `.describe()` text (shipped both as + `src/system/auth-config.zod.ts` and in the generated `json-schema/` bundle), + and therefore the generated `content/docs/references/system/auth-config.mdx` + reference page app authors read. +- `@objectstack/platform-objects` — the `protection.reason` strings on the eight + `sys_scim_*` identity objects and on `sys_user`. + +No behaviour moves. No schema accepts or refuses anything it did not accept or +refuse before, no security or permission semantics are touched, and no ADR +record is written or edited. Bare `ADR-0071` still resolves exactly as it did: +the 22 dataset-meaning citations are byte-identical to `main` and +`check:adr-anchors` reports the same 35477 resolving citations before and after. +Historical archives — the six package CHANGELOGs — are deliberately untouched.