refactor: derive hosts and display name from config

Closed
KI
21d ago
+770-206

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.ts derives its URL defaults without importing @superset/shared, so electron-vite build/dev work.
  • The cookie import lowercases configured domains.
  • The About/Hide/Quit labels are translated in all locales.
  • The mobile app.config.ts fallback is read after config() 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/shared
    • CANONICAL_URLS (MARKETING, API, APP, DOCS) are built from NEXT_PUBLIC_ROOT_DOMAIN, or EXPO_PUBLIC_ROOT_DOMAIN on 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_TO is built from it.
    • IOS_APP and the root domain move to app-identity.ts, which is now the only shared file apps/mobile/app.config.ts loads.
  • @superset/email/sender
    • NOREPLY_FROM is built from EMAIL_SENDING_DOMAIN (default superset.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, /mcp redirect, Link header), 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
  • Desktop build
    • defineEnv falls 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. Setting SUPERSET_API_URL/SUPERSET_WEB_URL at runtime still overrides it.
  • Env plumbing
    • NEXT_PUBLIC_ROOT_DOMAIN goes to the api/web/marketing/docs deploys (production and preview), build-desktop.yml and build-cli.yml.
    • EMAIL_SENDING_DOMAIN goes to every deploy that has RESEND_API_KEY.
    • RELAY_BACKUP_URL goes to the web deploy: next.config already read it, but nothing set it.
    • .env.example, .env.local.example (empty, so the vars stay unset) and turbo.jsonc are updated.
    • No secret needs to be set now.

Display name (from #8379)

  • getAppDisplayName() replaces the first word of app.name with COMPANY.NAME and keeps the suffix ("Canary", "(workspace)").
  • The macOS app menu, About/Hide/Quit labels, About panel and notification registration use it.
  • CFBundleName, CFBundleDisplayName and the Linux desktop entry Name= come from a displayName value in the electron-builder configs. Canary overrides Name= 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, the superset:// scheme, the CLI binary, ~/.superset and the SUPERSET_* 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/fly configs, seed data, desktop dev-only preview notices, cli-framework (it has no @superset/shared dependency), 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 and serverError.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 uses COMPANY.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_URLS and COMPANY (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_FROM equals Superset <noreply@superset.sh> by default and follows EMAIL_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/auth on my machine; my install has two @better-auth/core copies. CI on main is green.
    • One real error from this change (a menu.ts replace 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) and lint:prose in apps/marketing (0 errors) pass.
  • Rendered output, compared with main:
    • Marketing dev server: llms.txt, llms-full.txt, llms.md, robots.txt, sitemap.xml and all 7 sub-sitemaps, index.md, auth.md, agents.md and .well-known/did.json are byte-identical. So are the / Link header and the /mcp redirect.
    • Docs dev server: llms.mdx, the server card and the MCP initialize instructions are identical. llms.txt and llms-full.txt have the same lines, but page order changes between dev-server runs of the same code.
  • 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 (or EXPO_PUBLIC_ROOT_DOMAIN on mobile); the API URL stays on api.superset.sh and the CLI default follows it.
  • Transactional from: addresses use NOREPLY_FROM, built from EMAIL_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 literal superset.sh references 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 desktop Name= come from a new displayName value; 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.