Skip to content

Commit 5448585

Browse files
fix(service-settings): Settings help in business words; Country is a picker that shows names and stores the code (#28063)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} --> _Requested by **Elon** · [project thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DHDem82MKCs3q2zQkEAkpqZ)_ Fixes #27853 Fixes #27867 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:设置页(认证、功能开关、邮件)的帮助文字改成业务用语,不再出现环境变量名、选项键、错误码、对象 API 名或代码调用;继承值徽标从「租户」改为「组织」;Webhooks 副标题改写;公司「国家」与本地化「默认国家」改为显示国名、存储代码的选择器。四个语言包同步。 - **为什么改**:管理员在 Setup → Settings 看到的是像代码注释的说明(`invite_only`、`OS_AUTH_REQUIRE_EMAIL_VERIFICATION`、`defineWebhook()`),国家要手打两位大写代码,输入「de」或「Germany」都被拒且没有提示。 - **风险与回滚**:低。只改文字与国家字段的控件类型;国家仍以 `iso_3166_alpha2` 值域校验,接受的值集合不变(`DE` 照收,`de`、`ZZ` 照拒)。回滚 = revert 本 PR。 - **先看**:`packages/services/service-settings/src/manifests/company.manifest.ts`(国家选项从共享的 249 个代码生成);`packages/services/service-settings/src/manifests/auth.manifest.ts`(帮助改写);`packages/services/service-settings/src/settings-help-business-words.pin.test.ts`(形状钉子)。 - **两分钟验证**:`pnpm --filter @objectstack/service-settings exec vitest run src/settings-help-business-words.pin.test.ts src/manifests/country-picker.pin.test.ts`,预期全绿;或打开 Setup → Company,在「国家」输入 Germ,列表出现「Germany (DE)」,选中后保存为 DE。 - **需要你做的**:无,席位落地。 ## What changes **#27853 — Settings help in business words.** Every string an administrator is shown for Authentication, Feature Flags and Mail (the manifest literals, which are the English fallback, and the en / zh-CN / ja-JP / es-ES packs) now names the effect instead of the machine value: - Self-registration audience: the help says Invitation only / Allowlisted email domains / Open (the labels the page shows), not the stored option keys. The email-verification override, the Google client ID and secret, and the reverse-proxy trust switch are described as "set in the server configuration" instead of by env var name or stack-config path. The API-key help no longer quotes error codes; the permission-set help no longer quotes permission-set API names. - Feature Flags beta banner: no env var, no code span. - Mail: the durable-queue help no longer names the queue object. The Send-test-email result for a log-only provider (`packages/plugins/plugin-email/src/email-plugin.ts`, the two message strings only) says "kept in the email log" instead of naming the mail object. - The inherited-value badge (`settingsCommon.sourceLabels.tenant`) reads Organization / 组织 / 組織 / Organización. - The Webhooks subtitle (`sys_webhook` description, `packages/plugins/plugin-webhooks/src/sys-webhook.object.ts`) says what a webhook is without the code calls or the internal component names; the code-level account stays in the comment beside it. The en bundle is regenerated with `node scripts/check-i18n-bundles.mjs --write`; the zh-CN / ja-JP / es-ES values are hand-translated leaves. The technical detail the help used to inline is already in the deployment docs (`content/docs/deployment/environment-variables.mdx` names the email-verification override). The settings manifest has no per-field docs-link slot, so the help now carries no technical detail rather than a link. **#27867 — Country pickers.** Company → Country and Localization → Default country change from `text` to `select`. Their `options` are every member of the `iso_3166_alpha2` value domain, generated from the shared `ISO_3166_ALPHA2_CODES` (`@objectstack/spec/shared`) with names from `Intl.DisplayNames`, never a hand-kept list; each locale pack names the same 249 codes in its own language through `countryOptionLabels(locale)`. The `valueDomain` declaration stays, so it remains the enforcement boundary and the accepted set is unchanged. The console renders a domain-bearing `select` as its suggest-and-pick combobox: typing a name lists the named options, and picking one stores the code. `pattern` / `minLength` / `maxLength` stay on the specifier, so the server's verdict for every value is the same as before. **Premise correction for #27867.** Company has no Locale or Timezone setting; it has only Country. Locale and Timezone live on Localization, where `locale` is already a closed picker with language names and `timezone` is the suggest-any-IANA-zone combobox with named suggestions, so neither changes here. Localization's `default_country` had the same two-letter free-text shape as Company's country and gets the same picker (file surface amended on the claim). ## Tests - `src/settings-help-business-words.pin.test.ts` (new): for auth, feature_flags and mail, the manifest literals and every shipped pack carry no env var or error code, enum key or object API name, camelCase code identifier, code call or code span; a teeth case proves each shape catches the string it was written for. Every pack's tenant source label is not "tenant" in any of the four languages. - `src/manifests/country-picker.pin.test.ts` (new): both keys are `select` with `valueDomain: 'iso_3166_alpha2'` after the spec parse; their options equal the 249 domain codes, each named with its code; a stored `DE` round-trips through `SettingsService` and is one of the options; `ZZ` and `de` are still refused with `value_domain` and `Germany` is still refused; each pack names all 249 codes. - `packages/plugins/plugin-webhooks/src/sys-webhook.description.pin.test.ts` (new): the source description and the four bundles carry no code call, camelCase identifier, object API name or internal component name. - `src/manifests/auth.manifest.test.ts`: the two pins that held the old wording now hold the business wording (the sign-up help names each posture by its label and spells no key; the Google help names the server configuration and no env var). ## Acceptance notes - The console shows a stored country code (`DE`) in the combobox text box, with the named options in its list. Showing the stored code as its name inside the box is a `ui/` change to `DomainCombobox` (`ui/apps/console/src/pages/settings/SettingsField.tsx`), outside this PR's file surface; reported to the seat for a follow-up card. - The console's own fallback for the source badge (`ui/apps/console/src/pages/settings/useSettingsLabel.ts`, used only when no server bundle loads) still reads "Tenant"; outside this file surface. - AI & Embedder help still names env vars and `sys_ai_trace` (parent row 2 lists it); not in this card's three namespaces; reported to the seat. ## Verification All readings at head `b7cfa217` (base `origin/main` `5aee2065`). <details><summary>Package tests and typecheck (under the shared verify lock)</summary> - `@objectstack/service-settings`: Test Files 64 passed (64), Tests 1053 passed (1053); `typecheck` exit 0. - `@objectstack/plugin-webhooks`: Test Files 25 passed (25), Tests 258 passed (258); `typecheck` exit 0. - `@objectstack/plugin-email`: Test Files 45 passed (45), Tests 749 passed (749); `typecheck` exit 0. </details> <details><summary>Ablations (scripts/ablation-replace.mjs, restored by blob hash)</summary> - `company.manifest.ts` country `type: 'select'` mutated to `'text'`: `country-picker.pin.test.ts` 1 failed / 16 passed (company.country is a select that keeps the iso_3166_alpha2 domain); restored blob `0ee79213` matches HEAD. - en pack Feature Flags banner given `OS_FEATURE_FLAGS_AI_ENABLED`: `settings-help-business-words.pin.test.ts` 1 failed / 19 passed (feature_flags: the en pack carries no developer-only value); restored blob `eaba9945` matches HEAD. </details> <details><summary>Gate families (dispatch-gates --commands, reconciled with --ran)</summary> - 62 families derived; 61 run with exit 0; 1 NOT MEASURED: `pnpm check:dual-build-cjs-loads` exits 3 (PREREQUISITE NOT MET: needs every package's dist), left to CI. - `check-plugin-teardown-shape --self-test` first exited 3 in the shallow clone; after fetching its pinned fixture commit it exits 0 (48 cases). - `check:i18n` exit 0 after `node scripts/check-i18n-bundles.mjs --write` (only `plugin-webhooks` en bundle moved). - `check:dts-closure` and `check:sourcemap-no-sources-content` swept the 62 built packages only; CI sweeps the whole tree. - `pnpm lint` exit 0. - Reconcile: `62 derived famil(ies) accounted for — 61 run, 1 NOT-MEASURED`. </details> Not browser-driven: the combobox rendering was read from `SettingsField.tsx`, not re-driven in Chromium. --- _Generated by [Claude Code](https://claude.ai/code/session_016WV4d1MApr9y6yjfjdVYmA)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent f6adf48 commit 5448585

20 files changed

Lines changed: 479 additions & 81 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/service-settings': patch
3+
'@objectstack/plugin-webhooks': patch
4+
'@objectstack/plugin-email': patch
5+
---
6+
7+
fix(service-settings): Setup → Settings help says what a setting does in business words, and Country is a picker that shows names and stores the code
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
An administrator in Setup → Settings read help written like code comments. It now names the effect, and the technical detail stays in the deployment docs.
13+
14+
- **Authentication.** The help names the Self-registration audience by the labels the page shows (Invitation only, Allowlisted email domains, Open), not by their stored keys. It no longer quotes env var names (the email-verification override, the Google client ID and secret), the stack-config paths, the API-key error codes or permission-set API names; it says "set in the server configuration" instead.
15+
- **Feature Flags.** The beta banner says a feature set in the server configuration is locked for the whole deployment, without quoting an env var.
16+
- **Mail.** The durable-queue help no longer names the queue's object, and the Send-test-email result for a log-only provider says the message was "kept in the email log" (`@objectstack/plugin-email`).
17+
- **Webhooks.** The `sys_webhook` description, shown as the Webhooks subtitle, says what a webhook is without `defineStack` / `defineWebhook()` or the auto-enqueuer (`@objectstack/plugin-webhooks`).
18+
- **The inherited-value badge** names the layer "Organization" (组织 / 組織 / Organización) instead of "Tenant".
19+
- **Country pickers.** Company → Country and Localization → Default country are `select`s whose options are every ISO 3166-1 alpha-2 code with its name, in each shipped locale (`Germany (DE)`, `德国 (DE)`). The stored value is still the code, a stored code is one of the options, and the `iso_3166_alpha2` value domain stays the enforcement boundary, so the set of accepted values is unchanged. The names come from `Intl.DisplayNames`; the codes come from the shared `ISO_3166_ALPHA2_CODES`.
20+
21+
Every shipped pack (en, zh-CN, ja-JP, es-ES) carries the same wording.

‎packages/plugins/plugin-email/src/email-plugin.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,7 +807,7 @@ export class EmailServicePlugin implements Plugin {
807807
return {
808808
ok: false,
809809
severity: 'warning',
810-
message: 'No delivery transport is active — the message was only logged and recorded in sys_email. '
810+
message: 'No delivery transport is active — the message was only logged and kept in the email log. '
811811
+ 'Configure an SMTP host (or an API provider) and save before testing.',
812812
};
813813
}
@@ -819,7 +819,7 @@ export class EmailServicePlugin implements Plugin {
819819
ok: false,
820820
severity: 'warning',
821821
message: 'Nothing was delivered — the provider on the form is "None (log only)", so the test message '
822-
+ 'was only logged and recorded in sys_email; no mail left this machine. The live transport was '
822+
+ 'was only logged and kept in the email log; no mail left this machine. The live transport was '
823823
+ `not used (${describeLiveMailTransport(svc).label}); save the form to stop delivery.`,
824824
};
825825
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* The Webhooks subtitle in Setup is the `sys_webhook` object description, and
5+
* an administrator reads it (#27853). It used to explain the object in code —
6+
* `defineStack({ webhooks })` / `defineWebhook()`, the "webhook auto-enqueuer",
7+
* the "HTTP outbox". The source description and every shipped locale's copy
8+
* now say it in business words; the code-level account lives in the comment
9+
* beside the description in `sys-webhook.object.ts`.
10+
*
11+
* Pins SHAPES, never wording.
12+
*/
13+
14+
import { describe, it, expect } from 'vitest';
15+
import { SysWebhook } from './sys-webhook.object.js';
16+
import { enObjects } from './translations/en.objects.generated.js';
17+
import { zhCNObjects } from './translations/zh-CN.objects.generated.js';
18+
import { jaJPObjects } from './translations/ja-JP.objects.generated.js';
19+
import { esESObjects } from './translations/es-ES.objects.generated.js';
20+
21+
const DEVELOPER_SHAPES: Array<[string, RegExp]> = [
22+
['code call', /\b[A-Za-z_$][\w$]*\(\s*[\w{}\s,]*\)/],
23+
['camelCase code identifier', /\b[a-z][a-z0-9]*[A-Z][A-Za-z0-9]*\b/],
24+
['object API name', /\b[a-z][a-z0-9]*_[a-z0-9_]*[a-z0-9]\b/],
25+
['internal component', /auto-enqueuer|outbox/i],
26+
];
27+
28+
const shapesIn = (text: string) =>
29+
DEVELOPER_SHAPES.filter(([, re]) => re.test(text)).map(([name]) => name);
30+
31+
describe('sys_webhook description reads as business words (#27853)', () => {
32+
it('the sweep has teeth on the old subtitle', () => {
33+
expect(
34+
shapesIn('declared in code via defineStack({ webhooks }) / defineWebhook() by the webhook auto-enqueuer'),
35+
).toEqual(['code call', 'camelCase code identifier', 'internal component']);
36+
});
37+
38+
it('the source description carries no code', () => {
39+
expect(typeof SysWebhook.description).toBe('string');
40+
expect(shapesIn(SysWebhook.description as string)).toEqual([]);
41+
});
42+
43+
for (const [locale, objects] of [
44+
['en', enObjects],
45+
['zh-CN', zhCNObjects],
46+
['ja-JP', jaJPObjects],
47+
['es-ES', esESObjects],
48+
] as const) {
49+
it(`the ${locale} bundle's description carries no code`, () => {
50+
const description = (objects as any).sys_webhook?.description;
51+
expect(typeof description).toBe('string');
52+
expect(shapesIn(description)).toEqual([]);
53+
});
54+
}
55+
});

‎packages/plugins/plugin-webhooks/src/sys-webhook.object.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ export const SysWebhook = ObjectSchema.create({
5555
userActions: { create: true, edit: true, delete: true, import: false },
5656
// [ADR-0131 D2] A row is an organization's own subscription; a declared
5757
// webhook is read from the metadata registry (`declared-webhooks.ts`) and
58-
// writes no row. The description says so to every Setup/Studio reader.
59-
description: 'Outbound HTTP webhook subscription authored by an organization in the Studio editor or through the API. A webhook declared in code via defineStack({ webhooks }) / defineWebhook() is read from the metadata registry and writes no row here. Both are dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.',
58+
// writes no row. The description says so to every Setup/Studio reader, in
59+
// an administrator's words: a webhook declared in code (`defineStack({
60+
// webhooks })` / `defineWebhook()`) is the one "built into an app", and the
61+
// "delivery queue" is the auto-enqueuer's shared HTTP outbox.
62+
description: 'An outbound HTTP webhook your organization creates here or through the API. Webhooks that come built into an app are not listed here. Both kinds are sent through the same delivery queue.',
6063
displayNameField: 'name',
6164
nameField: 'name', // [ADR-0079] canonical primary-title pointer (mirrors deprecated displayNameField)
6265
titleFormat: '{label}',

‎packages/plugins/plugin-webhooks/src/translations/en.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
1818
sys_webhook: {
1919
label: "Webhook",
2020
pluralLabel: "Webhooks",
21-
description: "Outbound HTTP webhook subscription authored by an organization in the Studio editor or through the API. A webhook declared in code via defineStack({ webhooks }) / defineWebhook() is read from the metadata registry and writes no row here. Both are dispatched by the webhook auto-enqueuer onto the shared HTTP outbox.",
21+
description: "An outbound HTTP webhook your organization creates here or through the API. Webhooks that come built into an app are not listed here. Both kinds are sent through the same delivery queue.",
2222
fields: {
2323
id: {
2424
label: "Webhook ID"

‎packages/plugins/plugin-webhooks/src/translations/es-ES.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const esESObjects: NonNullable<TranslationData['objects']> = {
1818
sys_webhook: {
1919
label: "Webhook",
2020
pluralLabel: "Webhooks",
21-
description: "Suscripción saliente de Webhook HTTP creada por una organización con el editor de Studio o mediante la API. Un webhook declarado en código mediante defineStack({ webhooks }) / defineWebhook() se lee del registro de metadatos y no escribe ninguna fila aquí. Ambos los despacha el encolador automático de webhooks al outbox HTTP compartido.",
21+
description: "Webhook HTTP saliente que su organización crea aquí o mediante la API. Los webhooks incluidos en una aplicación no aparecen aquí. Ambos tipos se envían por la misma cola de entrega.",
2222
fields: {
2323
id: {
2424
label: "ID de webhook"

‎packages/plugins/plugin-webhooks/src/translations/ja-JP.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const jaJPObjects: NonNullable<TranslationData['objects']> = {
1818
sys_webhook: {
1919
label: "ウェブフック",
2020
pluralLabel: "ウェブフック",
21-
description: "送信 HTTP Webhook サブスクリプション。組織がスタジオエディタまたは API で作成します。コードで defineStack({ webhooks }) / defineWebhook() により宣言した Webhook はメタデータレジストリから読み取られ、ここに行を書き込みません。どちらも Webhook 自動エンキューアが共有 HTTP アウトボックスへディスパッチします。",
21+
description: "組織がここまたは API で作成する送信 HTTP Webhook。アプリに組み込まれた Webhook はここには表示されません。どちらも同じ配信キューを通じて送信されます。",
2222
fields: {
2323
id: {
2424
label: "Webhook ID"

‎packages/plugins/plugin-webhooks/src/translations/zh-CN.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const zhCNObjects: NonNullable<TranslationData['objects']> = {
1818
sys_webhook: {
1919
label: "Webhook",
2020
pluralLabel: "Webhook",
21-
description: "外发 HTTP Webhook 订阅,由组织在 Studio 编辑器中或通过 API 编写。在代码中通过 defineStack({ webhooks }) / defineWebhook() 声明的 Webhook 从元数据注册表读取,不会在此写入数据行。两者均由 Webhook 自动入队器分发到共享的 HTTP 发件箱。",
21+
description: "由您的组织在此处或通过 API 创建的外发 HTTP Webhook。应用内置的 Webhook 不在此列出。两者都通过同一个投递队列发送。",
2222
fields: {
2323
id: {
2424
label: "Webhook ID"

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,28 +244,35 @@ describe('authSettingsManifest', () => {
244244
expect(signup.visible).toBe('${data.email_password_enabled !== false}');
245245

246246
// The help names every posture the select declares, read from the option
247-
// table, so a posture added later reds here until the help covers it.
247+
// table, so a posture added later reds here until the help covers it. It
248+
// names each one the way the page shows it, by the option's label, never
249+
// by the stored key (#27853).
248250
expect(typeof signup.description).toBe('string');
249-
for (const value of posture.options.map((o: any) => o.value)) {
250-
expect(signup.description, `signup_enabled help names posture ${value}`).toContain(value);
251+
for (const option of posture.options as Array<{ value: string; label: string }>) {
252+
const shown = option.label.split(' — ')[0].replace(/ only$/, '');
253+
expect(signup.description, `signup_enabled help names posture ${option.value}`).toContain(shown);
254+
expect(signup.description, `signup_enabled help spells no key ${option.value}`).not.toMatch(
255+
new RegExp(`\\b${option.value}\\b`),
256+
);
251257
}
252258
expect(signup.description).toContain(posture.label);
253259
});
254260

255261
// plugin-auth registers Google only when the toggle is on AND a client ID and
256262
// secret both resolve, from the stored keys or from GOOGLE_CLIENT_ID /
257263
// GOOGLE_CLIENT_SECRET, which the settings service cannot see. The page can
258-
// only say so; the help is where it does.
259-
it('google_enabled keeps the runtime default and names the env credentials that complete it', () => {
264+
// only say so; the help is where it does — in an administrator's words, the
265+
// server configuration, not the env var names (#27853).
266+
it('google_enabled keeps the runtime default and says the server configuration completes it', () => {
260267
const specs = authSettingsManifest.specifiers as any[];
261268
const google = specs.find((s) => s.key === 'google_enabled');
262269

263270
// `true` is what the runtime assumes for an undeclared toggle; a `false`
264271
// default would read OFF on a deployment that signs in with Google through
265272
// the env credentials, and hide its stored credentials behind `visible`.
266273
expect(google.default).toBe(true);
267-
expect(google.description).toContain('GOOGLE_CLIENT_ID');
268-
expect(google.description).toContain('GOOGLE_CLIENT_SECRET');
274+
expect(google.description).toContain('server configuration');
275+
expect(google.description).not.toContain('GOOGLE_CLIENT_');
269276
});
270277

271278
// The console resolves `settings.auth.keys.<key>.label` / `.help` from the

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const manifest = {
4545
required: false,
4646
default: true,
4747
description:
48-
'Opens the email-and-password sign-up form. Who it admits is decided by the Self-registration audience below: under invite_only, only an address holding a pending invitation; under email_domain, the allowlisted domains and invited addresses; under open, anyone. Off closes the form under every audience, invitations included.',
48+
'Opens the email-and-password sign-up form. Who it admits is decided by the Self-registration audience below: under Invitation only, only an address holding a pending invitation; under Allowlisted email domains, the allowlisted domains and invited addresses; under Open, anyone. Off closes the form under every audience, invitations included.',
4949
visible: "${data.email_password_enabled !== false}",
5050
},
5151
{
@@ -113,7 +113,7 @@ const manifest = {
113113
{ value: 'open', label: 'Open — anyone may self-register' },
114114
],
115115
description:
116-
'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.',
116+
'Invitation only closes self-registration: people become users only when an administrator invites, creates or imports them, or through directory provisioning (SCIM) or enterprise single sign-on. Allowlisted email domains opens it to the domains listed below. Open admits anyone. Both Allowlisted email domains and Open need the self-registration permission set below. Allowlisted email domains always requires email verification. Open requires it unless the server configuration turns it off; turning it off in this console is refused under Open.',
117117
},
118118
{
119119
type: 'textarea',
@@ -130,7 +130,7 @@ const manifest = {
130130
label: 'Self-registration permission set',
131131
required: false,
132132
description:
133-
'Name of the permission set granted to each self-registrant (declaring member_default explicitly is fine; admin_full_access is refused). Required whenever the posture permits self-registration.',
133+
'Name of the permission set each self-registered user receives. The standard member set is fine; the full administrator set is refused. Required whenever the audience allows self-registration.',
134134
visible: "${data.audience_posture === 'email_domain' || data.audience_posture === 'open'}",
135135
},
136136

@@ -369,7 +369,7 @@ const manifest = {
369369
label: 'Allowed IP ranges',
370370
required: false,
371371
description:
372-
'CIDR ranges or exact IPs (one per line, or comma-separated), e.g. 203.0.113.0/24. When set, sign-in from outside these ranges is rejected, and so is any request whose client address the server cannot establish. Empty = no restriction. The address judged is the connection\'s peer; behind a reverse proxy, declare server.trustProxy in the stack so the forwarded client address is judged instead.',
372+
'CIDR ranges or exact IPs (one per line, or comma-separated), e.g. 203.0.113.0/24. When set, sign-in from outside these ranges is rejected, and so is any request whose client address the server cannot establish. Empty = no restriction. The address judged is the one the connection comes from; behind a reverse proxy, the server must be configured to trust that proxy so the original client address is judged instead.',
373373
},
374374
// [#23367, ruling A] The API-key lifecycle policy. Two readers, one
375375
// source: `bindAuthSettings` binds these into the auth service
@@ -405,7 +405,7 @@ const manifest = {
405405
min: 1,
406406
max: 3650,
407407
description:
408-
'The furthest expiry date a new key may ask for, counted from its creation. A longer one is refused (API_KEY_EXPIRY_TOO_LONG); exactly this many days is accepted.',
408+
'The furthest expiry date a new key may ask for, counted from its creation. A longer one is refused; exactly this many days is accepted.',
409409
},
410410
{
411411
type: 'number',
@@ -416,7 +416,7 @@ const manifest = {
416416
min: 0,
417417
max: 3650,
418418
description:
419-
'A key with no recorded use for this many days is refused (API_KEY_INACTIVE). It is not revoked; create a new key. A key never used counts from its creation, or from the policy start below when it is older. 0 turns inactivity expiry off.',
419+
'A key with no recorded use for this many days is refused. It is not revoked; create a new key. A key never used counts from its creation, or from the policy start below when it is older. 0 turns inactivity expiry off.',
420420
},
421421
{
422422
type: 'text',
@@ -434,7 +434,7 @@ const manifest = {
434434
label: 'Social sign-in',
435435
required: false,
436436
description:
437-
'Configure the built-in Google sign-in provider. Deployment env vars still win.',
437+
'Configure the built-in Google sign-in provider. Credentials set in the server configuration still win.',
438438
},
439439
// Google is offered only when this is on AND a client ID and secret both
440440
// resolve (saved here, or GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, which
@@ -458,15 +458,15 @@ const manifest = {
458458
required: false,
459459
default: true,
460460
description:
461-
'Offers Google sign-in once a client ID and a client secret both resolve: saved below, or GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET on the server, which win over saved values. Until both resolve, the login page shows no Google button, even while this is on. Off withdraws Google sign-in even when the server sets both. Create the OAuth client in Google Cloud Console.',
461+
'Offers Google sign-in once a client ID and a client secret are both available: saved below, or set in the server configuration, which wins over saved values. Until both are available, the login page shows no Google button, even while this is on. Off withdraws Google sign-in even when the server sets both. Create the OAuth client in Google Cloud Console.',
462462
},
463463
{
464464
type: 'text',
465465
key: 'google_client_id',
466466
label: 'Google client ID',
467467
required: false,
468468
description:
469-
'OAuth client ID from Google Cloud Console. GOOGLE_CLIENT_ID can also be set on the server.',
469+
'OAuth client ID from Google Cloud Console. It can also be set in the server configuration.',
470470
visible: "${data.google_enabled !== false}",
471471
},
472472
{
@@ -476,7 +476,7 @@ const manifest = {
476476
required: false,
477477
encrypted: true,
478478
description:
479-
'Stored encrypted at rest. GOOGLE_CLIENT_SECRET can also be set on the server.',
479+
'Stored encrypted at rest. It can also be set in the server configuration.',
480480
visible: "${data.google_enabled !== false}",
481481
},
482482

0 commit comments

Comments
 (0)