CXB Console ships as one codebase that builds into many branded apps. Brand-specific values live entirely under brands/<name>/ and src/ contains no client names, colors, or assets.

Brand Inputs

Each brand directory under brands/ holds its config and assets. Each deployment gets its own brand directory.
FileRequiredPurpose
.envYesVITE_* config (API URL, brand name, token key, theme colors)
favicon.pngYesBrowser favicon
favicon.icoOptionalLegacy favicon
logo.pngOptionalHeader/app logo (absent → no logo mark)
theme.cssOptionalExtra CSS injected as a stylesheet link
favicon-512.pngOptionalHigh-res favicon variant
Brand directories vary: for example one brand ships favicon.png + logo.png, while another ships only favicons (no logo).

The Build Script

scripts/build.sh <brand> is the single entry point. Steps performed:
  1. Validates the brand exists under brands/.
  2. Copies brands/<brand>/.env to the repo root .env.
  3. Copies logo.png, favicon.png, favicon.ico, and theme.css into public/ via a helper that chmod 644s each file. If logo.png or theme.css is absent, the script removes any stale copy from public/.
  4. Runs npm run build (Vite).
  5. Injects a <style>:root{...}</style> block into dist/index.html containing --primary, --primary-soft, and --bg read from the brand .env.
  6. If theme.css exists, injects a <link rel="stylesheet" href="/theme.css"> into dist/index.html.
The script chmod 644s every copied asset on purpose. Brand assets saved with 600 permissions cause nginx (running as www-data) to return 403 for logos/favicons after deploy.

Brand Config Values

src/brand.ts reads the VITE_* values with safe defaults:
ExportEnv varDefault
BRAND_NAMEVITE_BRAND_NAMECXB Console
BRAND_POWERED_BYVITE_BRAND_POWERED_BYPowered by CX Bridge
TOKEN_KEYVITE_TOKEN_KEYcxb_token
BRAND_LOGOVITE_BRAND_LOGOshown unless set to false
AURORA_COLOR1 / AURORA_COLOR2VITE_AURORA_COLOR1 / 2teal defaults
BRAND_THEMEVITE_BRAND_THEMEempty
The primary theme colors (--primary, --primary-soft, --bg) come from VITE_PRIMARY_COLOR, VITE_PRIMARY_SOFT, and VITE_BG_COLOR, injected into the HTML by the build script rather than read at runtime.

CSS Variables, Not a Plugin

CSS variable injection happens in the build script with sed against dist/index.html, not through a Vite plugin. This is deliberate — direct HTML injection is more reliable than relying on plugin ordering. The layouts and primitives reference these variables (var(--primary), etc.) so a single brand .env retheme propagates everywhere.

The ADK Gate

src/tier.ts computes the ADK flag by hashing the lowercased BRAND_NAME against an allow-list. Agent Desk routes, nav items, and the bot Agent Desk section are gated on ADK. A brand without Agent Desk licensing simply never renders those surfaces — no source edits required.

No-Leakage Rules

The whole point of the brand split is that one brand’s identity never reaches another’s build.
Never copy another brand’s favicon or logo as a placeholder. Always use the client’s actual favicon/logo, or none at all (omit logo.png to disable the logo mark).
  • Do not hard-code client names, colors, favicons, or logos in src/ — that defeats the build-time split.
  • When deploying, rsync the built dist/ with --delete. Without it, stale chunks from a previous brand linger in the web root and can surface cross-brand assets or trigger CORS errors on old chunk filenames.
  • After a build, verify zero leakage across HTML, JS, CSS, favicons, .env, and API responses.

API Client & Auth

VITE_API_URL and the brand-scoped token key.

App Shell & Routing

How the ADK flag gates routes.