refactor: derive hosts from env so the root domain is configurable
What
This routes hardcoded production hosts through NEXT_PUBLIC_ROOT_DOMAIN, so changing the root domain later only needs env vars. With no new env vars set, every derived value equals today's literal. Defaults stay superset.sh.
packages/shared/src/constants.ts- Adds
CANONICAL_URLS(MARKETING,API,APP,DOCS), derived only from the root domain. It deliberately ignoresNEXT_PUBLIC_MARKETING_URL/NEXT_PUBLIC_DOCS_URL, so preview and local deployments keep printing the same production URLs they print today. - Adds
COMPANY.SUPPORT_EMAIL.MAIL_TOnow builds from it.
- Adds
packages/email: addsEMAIL_SENDING_DOMAINandNOREPLY_FROM(@superset/email/sender). All 16 transactionalfrom:fields use it: auth (9), support (2), marketing forms (3), YC deals (1).- Call sites: literal hosts and support addresses are replaced with the constants above in:
- API discovery, OpenAPI, MCP and OAuth metadata
- Slack links
- docs
llms.txt, MCP and source links - marketing
next.config(API origin,/mcpredirect,Linkheader), install commands,llms, contact/enterprise inbox - desktop docs links, billing mailtos, sample prompts, cookie-protected host
- web billing mailto
- mobile terms/privacy/share URL
- CLI hints
- email template links
- Builds:
- Desktop
defineEnvfallbacks and the renderer CSP API origin derive fromNEXT_PUBLIC_ROOT_DOMAIN. - The CLI build bakes
NEXT_PUBLIC_ROOT_DOMAINin.
- Desktop
- i18n: two support-address messages now take the address as a placeholder (
{email},{SUPPORT_EMAIL}). The existing translations were carried over in all 16 locales. - Workflows and templates:
NEXT_PUBLIC_ROOT_DOMAINis passed to the api/web/marketing/admin/docs deploys (production and preview),build-desktop.ymlandbuild-cli.yml.EMAIL_SENDING_DOMAINis passed to every deploy that hasRESEND_API_KEY.RELAY_BACKUP_URLis passed to the web deploy.next.configalready read it, but nothing supplied it.- Unset secrets arrive as empty strings, and every reader falls back on empty (
||, oremptyStringAsUndefined).
Env vars that control each host
| Host / value | Env var | Default |
|---|---|---|
Root domain (marketing, api., app., docs., status, trust, support@, did:web) | NEXT_PUBLIC_ROOT_DOMAIN | superset.sh |
| Marketing URL per deployment | NEXT_PUBLIC_MARKETING_URL | https://${root} |
| Docs URL per deployment | NEXT_PUBLIC_DOCS_URL | https://docs.${root} |
| API / Web per deployment | NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WEB_URL (desktop fallbacks now api./app.${root}) | unchanged |
| Desktop streams | NEXT_PUBLIC_STREAMS_URL (fallback now streams.${root}) | https://streams.superset.sh |
| CLI | SUPERSET_API_URL, SUPERSET_WEB_URL, NEXT_PUBLIC_ROOT_DOMAIN (baked at build) | unchanged |
| Relay / relay backup / realtime | RELAY_URL, RELAY_BACKUP_URL, REALTIME_URL / NEXT_PUBLIC_REALTIME_URL | stay on superset.sh |
| Usercontent / sandbox gate | USERCONTENT_URL, SANDBOX_GATE_ORIGIN | stay on supersetusercontent.com |
| Transactional sender | EMAIL_SENDING_DOMAIN (new) | superset.sh |
Why the sending domain is separate
A new sending domain has no reputation. It needs weeks of gradual volume before inbox providers trust it. If the sender followed NEXT_PUBLIC_ROOT_DOMAIN, flipping the root domain would move all transactional mail (sign-in, invitations, billing) to a cold domain overnight. Keeping it on its own variable lets mail stay on the warmed-up domain until warm-up is done.
New env var checklist (docs/environment-variables.md):
EMAIL_SENDING_DOMAINhas a schema inpackages/email/src/lib/env.tswith a real default.NEXT_PUBLIC_ROOT_DOMAIN,EMAIL_SENDING_DOMAINandRELAY_BACKUP_URLare in.env.example..env.local.examplegets empty values for the first two, so they stay unset locally.- The deploy workflows get both the
env:entry and--env. - No secrets need setting now: unset means today's behaviour.
Intentionally out of scope
- Cloudflare
wrangler.jsoncroutes and vars (relay, relay2, realtime, usercontent, gate): these stay onsuperset.sh. packages/sdk/src/client.tspublished defaults, andpackages/mcp/server.json(registry manifest).- Slack
manifest.json(static, applied by hand).apps/mobile/store.config.js, podspecs, stories. - Demo, tape and shell scripts (
review-host,cli/scripts,realtime/scripts,relay/scripts/e2e-probe.ts,email/scripts/sync-automations.ts). - Brand name, deep-link schemes, iOS bundle IDs, social and GitHub URLs.
- Plugin catalog MCP URL and
SUPERSET_API_URLinpackages/shared/src/plugins/index.ts. These match against generated, published plugin manifests, so changing them needs a manifest republish. cdn.superset.shsandbox asset base: asset infrastructure, not a product host.- Commit author identities (
noreply@superset.shin sandbox claim/release), personal addresses, and theapps/desktop/package.jsonauthor email. - Growth analytics host filters, sitemap source, and Search Console property (
packages/trpc/src/lib/growth,GSC_SITE_URLdefault). These describe historical traffic data. - Copy that names a domain in marketing content: contact page, the SOC 2 FAQ answers, admin growth descriptions, web design showcase. They are content, like MDX, and need an editorial pass anyway.
packages/cli-frameworkpricing hint: the framework has no@superset/shareddependency.- Discord triage bot footer and
fly.tomlvars. - Comments, tests, the desktop dev-only preview notices, and seed data.
Known gap: the mobile bundle only inlines EXPO_PUBLIC_*, so it does not receive NEXT_PUBLIC_ROOT_DOMAIN. Its COMPANY/CANONICAL_URLS values stay on the default until the root domain is wired into mobile (EXPO_PUBLIC_WEB_URL etc. already work).
Verification
bun run check:i18n: passes. Only the two re-keyed messages changed, and all locales are filled.bun run lint:fix: clean; it only reformatted files in this diff.bun run typecheckon shared, email, auth, trpc, i18n, cli, api, web, marketing, docs, desktop, mobile:- shared, email, i18n and docs pass.
- The others fail with better-auth plugin type-inference errors:
addMember,createOrganization,activeOrganizationId,planand similar missing from inferred auth types. - The same errors happen with these changes reverted (
packages/auth: 5 errors before and after), so this is local install drift, not this change. - None of the errors point at a line this PR changes.
bun testpassed, 0 failures:- packages: shared (1311), email (3), i18n (165), cli (374), auth (8), trpc user/connectors (59)
- apps: api slack/openapi/mcp/lib (163), marketing src (158), web src (55), desktop chrome-cookie-import (24)
- Not run end to end in a real app.
Summary by cubic
Makes the root domain configurable via env vars so a rebrand needs no code changes; with no new vars set, every derived URL matches today's superset.sh defaults.
- Adds
NEXT_PUBLIC_ROOT_DOMAIN,EXPO_PUBLIC_ROOT_DOMAIN,EMAIL_SENDING_DOMAIN, andCOMPANY.SUPPORT_EMAIL; api, docs, marketing, desktop, web, mobile, cli, auth, and email templates now derive hosts and support addresses from them. - Keeps the sending domain on its own variable so transactional mail stays on the warmed-up domain after a root-domain flip; the CLI and desktop builds bake
NEXT_PUBLIC_ROOT_DOMAINin, and mobile readsEXPO_PUBLIC_ROOT_DOMAIN. - Desktop cookie import protects both the configured root domain and the legacy
superset.shdomain so old session cookies stay unimportable. - Two i18n messages take the support address as a placeholder, with a
support@superset.shfallback for old API responses; translations carried over in all 16 locales.
Summary by CodeRabbit
- Improvements
- Links across the app, emails, documentation, and integrations now use consistent, centrally configured website addresses.
- Support contact details and outgoing email sender addresses can be configured for each deployment.
- Error messages display the configured support contact across supported languages.
- Desktop, CLI, and mobile apps, along with deployment builds, can use a configurable root domain while preserving existing defaults.
- Workspace sharing links and published-page addresses also follow the configured app and company domains.