Skip to content

Commit 24b7085

Browse files
fix(service-settings): audience help and the identity-auth checklist state the email-verification rule as shipped (#20421)
Part of #20413 — the settings console text (4 locales) and the identity-auth checklist. The plugin-auth boot-diagnostic location of the same stale statement follows in a second round on the card (seat append below). Clause-②: no ## What changed The auth settings console and the identity-auth platform checklist both said every audience posture other than `invite_only` forces email verification on. Commit `65352b7d` changed that rule. Each location now states the rule the code enforces. **The rule, re-derived from `packages/plugins/plugin-auth/src` at base `40b315b0`:** - **`email_domain`** always forces verification on (`resolveEmailVerificationRequirement`, audience-posture.ts). An explicit `false` is refused at entry from any source (`assertAudienceConfig`). - **`open`** forces it on unless the deployment declares an explicit `false`, in either of two ways: - `emailAndPassword.requireEmailVerification: false` in the stack config; - `OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false`. `bindAuthSettings` in auth-plugin.ts treats an env-sourced value as the deployment's own declaration. - **Under `open`, a `false` stored through the settings console is refused.** This is the `verificationDeclaredBy: 'console'` branch of `assertAudienceConfig`, and it plays out two ways: - Posture and `false` saved in one pass: the posture change is refused. - `false` saved while `open` already stands: the stored `false` is refused. - In both cases the standing config keeps ruling. - **`invite_only`** uses whatever value is declared, and defaults to off. The code agrees with the card's statement of the rule. ### Landing points (strings re-located on this tree by key and by each locale's wording) | File | Keys | Hits by key / by wording | |---|---|---| | `service-settings/src/manifests/auth.manifest.ts` | `audience` group `description`; `audience_posture` `description` | 2 / 2 | | `service-settings/src/translations/en.ts` | `groups.audience.description`; `keys.audience_posture.help` | 2 / 2 | | `service-settings/src/translations/zh-CN.ts` | same keys | 2 / 2 | | `service-settings/src/translations/es-ES.ts` | same keys | 2 / 2 | | `service-settings/src/translations/ja-JP.ts` | same keys | 2 / 2 | | `docs/qa/platform-checklist/areas/identity-auth.json` | two items, below | | The card calls the posture string `help`. In the manifest it is actually the specifier's `description`; only the bundles call it `help`. A repo-wide search for each locale's wording found these files and nothing else that repeats the old rule, with one exception listed under Acceptance notes. Three other hits already state the new rule: - the spec `describe`; - the generated reference page; - `content/docs/deployment/self-hosting.mdx`. No `*.generated.ts` bundle holds these strings. ### "No env knob" was measured, and it is false The checklist said the audience is config-only, with no env knob. In fact, the settings service derives an `OS_AUTH_*` override for every auth manifest key (`envKeyOf`, settings-service.types.ts). So the three audience keys have these overrides: - `OS_AUTH_AUDIENCE_POSTURE` - `OS_AUTH_AUDIENCE_ALLOWED_EMAIL_DOMAINS` - `OS_AUTH_AUDIENCE_SELF_REGISTRATION_PERMISSION_SET` **Probe.** A one-off probe, not committed, ran the real `SettingsService` with the shipped `authSettingsManifest`, set the env as shown, and called `getNamespace('auth')`: ```text control, no env: audience_posture = "invite_only" source=default locked=false open + set + off: audience_posture = "open" source=env locked=true audience_self_registration_permission_set = "member_default" source=env locked=true require_email_verification = false source=env locked=true email_domain + list: audience_posture = "email_domain" source=env locked=true audience_allowed_email_domains = "acme.com" source=env locked=true off-vocabulary value: audience_posture = "invite_only" source=default (override logged as IGNORED) ``` **The plugin-auth half is already pinned.** In `audience-posture-setting.test.ts`, the case "open with the OS_AUTH_REQUIRE_EMAIL_VERIFICATION env override OFF is honoured" drives `bindAuthSettings` with env-sourced audience keys and asserts posture `open`. **Composition.** `settings` is in the always-on capability slate for every `serve` preset except `minimal`. **Not measured:** a live stock boot with those variables set. The checklist text says this. ### Checklist: two items, beyond the two quoted sentences The card quotes two `knownGaps` sentences. The same file states the same old rule in seven more fields across two items. These are fixed here as a bounded in-place fix, which all four conditions allow: 1. It is the same defect class. 2. The edit is mechanical, and the rule above fixes its form. 3. The file is on this claim's declared surface. 4. It is the same gate, `check:platform-checklist`. Left alone, those fields would contradict the rewritten gaps. - **`identity-auth.self-signup-gate`, revision 1 to 2:** - `knownGaps[0]` now names both doors: stack config, and the settings namespace through the console or env. It says the env door is measured in halves, not yet end to end. - `knownGaps[1]` (the BOUNDARY note). - `variants[2]`. - `source[2]`. - **`identity-auth.email-verification-loop`, revision 1 to 2:** - `fixtures.requires[0]`. - `acceptance[5]`. - `negative[2]`. As written, it would have scored an `open` boot with the deployment opt-out as a FAIL. That boot correctly advertises `false`. - `source[0]` and `source[1]`. Each item gets a new `history` entry with ref `#20413`. The earlier entries keep their text. I searched the file for nine old-rule phrases, such as `config-only`, `no env knob` and `permitting-posture boot advertising`. On base `40b315b0` they occur 11 times; at head they occur 0 times outside the two new history entries. ## Verification Every run below is at head `a22b90fc`. **Build.** `pnpm --filter '@objectstack/service-settings^...' build` finished with VERDICT command-exit 0, then the package itself was built. **Tests.** `pnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2`: Test Files 33 passed (33), Tests 584 passed (584). This includes: - `settings-translation-coverage.test.ts`, which checks that zh-CN, ja-JP and es-ES cover `settings.auth`; - `auth.manifest.test.ts`. **Typecheck.** `pnpm --filter @objectstack/service-settings run typecheck` (`tsc --noEmit`) exits 0. **Derived gates.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 57 commands, and all 57 were run. Reconciled with `--ran` and exit codes: 57 derived, 56 run, 1 NOT-MEASURED. - **NOT MEASURED: `pnpm check:dual-build-cjs-loads`.** It exited 3 (PREREQUISITE NOT MET) because it needs every package's `dist/`. **Declared narrowing:** I applied the gate's two properties to the one package the diff touches. - `node --check dist/index.cjs` exits 0. - `require('./dist/index.cjs')` loads (46 exports, new text present). - The diff only changes string literals. - **`check-plugin-teardown-shape.mjs --self-test`** first exited 3, because its pinned fixture commit was outside the shallow clone. After I fetched that one commit at depth 1, it passed 48 cases. - **`pnpm check:platform-checklist`:** OK, 15 areas, 266 items. - **`pnpm check:nul-bytes`:** OK. **Roster gates.** The derivation said the rosters of three gates live under `packages/`, so their silence is not evidence either way. I ran them anyway, and all three exit 0: `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`. **Translation gates.** No gate script reads these translation files. A search of `scripts/` and `.github/` for `service-settings/src/translations` and `settingsBuiltinTranslations` finds 0 hits. As a control, the same search surface does find `service-automation`. The only gate on these files is the package's coverage test above. ## Acceptance notes - **Stale boot report text.** `packages/plugins/plugin-auth/src/boot-sign-in-reachability.ts` line 478 still says an `open` or `email_domain` posture "forces email verification on the INVITED login too". Line 492 of the same message already names the `open` opt-out. plugin-auth source is outside this claim, so this goes to the PM as a finding. The phrase is pinned in `boot-sign-in-reachability.test.ts`. - **Stale comment.** A comment in `auth-manager.ts`, near line 2295, still says a self-registration posture forces verification on. It is a comment only. ## Changeset `.changeset/20413-verification-posture-statements.md`: `@objectstack/service-settings` patch, because the strings ship in that package. The `docs/qa` file is not published. ## Seat append (`domain:services` seat #6021, `session_017B6YKCGu8CTY2KBWgwaHAs`) - The first line changed from a closing keyword to `Part of #20413`. The dev's report `5867903998` found one more location of the same stale statement, outside this PR's claim surface: the `no_sign_in_account_at_boot` remedy prose in `packages/plugins/plugin-auth/src/boot-sign-in-reachability.ts`, pinned by its test. By the family rule it joins #20413 as a second round instead of a new card, so this PR no longer closes the card. --- _Generated by [Claude Code](https://claude.ai/code/session_017B6YKCGu8CTY2KBWgwaHAs)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0283cb9 commit 24b7085

7 files changed

Lines changed: 40 additions & 23 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/service-settings": patch
3+
---
4+
5+
The auth settings console states the email-verification rule each audience posture enforces (#20413).
6+
7+
Clause-②: no
8+
9+
The Audience group description and the `audience_posture` help said every posture other than invitation-only forces email verification on. That stopped being true when posture `open` began honouring a deployment's opt-out. Both strings, in the manifest and in the `en`, `zh-CN`, `es-ES` and `ja-JP` bundles, now state the rule the auth plugin enforces:
10+
11+
- `email_domain` always forces email verification on.
12+
- `open` forces it on unless the deployment turns it off, with `OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false` or `emailAndPassword.requireEmailVerification: false` in the stack config.
13+
- A `false` saved in the settings console is refused under `open`.
14+
15+
Text only: no key, option, default or accepted value changes.

‎docs/qa/platform-checklist/areas/identity-auth.json‎

Lines changed: 15 additions & 13 deletions
Large diffs are not rendered by default.

‎packages/services/service-settings/src/manifests/auth.manifest.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const manifest = {
8989
label: 'Audience',
9090
required: false,
9191
description:
92-
'Who may become a user of this environment\'s apps. Postures other than invitation-only force email verification on. Invitations, admin-created users, SCIM provisioning, and enterprise SSO are admitted under every posture.',
92+
'Who may become a user of this environment\'s apps. Allowlisted email domains always force email verification on. Open forces it on unless the deployment turns it off; a value saved in this console cannot. Invitations, admin-created users, SCIM provisioning, and enterprise SSO are admitted under every posture.',
9393
},
9494
{
9595
type: 'select',
@@ -103,7 +103,7 @@ const manifest = {
103103
{ value: 'open', label: 'Open — anyone may self-register' },
104104
],
105105
description:
106-
'invite_only closes self-registration: users come into existence only through an operator-side act (invitation, admin create/import, SCIM, enterprise SSO). email_domain opens it to the allowlisted domains below. open admits anyone. Any posture other than invite_only forces email verification on and requires the self-registration permission set below.',
106+
'invite_only closes self-registration: users come into existence only through an operator-side act (invitation, admin create/import, SCIM, enterprise SSO). email_domain opens it to the allowlisted domains below. open admits anyone. Both email_domain and open require the self-registration permission set below. email_domain always forces email verification on. open forces it on unless the deployment turns it off (OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false, or emailAndPassword.requireEmailVerification: false in the stack config); a false saved in this console is refused under open.',
107107
},
108108
{
109109
type: 'textarea',

‎packages/services/service-settings/src/translations/en.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export const en: PlatformTranslationData = {
141141
},
142142
audience: {
143143
title: 'Audience',
144-
description: 'Who may become a user of this environment\'s apps. Postures other than invitation-only force email verification on. Invitations, admin-created users, SCIM provisioning, and enterprise SSO are admitted under every posture.',
144+
description: 'Who may become a user of this environment\'s apps. Allowlisted email domains always force email verification on. Open forces it on unless the deployment turns it off; a value saved in this console cannot. Invitations, admin-created users, SCIM provisioning, and enterprise SSO are admitted under every posture.',
145145
},
146146
password_policy: {
147147
title: 'Password policy',
@@ -171,7 +171,7 @@ export const en: PlatformTranslationData = {
171171
},
172172
audience_posture: {
173173
label: 'Self-registration audience',
174-
help: 'invite_only closes self-registration: users come into existence only through an operator-side act (invitation, admin create/import, SCIM, enterprise SSO). email_domain opens it to the allowlisted domains below. open admits anyone. Any posture other than invite_only forces email verification on and requires the self-registration permission set below.',
174+
help: 'invite_only closes self-registration: users come into existence only through an operator-side act (invitation, admin create/import, SCIM, enterprise SSO). email_domain opens it to the allowlisted domains below. open admits anyone. Both email_domain and open require the self-registration permission set below. email_domain always forces email verification on. open forces it on unless the deployment turns it off (OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false, or emailAndPassword.requireEmailVerification: false in the stack config); a false saved in this console is refused under open.',
175175
options: {
176176
invite_only: 'Invitation only — no self-registration (default)',
177177
email_domain: 'Allowlisted email domains only',

‎packages/services/service-settings/src/translations/es-ES.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const esES: PlatformTranslationData = {
9191
},
9292
audience: {
9393
title: 'Audiencia',
94-
description: 'Quién puede convertirse en usuario de las aplicaciones de este entorno. Las posturas distintas de «solo por invitación» fuerzan la verificación de correo. Las invitaciones, los usuarios creados por administradores, el aprovisionamiento SCIM y el SSO empresarial se admiten bajo cualquier postura.',
94+
description: 'Quién puede convertirse en usuario de las aplicaciones de este entorno. «Dominios de correo» siempre fuerza la verificación de correo. «Abierto» también la fuerza, salvo que el despliegue la desactive; un valor guardado en esta consola no puede hacerlo. Las invitaciones, los usuarios creados por administradores, el aprovisionamiento SCIM y el SSO empresarial se admiten bajo cualquier postura.',
9595
},
9696
password_policy: {
9797
title: 'Política de contraseñas',
@@ -133,7 +133,7 @@ export const esES: PlatformTranslationData = {
133133
},
134134
audience_posture: {
135135
label: 'Audiencia de autorregistro',
136-
help: '«Solo por invitación» cierra el autorregistro: los usuarios solo se crean mediante un acto del operador (invitación, creación/importación por un administrador, SCIM o SSO empresarial). «Dominios de correo» lo abre solo a los dominios de la lista inferior; «Abierto» admite a cualquiera. Cualquier postura distinta de «solo por invitación» fuerza la verificación de correo y requiere el conjunto de permisos de autorregistro inferior.',
136+
help: '«Solo por invitación» cierra el autorregistro: los usuarios solo se crean mediante un acto del operador (invitación, creación/importación por un administrador, SCIM o SSO empresarial). «Dominios de correo» lo abre solo a los dominios de la lista inferior; «Abierto» admite a cualquiera. «Dominios de correo» y «Abierto» requieren el conjunto de permisos de autorregistro inferior. «Dominios de correo» siempre fuerza la verificación de correo. «Abierto» también la fuerza, salvo que el despliegue la desactive (OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false, o emailAndPassword.requireEmailVerification: false en la configuración del stack); un false guardado en esta consola se rechaza bajo «Abierto».',
137137
options: {
138138
invite_only: 'Solo por invitación — sin autorregistro (predeterminado)',
139139
email_domain: 'Solo dominios de correo permitidos',

‎packages/services/service-settings/src/translations/ja-JP.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export const jaJP: PlatformTranslationData = {
9191
},
9292
audience: {
9393
title: '登録対象',
94-
description: 'この環境のアプリのユーザーになれる対象。「招待のみ」以外のポスチャではメール確認が強制されます。招待、管理者による作成、SCIM プロビジョニング、エンタープライズ SSO はどのポスチャでも許可されます。',
94+
description: 'この環境のアプリのユーザーになれる対象。「メールドメイン」ではメール確認が常に強制されます。「オープン」でも強制されますが、デプロイ側で無効にできます。このコンソールで保存した値では無効にできません。招待、管理者による作成、SCIM プロビジョニング、エンタープライズ SSO はどのポスチャでも許可されます。',
9595
},
9696
password_policy: {
9797
title: 'パスワードポリシー',
@@ -133,7 +133,7 @@ export const jaJP: PlatformTranslationData = {
133133
},
134134
audience_posture: {
135135
label: 'セルフ登録の対象',
136-
help: '「招待のみ」はセルフ登録を閉じます。ユーザーは運用側の行為(招待、管理者による作成/インポート、SCIM、エンタープライズ SSO)によってのみ作成されます。「メールドメイン」は下の許可リストのドメインにのみ開放し、「オープン」は誰でも登録できます。「招待のみ」以外ではメール確認が強制され、下のセルフ登録権限セットの指定が必要です。',
136+
help: '「招待のみ」はセルフ登録を閉じます。ユーザーは運用側の行為(招待、管理者による作成/インポート、SCIM、エンタープライズ SSO)によってのみ作成されます。「メールドメイン」は下の許可リストのドメインにのみ開放し、「オープン」は誰でも登録できます。「メールドメイン」と「オープン」では下のセルフ登録権限セットの指定が必要です。「メールドメイン」ではメール確認が常に強制されます。「オープン」でも強制されますが、デプロイ側で無効にできます(OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false、またはスタック設定の emailAndPassword.requireEmailVerification: false)。このコンソールで保存した false は「オープン」では拒否されます。',
137137
options: {
138138
invite_only: '招待のみ — セルフ登録なし(既定)',
139139
email_domain: '許可リストのメールドメインのみ',

‎packages/services/service-settings/src/translations/zh-CN.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ export const zhCN: PlatformTranslationData = {
257257
},
258258
audience: {
259259
title: '注册受众',
260-
description: '谁可以成为本环境应用的用户。除「仅限邀请」外的口径会强制开启邮箱验证。邀请、管理员创建、SCIM 开通和企业 SSO 在任何口径下都可进入。',
260+
description: '谁可以成为本环境应用的用户。「邮箱域名」口径始终强制开启邮箱验证。「开放」口径也会强制开启,除非部署方关闭它;在此控制台保存的值无法关闭。邀请、管理员创建、SCIM 开通和企业 SSO 在任何口径下都可进入。',
261261
},
262262
password_policy: {
263263
title: '密码策略',
@@ -298,7 +298,7 @@ export const zhCN: PlatformTranslationData = {
298298
},
299299
audience_posture: {
300300
label: '自助注册受众',
301-
help: '「仅限邀请」关闭自助注册:用户只能通过运营侧行为进入——邀请、管理员创建/导入、SCIM 开通或企业 SSO。「邮箱域名」仅向下方允许列表中的域名开放;「开放」允许任何人自助注册。除「仅限邀请」外的口径会强制开启邮箱验证,并要求配置下方的自助注册权限集。',
301+
help: '「仅限邀请」关闭自助注册:用户只能通过运营侧行为进入——邀请、管理员创建/导入、SCIM 开通或企业 SSO。「邮箱域名」仅向下方允许列表中的域名开放;「开放」允许任何人自助注册。「邮箱域名」与「开放」都要求配置下方的自助注册权限集。「邮箱域名」始终强制开启邮箱验证。「开放」也会强制开启,除非部署方关闭它(OS_AUTH_REQUIRE_EMAIL_VERIFICATION=false,或在 stack 配置中设置 emailAndPassword.requireEmailVerification: false);在此控制台保存的 false 在「开放」下会被拒绝。',
302302
options: {
303303
invite_only: '仅限邀请——不开放自助注册(默认)',
304304
email_domain: '仅允许列表中的邮箱域名',

0 commit comments

Comments
 (0)