refactor: derive hosts and display name from config
Re-opens #8494, which was merged before review and then reverted in #8509. The commit is a git revert of the #8509 revert, so the code is #8494's as merged. It includes the four review fixes from c8927d8781:
env.main.tsderives its URL defaults without importing@superset/shared, soelectron-vite build/devwork.- The cookie import lowercases configured domains.
- The About/Hide/Quit labels are translated in all locales.
- The mobile
app.config.tsfallback is read afterconfig()loads the root.env.
Since #8494, #8354 moved billing code out of packages/auth/src/server.ts into packages/auth/src/lib/billing/. Those files use NOREPLY_FROM and COMPANY.SUPPORT_EMAIL again, as on main before #8509.
Checks on this branch: bun run lint and bun run check:i18n pass. The auth, shared and email suites and the desktop display-name and cookie-import tests pass. Typecheck gives the same local better-auth errors as main, and none is new. Every file that #8494 changed and no later PR touched is byte-identical to the #8494 merge.
The original description follows. Its test results are from #8494.
What
Hosts, support addresses, the transactional sender and the desktop display name now come from config instead of literals. A later domain or display-name change then needs only env vars plus a text swap.
No behaviour change by default. With no new env var set, every derived host, URL, address and display name equals today's literal. A test checks this.
This supersedes #8360 and #8379 (both closed). I re-applied both on today's main instead of resolving the old conflicts, and routed literals added since then through the same constants: the CLI SUPERSET_API_URL/SUPERSET_WEB_URL defaults, the hosts add install URL, and the email preview links.
Hosts (from #8360)
@superset/sharedCANONICAL_URLS(MARKETING,API,APP,DOCS) are built fromNEXT_PUBLIC_ROOT_DOMAIN, orEXPO_PUBLIC_ROOT_DOMAINon mobile.- They ignore
NEXT_PUBLIC_MARKETING_URL/NEXT_PUBLIC_DOCS_URL, so preview and local deployments keep printing production URLs as they do today. - New
COMPANY.SUPPORT_EMAIL;MAIL_TOis built from it. IOS_APPand the root domain move toapp-identity.ts, which is now the only shared fileapps/mobile/app.config.tsloads.
@superset/email/senderNOREPLY_FROMis built fromEMAIL_SENDING_DOMAIN(defaultsuperset.sh).- Every transactional
from:uses it: auth, support, marketing forms and YC deals. - It is separate from the root domain on purpose. A new sending domain needs weeks of warm-up, so mail can stay on the old domain after the root domain changes.
- Call sites that now use the constants:
- API: discovery, OpenAPI, MCP, OAuth metadata, Slack links, connector
client_uri - Docs:
llms.txt, MCP instructions, source links - Marketing:
next.config(API origin,/mcpredirect,Linkheader), install commands,llms, contact/enterprise inbox - Desktop: docs links, billing mailtos, sample prompts
- Web: billing mailto
- Mobile: terms, privacy, share URL, web URL default
- CLI: hints, docs URL
- Email templates
- API: discovery, OpenAPI, MCP, OAuth metadata, Slack links, connector
- Desktop build
defineEnvfalls back when a value is empty, but only if a fallback is given. Unset secrets reach the build as empty strings.- Vite defines, env defaults and the renderer CSP API origin are built from the root domain.
- Cookie import protects both the configured domain and
superset.sh.
- CLI build bakes in
NEXT_PUBLIC_ROOT_DOMAIN. SettingSUPERSET_API_URL/SUPERSET_WEB_URLat runtime still overrides it. - Env plumbing
NEXT_PUBLIC_ROOT_DOMAINgoes to the api/web/marketing/docs deploys (production and preview),build-desktop.ymlandbuild-cli.yml.EMAIL_SENDING_DOMAINgoes to every deploy that hasRESEND_API_KEY.RELAY_BACKUP_URLgoes to the web deploy:next.configalready read it, but nothing set it..env.example,.env.local.example(empty, so the vars stay unset) andturbo.jsoncare updated.- No secret needs to be set now.
Display name (from #8379)
getAppDisplayName()replaces the first word ofapp.namewithCOMPANY.NAMEand keeps the suffix ("Canary", "(workspace)").- The macOS app menu, About/Hide/Quit labels, About panel and notification registration use it.
CFBundleName,CFBundleDisplayNameand the Linux desktop entryName=come from adisplayNamevalue in the electron-builder configs. Canary overridesName=so it keeps "Superset Canary".- These stay unchanged:
productName(userData, logs, sessionData, keychain entry, updater bundle and Windows install folder all come from it), appId, thesuperset://scheme, the CLI binary,~/.supersetand theSUPERSET_*env vars.
Left out on purpose
- Relay, realtime, usercontent, sandbox gate,
cdn.hosts, the published SDK defaults, plugin manifests and the scaffold$schema. - The Slack manifest,
wrangler/flyconfigs, seed data, desktop dev-only preview notices,cli-framework(it has no@superset/shareddependency), commit author identities, and comments. - Copy that names a domain: the contact page, SOC 2 FAQ answers, the web design showcase.
- Translated strings that name
support@superset.sh: the mobile delete-account error andserverError.user.theRecoveryPeriodHasEndedContact. Changing them would re-key the catalogs in all locales, so they are part of the later text swap. The server's raw message usesCOMPANY.SUPPORT_EMAIL, which gives the same string.
Released clients
No tRPC procedure, input or output shape changes. The only server-side differences are string values built from constants that equal the old literals.
Mobile fingerprint
The evaluated Expo config is identical; I checked with @expo/fingerprint. But app.config.ts now loads app-identity.ts instead of constants.ts, so the runtime fingerprint changes once, as any constants.ts edit already does today. After this PR, edits to constants.ts no longer change it.
How I tested
- New tests:
packages/shared/src/app-identity.test.ts: with default env,CANONICAL_URLSandCOMPANY(domain, support/team email,MAIL_TO, terms, privacy, status, trust) equal the old literals. Empty vars keep the defaults, both root-domain vars change them, and per-deployment URL overrides don't. I made it fail once by changing the default.packages/email/src/lib/sender.test.ts:NOREPLY_FROMequalsSuperset <noreply@superset.sh>by default and followsEMAIL_SENDING_DOMAIN, not the root domain.app-display-name.test.ts: display names are unchanged with the default brand.chrome-cookie-import.test.ts: the protected-domain list.
- Test suites, all passing: shared (1376), email (5), cli (423), i18n (164), marketing (160), api (236), trpc user/support/connectors (59).
- Desktop: 8234 pass, 36 fail. The same 36 tests fail on main (v1 git-utils and shell-env suites under full-suite load), and they pass when run alone.
- Typecheck:
- shared, email, i18n and docs pass.
- auth, trpc, api, web, marketing, cli, desktop and mobile fail locally, all with better-auth type-inference errors (
addMember,activeOrganizationId,deletionRequestedAt, …) that come from@superset/auth/@superset/trpc. Untouched main gives the same errors for@superset/authon my machine; my install has two@better-auth/corecopies. CI on main is green. - One real error from this change (a
menu.tsreplace that hit the non-macOS Quit item) was found and fixed. - None of these errors are on a line this PR changes.
bun run lint,bun run check:i18n(no catalog changes) andlint:proseinapps/marketing(0 errors) pass.- Rendered output, compared with main:
- Marketing dev server:
llms.txt,llms-full.txt,llms.md,robots.txt,sitemap.xmland all 7 sub-sitemaps,index.md,auth.md,agents.mdand.well-known/did.jsonare byte-identical. So are the/Linkheader and the/mcpredirect. - Docs dev server:
llms.mdx, the server card and the MCPinitializeinstructions are identical.llms.txtandllms-full.txthave the same lines, but page order changes between dev-server runs of the same code.
- Marketing dev server:
- Not run end to end: a packaged desktop build (bundle plist and menu labels), a mobile build, and real email sends.
Summary by cubic
Derives hosts, transactional senders, support addresses, and the desktop display name from config instead of literals, so a later domain or brand change needs only env vars plus a text swap. No behavior change by default — with no new env var set, every derived URL, address, and display name equals today's literal.
Hosts and email
- Marketing, app, and docs URLs derive from
NEXT_PUBLIC_ROOT_DOMAIN(orEXPO_PUBLIC_ROOT_DOMAINon mobile); the API URL stays onapi.superset.shand the CLI default follows it. - Transactional
from:addresses useNOREPLY_FROM, built fromEMAIL_SENDING_DOMAIN, so mail can stay on the old domain after a domain change. - New company constants (
COMPANY.SUPPORT_EMAIL,MAIL_TO, domain, URLs) replace literalsuperset.shreferences across API, marketing, docs, desktop, web, mobile, CLI, and email templates. - The desktop renderer CSP origin, the V2 setup-script docs link, and the contact page's status/trust hosts also follow the root domain.
- Staff access checks (web internal routes, admin procedures) accept both the configured domain and
@superset.sh. - Deploy and build workflows pass the new env vars; no secret needs to be set.
Desktop display name
- The macOS app menu, About labels, and notification registration use a display name built from
COMPANY.NAME, keeping the suffix ("Canary", "(workspace)"). Default builds keep Electron's role labels and About-panel defaults. CFBundleName,CFBundleDisplayName, and the Linux desktopName=come from a newdisplayNamevalue;productName(userData, logs, keychain entry) stays unchanged.- Cookie import now protects the configured domain in addition to
superset.sh.
Summary by CodeRabbit
- New Features
- Root domains can be configured for web, mobile, desktop, and email links.
- Desktop app names appear consistently in system menus, notifications, and About screens, with updated translations across supported languages.
- Improvements
- App, documentation, support, and email links now follow shared configuration across products.
- Staff access recognizes addresses using either the configured company domain or the legacy Superset domain.