Skip to main content

Feature Flags

Honest status: there is no centralized feature-flag system in the MIA codebase today. No GrowthBook, no Statsig, no Firestore-backed flag collection. The repo uses two ad-hoc mechanisms.

1. Environment variables

Flag-like booleans flipped via env vars and read at function/service startup. Examples:

  • USE_OPENROUTER — switch the default LLM provider.
  • ENABLE_ONBOARDING — gate the AI onboarding route on the frontend.
  • TEMPORAL_ADDRESS — not a flag per se, but acts like one (presence/absence changes whether scheduling is wired up).

Env files: .env.<project-id> (loaded at import via scripts/generate-env.ts), or Firebase Secret Manager for secrets. See Operations.

2. Per-company AI config (admin-controlled)

The closest thing to a kill-switch / cohort tool in the codebase today. Lets an admin route one company onto a different LLM provider / model without redeploying.

RTDB paths:

  • Companies/{companyId}/aiConfig{ provider, model, updatedAt } or null. Per-company override.
  • GlobalAiConfig{ provider, model, updatedAt }. Global default.

Admin endpoints (in the companies module, gated by an inline isAdmin() check):

EndpointPurposeFile
GET /ai-config/globalCurrent global default (no admin gate)functions/modules/companies/endpoints/apis/ai-usage.router.ts:176
PATCH /ai-config/globalSet global default (admin)ai-usage.router.ts:181
GET /companies/:id/ai-configRead per-company override (admin)ai-usage.router.ts:120
PATCH /companies/:id/ai-configSet per-company override (admin)ai-usage.router.ts:132
DELETE /companies/:id/ai-configClear per-company override (admin)ai-usage.router.ts:152

provider is constrained to 'anthropic' | 'openrouter'; model is a free-form string the provider adapter understands.

3. MIA access control (per-company + per-manager)

The gate for who can see the Mia tab in the navbar and call /api/chat. Three steps, all must pass.

RTDB pathPurposeWho writes
CompanySettings/{cid}/aiAgentEnabledOrg-level toggle. If false or missing, no one in this company sees Mia.Pivot internal admin (OwnerDashboard)
Companies/{cid}/pivotAiAgentAccess/{userId}Per-manager allowlist — { enabled: true } grants access. owner / head-office roles bypass this check.Company owner (or head-office)
CompanySettings/{cid}/aiAgentPersonaUI persona name: 'Mia' (default) or 'zack' (legacy alternate). Applied per-company.Company-level setting; used in the chat header, planner intro line, and UI labels.

Frontend gate logic at src/components/NavBar/NavBar.tsx:84-109 and src/routes/PivotAiAgentDashboard/PivotAiAgentGlobal.tsx:497. See Security Model: Who can use MIA for the full layered explanation.

Important caveat: the per-manager allowlist is checked on the frontend only. The chat endpoint /api/chat doesn't enforce a separate Mia-specific allow list — only the standard auth + scope middleware. A determined manager could bypass the UI and still call the API directly. See TODOs for the open work to make this server-enforced.

What this means in practice

  • You can't roll a generic feature out to 10% of companies today.
  • You can kill-switch a misbehaving AI model per company by setting aiConfig to a fallback provider — that ships immediately, no redeploy.
  • A/B testing arbitrary features requires manual cohort assignment in some bespoke spot in code.

Note: earlier versions of these docs referenced a Companies/{cid}/miaSettings path. That path is not written anywhere in the codebase — disregard. Per-company AI configuration lives at aiConfig as described above.

Planned work

Adding a proper flag collection (e.g., Firestore FeatureFlags/{flagKey} with enabled, rolloutPercent, companyAllowlist) is open. See Reference: TODOs.