Config schema
The site config is stored on Firestore at sites/{siteId}.config and surfaced to the widget via the /api/session/init response. Every field documented below.
Top-level shape
type SiteConfig = { layout?: 'floating' | 'bottom-bar' | 'side-panel'; // default 'floating' primaryColor?: string; // hex / rgb / hsl / color() greeting?: string; // max 240 chars, supports **bold** position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; siteName?: string; // shown as "{siteName} Assistant" in header logoUrl?: string; // public image URL, square ≥64px
typography?: { headingFont?: string; bodyFont?: string; headingWeight?: string; // CSS font-weight, e.g. '800' buttonRadius?: string; // CSS radius, e.g. '8px' };
launcher?: LauncherConfig; nudge?: NudgeConfig; quickChips?: QuickChip[];
allowedHosts?: string[]; // hosts allowed to mount with this site's token
// Internal — auto-set by the crawler, surfaced as fallback: detectedPrimaryColor?: string;};LauncherConfig
type LauncherConfig = { shape?: 'circle' | 'squircle' | 'pill'; // default 'circle' size?: 'sm' | 'md' | 'lg'; // default 'md' icon?: 'chat' | 'sparkle' | 'question' | 'lightning' | 'cursor'; // default 'chat' position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; offsetX?: number; // px, default 24 offsetY?: number; // px, default 24 glow?: boolean; // default true};NudgeConfig
type NudgeConfig = { enabled?: boolean; // default false message?: string; // max 140 chars style?: 'bubble' | 'card' | 'minimal'; // default 'bubble' delay?: number; // ms before auto-show, default 5000 dismissable?: boolean; // default true};QuickChip
type QuickChip = { text: string; // max 30 chars — what shows on the chip payload: string; // max 280 chars — what the AI receives when tapped};Array max length: 5.
Conventions
- All color strings must be valid CSS color values. Invalid colors are silently dropped server-side and the widget falls back to defaults.
- Strings that are empty / whitespace-only are dropped; the widget never sees them.
- Boolean defaults are conservative — features off unless explicitly enabled.
Read flow
Firestore site doc.config ↓server/routes/chat.js → resolvePrimaryColor + filter null/empty ↓session/init response.siteConfig ↓embed.js → window.LefluxConfig ↓ChatUI constructor cleanCfg pass → strip null/undefined/empty ↓this.config (final, used for render)If a field is null at any layer it falls through to widget defaults. So missing dashboard config doesn’t break the widget; it just uses sensible defaults.
Write flow (dashboard → Firestore)
Dashboard Settings page builds a partial update:
firestore.update(`sites/${siteId}`, { 'config.layout': edits.layout, 'config.primaryColor': edits.primaryColor, // ...etc, one dotted-path per field});Only changed fields are written. Defaults persist as null, not absent.
Validation
Schema is enforced via Firestore security rules + a server-side validator on the /api/admin/sites/:id/config endpoint (admin route). Direct Firestore writes from clients are rejected unless the user has site-edit permission for the org.