Repository navigation
Commit 4fe3af9
docs(deployment): branding env vars follow the runtime — OS_PRODUCT_NAME defaults to ObjectOS, six readers added, precedence per posture (#24990)
Fixes #24887
Clause-②: no
## What changes
`content/docs/deployment/environment-variables.mdx` only: the branding
section. Docs follow the implementation. Every default, fallback and
reader below is read off `main`'s code (objectstack `e4d604efd3`, which
this branch carries; console consumers read in objectui at the
`.objectui-sha` pin `cf153eb2e535`). The section heading loses
"(single-tenant)", because under the walled postures these variables are
the console's only branding source. No page or package links the old
`#branding-single-tenant` anchor (`git grep` over `content`, `apps`,
`packages`, `docs` and `skills` finds 0), and `check:doc-anchors` is
green.
| Variable | Old row → new row | Reader (`file#symbol`) | Default |
|:--|:--|:--|:--|
| `OS_PRODUCT_NAME` | Old: default `ObjectStack`, "shown in the UI
chrome and emails". New: names the console only. That is the browser-tab
title (alone, or after the open app's label), the header's product mark,
the sign-in pages, the loading screen and the PWA manifest `name`. It
names no mail and no auth, which point to `OS_APP_NAME`. |
`packages/cloud-connection/src/runtime-config-plugin.ts#RuntimeConfigPlugin.constructor`
(`productName`), served by `#RuntimeConfigPlugin.brandingBlock`.
Console: objectui
`packages/app-shell/src/runtime-config.ts#getProductName`, read by
`apps/console/src/main.tsx`, `apps/console/index.html`,
`layout/ConsoleLayout.tsx`, `layout/AppHeader.tsx#ProductHomeMark`,
`pages/auth/AuthLayout.tsx`, `pages/auth/DeviceAuthPage.tsx` and
`chrome/LoadingScreen.tsx` | `ObjectOS` |
| `OS_PRODUCT_SHORT_NAME` | Old: default `OS`, "favicons, mobile
titles". New: the PWA manifest `short_name`, falling back to the product
name served. | `#RuntimeConfigPlugin.constructor`
(`configuredShortName`), `#RuntimeConfigPlugin.brandingBlock`. Console:
`apps/console/src/main.tsx` into objectui
`packages/mobile/src/pwa.ts#generatePWAManifest` | the product name
served |
| `OS_PRODUCT_STAGE` | Absent → added. `preview` / `beta` / `ga`,
matched exactly. Drives the top-bar chip. Unset sends no `stage` key and
the console keeps `preview`. Any other value gets one `warn` at boot and
is treated as unset. | `#RuntimeConfigPlugin.constructor`,
`#asPlatformStage`, refusal in `#RuntimeConfigPlugin.start`. Console:
objectui `layout/PreviewBadge.tsx#PreviewBadge` | no `stage` key |
| `OS_LOGO_URL` | Absent → added. Sign-in pages, loading screen, header
mark. | `#RuntimeConfigPlugin.constructor` (`logoUrl`), `#brandingBlock`
| none |
| `OS_FAVICON_URL` | Absent → added. Tab icon and PWA manifest icon. An
app's own `branding.favicon` replaces it while that app is open. No
setting outranks it. | `#RuntimeConfigPlugin.constructor`
(`faviconUrl`). Console: `apps/console/index.html`,
`apps/console/src/main.tsx` | none |
| `OS_BRAND_COLOR` | Absent → added. Reaches the console only as
`OS_PWA_THEME_COLOR`'s default. App `AppBranding` colours are separate.
| `#RuntimeConfigPlugin.constructor` (`brandColor`), `#brandingBlock` |
none |
| `OS_PWA_DESCRIPTION` | Absent → added. PWA manifest `description`. |
`#RuntimeConfigPlugin.constructor` (`configuredPwaDescription`),
`#brandingBlock` | the served product name followed by ` — runtime
console` |
| `OS_PWA_THEME_COLOR` | Absent → added. PWA manifest `theme_color`. |
`#RuntimeConfigPlugin.constructor` (`configuredPwaThemeColor`),
`#brandingBlock` | the served brand colour, else `#4f46e5` |
| `OS_APP_NAME` | Old last sentence: "An explicitly set
`branding.workspace_name` setting outranks it in auth." New: true under
`single` only, and the authenticator issuer takes a change only when the
auth instance is rebuilt. Under `group` / `isolated` nothing outranks
it. It never names the console. |
`packages/plugins/plugin-email/src/capability-arg.ts#resolveDeploymentAppName`,
passed in `packages/cli/src/commands/serve.ts` (`new AuthPlugin({
appName })`). Override binding:
`packages/plugins/plugin-auth/src/auth-plugin.ts` (the `brandingOffered`
test before `applyBrand`).
`packages/plugins/plugin-auth/src/auth-manager.ts#getAppName` /
`#setAppName`, and the issuer read in `createAuthInstance` |
`ObjectStack` (unchanged) |
All eight `OS_PRODUCT_*` / `OS_LOGO_URL` / `OS_FAVICON_URL` /
`OS_BRAND_COLOR` / `OS_PWA_*` variables are read **once, in the
constructor**, so a change takes a restart. The page now says so. A
host's own `RuntimeConfigPlugin` option of the same name outranks each
one. The CLI passes none:
`packages/cli/src/commands/serve.ts#Serve.RUNTIME_CONFIG_OPTIONS`
carries `controlPlaneUrl`, `singleEnvironment` and `installLocal` only.
The showcase's own mount passes none either.
## Precedence, per posture (PR #24959, re-read on `main`)
Under `single`, three served fields read the `branding` settings
namespace per request (`#RuntimeConfigPlugin.readSavedBranding`,
`#BRANDING_SETTINGS_KEYS`): `workspace_name` → `productName`, `logo_url`
→ `logoUrl`, `accent_color` → `brandColor`. For each, the first source
that is set wins:
1. the setting: `OS_BRANDING_WORKSPACE_NAME` / `OS_BRANDING_LOGO_URL` /
`OS_BRANDING_ACCENT_COLOR`
(`packages/services/service-settings/src/settings-service.types.ts#envKeyOf`;
it outranks a stored row and locks the field), then a value saved in
Setup → Branding;
2. the host's `RuntimeConfigPlugin` option;
3. `OS_PRODUCT_NAME` / `OS_LOGO_URL` / `OS_BRAND_COLOR`;
4. the built-in default.
A value with `source: 'default'` is skipped, so the Branding manifest's
own `ObjectStack` is never served to the console. A set `workspace_name`
also names sign-in mail, the authenticator issuer and MCP sign-in, ahead
of `OS_APP_NAME`.
Under `group` / `isolated`, `SettingsServicePlugin` holds `branding`
back
(`packages/services/service-settings/src/settings-service-plugin.ts#UNWALLED_ONLY_NAMESPACES`,
`#offerUnwalledOnlyNamespaces`). The namespace is absent from `GET
/api/settings`, `GET` and `PUT` on it answer `404 UNKNOWN_NAMESPACE`,
and Setup lists no Branding entry. The `OS_BRANDING_*` variables reach
no reader. The console's branding is the host option, then the
variables, then the default. The auth product name comes from the
`OS_APP_NAME` chain alone. The migration the changesets state,
`OS_BRANDING_WORKSPACE_NAME=Acme` → `OS_APP_NAME=Acme`, is on the page.
## Live reading
The showcase was booted on this branch's base (`7adfaec2a1`) with `pnpm
dev -- --fresh -p 41873`. `examples/app-showcase` mounts its own
`RuntimeConfigPlugin({ controlPlaneUrl: '', singleEnvironment: true,
installLocal: true })`, with no branding option. None of the eight
variables, `OS_APP_NAME`, `OS_BRANDING_*`, `OS_TENANCY_POSTURE` or
`OS_MULTI_ORG_ENABLED` was set; each was checked set/unset without
printing a value. The posture is `single`.
- `GET /api/v1/runtime/config` answered `200`, with `branding` =
`{"productName":"ObjectOS","productShortName":"ObjectOS","pwaDescription":"ObjectOS
— runtime console","pwaThemeColor":"#4f46e5"}`. There is no `stage`,
`logoUrl`, `faviconUrl` or `brandColor` key.
- `GET /api/settings/branding` (as the seeded admin) answered `200`:
`workspace_name` = `ObjectStack` with `source: default`, and
`accent_color` / `logo_url` = null with `source: default`. So the
namespace is offered under `single`, and its default did not reach the
console.
- The sign-in mail: `POST /api/v1/auth/request-password-reset` for the
seeded admin answered `200`, and its `sys_identity_email` row reads
subject `Reset your ObjectStack password`, status `sent`. The body names
`ObjectStack` once and `ObjectOS` zero times.
That is the two-names reading the card carries: `ObjectOS` in the
console and `ObjectStack` in the mail, on one unconfigured deployment.
## Verification (HEAD `d3a8afd655`)
- Gate families: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 41 commands from this tree. All 41
ran with exit 0, each code captured before any pipe. `--ran` reports:
`41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED`. Among
them: `check:doc-anchors` (563 links resolve), `check:docs-single-h1`,
`check:doc-frontmatter`, `check:docs-redirects`, `check:nul-bytes` (no
raw control bytes), and `@objectstack/spec check:docs` (227 generated
files in sync).
- The page compiles with the docs app's own `@mdx-js/mdx` 3.1.1 +
`remark-gfm` 4.0.1 (exit 0).
- `git merge-tree --write-tree origin/main HEAD` exit 0, tree
`e6b183653f`. The path is not routed to the regen driver.
- No package is touched, so there is no build closure, package test or
typecheck to run. Left to CI and not measured locally: `Build Docs`, the
`Test Core` shards and the type-check lanes.
- `skip-changeset`, measured: no published package's `files[]` names
`content`, `docs` or an mdx glob (73 published packages). A phrase
unique to this diff (`Branding precedence, by tenancy posture`) has 0
hits across the 205 `files[]` roots. Control: `OS_PWA_THEME_COLOR` has 4
hits, in `packages/cloud-connection/dist`.
## Acceptance notes
- Two product names on an unconfigured deployment (`ObjectOS` against
`ObjectStack`) is product behaviour, and the card scopes it out. The
page now states it, and names `branding.workspace_name` under `single`,
or `OS_PRODUCT_NAME` plus `OS_APP_NAME`, as the way to name a deployment
one way everywhere. Carrier: none.
- Empty-string handling differs between the constructor's reads.
`OS_PRODUCT_NAME`, `OS_PRODUCT_SHORT_NAME` and `OS_PRODUCT_STAGE` read
empty as unset. `OS_LOGO_URL`, `OS_FAVICON_URL`, `OS_BRAND_COLOR` and
the `OS_PWA_*` pair forward `''`, because `??` keeps it. The console's
`||` getters then fall back, so nothing visible changes. This is a
read-only observation and is not filed. The page claims "empty reads as
unset" for the product name only.
- No other page in `content/docs` states these defaults. objectui's
`packages/app-shell/README.md` names `OS_PRODUCT_STAGE` consistently
with this page.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent fde7456 commit 4fe3af9
1 file changed
Lines changed: 19 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
129 | 129 | | |
130 | 130 | | |
131 | 131 | | |
132 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
133 | 135 | | |
134 | 136 | | |
135 | 137 | | |
136 | | - | |
137 | | - | |
138 | | - | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
139 | 154 | | |
140 | 155 | | |
141 | 156 | | |
| |||
0 commit comments