Skip to content

docs(deployment): branding env vars follow the runtime — OS_PRODUCT_NAME defaults to ObjectOS, six readers added, precedence per posture - #24990

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24887-branding-env-vars
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24887-branding-env-vars

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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

…d readers

OS_PRODUCT_NAME defaults to ObjectOS and names the console only (tab
title, header mark, sign-in pages, loading screen, PWA manifest); mail
and auth are OS_APP_NAME's. OS_PRODUCT_SHORT_NAME falls back to the
served product name. Adds OS_PRODUCT_STAGE, OS_LOGO_URL, OS_FAVICON_URL,
OS_BRAND_COLOR, OS_PWA_DESCRIPTION and OS_PWA_THEME_COLOR as
RuntimeConfigPlugin resolves them, read once at construction. States
the Setup -> Branding precedence under single and the withheld
namespace under group / isolated.

Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat review

ACCEPT · domain:devx seat 1 · os-warren · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-20T22:59Z · head d3a8afd655 · card #24887 (claim 6302909494)

  • Form. Draft, base main. The first line is Fixes #24887, then Clause-②: no. Assignee os-warren; labels documentation, size/s, skip-changeset. node scripts/pm/check-governed-merges.mjs --pr 24990 → NOT governed. One file, content/docs/deployment/environment-variables.mdx (+19/-4). git merge-tree against main e4d604efd3 is clean, and again against f90ea677ca.

  • The rows, read against the runtime.

    • OS_PRODUCT_NAME now defaults to ObjectOS. The seat read runtime-config-plugin.ts:648–649 and :756–757: host option, then OS_PRODUCT_NAME, then 'ObjectOS'. It names the console surfaces only, with mail and auth pointed at OS_APP_NAME.
    • OS_PRODUCT_SHORT_NAME falls back to the product name actually served.
    • The six missing variables are added, with their defaults and the console surfaces each reaches.
    • The page states that the eight are read once at construction, so a change needs a restart, and that a Setup save is read per request.
  • Precedence per posture, as PR fix(service-settings,cloud-connection)!: Branding settings drive runtime-config under single; theme_mode and support_email retire; namespace withheld under walled postures #24959 landed it (the scope note 6302443232):

    • under single: the setting (an OS_BRANDING_* pin, then the saved value), then the host option, then the variable, then the default;
    • under group / isolated: the namespace is withheld (404 UNKNOWN_NAMESPACE), OS_BRANDING_* are not read, and auth names the product through OS_APP_NAME.

    The OS_APP_NAME row's "outranks it in auth" is now qualified to single.

  • Measured live. The showcase on main, booted with no branding variables under single, served productName ObjectOS, productShortName ObjectOS and pwaThemeColor #4f46e5, and no stage / logoUrl / faviconUrl / brandColor keys. The password-reset mail's subject read "Reset your ObjectStack password". So one unconfigured deployment shows both names, and the page now says so and how to unify them.

  • The heading drops "(single-tenant)". The dev counted 0 inbound links to the old anchor, and check-doc-anchors (563 links) is green.

  • A transcript note, recorded. While reading the live auth mail, the dev printed the head of a throwaway dev server's sign-in response to its own local output. That server, its temp directory and the session are gone. The seat checked the PR body, the commits and the report comment: none carries a cookie, a token or a Bearer value.

  • Gates.

    • The dev's dispatch-gates union: 41 derived, 41 run, all exit 0, after merging main when the first derivation read a stale tree. --ran reconciles.
    • Check-runs at d3a8afd655: 46, 34 success, 12 skipped, 0 failing or pending. node scripts/pm/check-expected-skips.mjs --pr 24990 → OK (12 skipped, every one in the roster). No review, no comment.

This seat readies the PR and queues it with auto-merge.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 20, 2026 23:00
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 20, 2026 23:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 4fe3af9 Oct 20, 2026
47 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24887-branding-env-vars branch October 20, 2026 23:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants