Skip to main content

C5 — Feature Flag Inventory

Scope: Every feature flag that the Ghasi Melmastoon frontend checks, including: per-tenant toggles resolved from theme-config-service, global platform flags from the BFF bootstrap, and local dev/CI flags. Flags control UI availability, experimental features, and A/B variants.

Source: Collated from useFeatureFlag references, tenant toggles in 06-theming-and-tenant-config.md section 17, and BFF bootstrap payload.

Rule: No hard-coded if (ENV === "prod") conditionals. All feature gating goes through this catalog and the useFeatureFlag hook. Adding a new flag requires adding it here first.


1. Flag resolution hierarchy

1. Local override (dev/.env.local, CI) ← highest priority
2. BFF bootstrap payload flag ← platform-wide or tenant-wide
3. Tenant config (theme-config-service toggle) ← tenant-specific
4. Global default (code default) ← lowest priority

Flags are loaded at app boot from GET /bff/bootstrap and cached in React Query with a 5-minute TTL. On desktop, flags are part of the sync pull payload and stored in local SQLite.


2. Flag schema

interface FeatureFlag {
key: string; // kebab-case, e.g. "consumer-map-view"
scope: FlagScope; // "global" | "tenant" | "property" | "user-role"
type: "boolean" | "string" | "number";
defaultValue: boolean | string | number;
description: string;
surfaces: Surface[];
phase: "R1" | "R2" | "R3" | "experimental";
killSwitch?: boolean; // true = flag disables feature if false (vs hides it)
}

3. Platform-wide flags (resolved from BFF bootstrap)

Flag keyTypeDefaultPhaseDescriptionSurface(s)
consumer-map-viewbooleantrueR1Enable map view on consumer meta search (Leaflet). Disable to fall back to list-only if map tiles are slow.consumer-web, consumer-mobile
consumer-shortlist-maxnumber10R1Max items a guest can shortlist before being prompted to log in.consumer-web, consumer-mobile
booking-hold-timeout-minutesnumber15R1How long an inventory hold lasts before expiry. Surfaced as countdown timer.tenant-booking-web, consumer-mobile
booking-card-3dsbooleantrueR1Enable Adyen 3DS2 challenge step. Disable for card processors that don't require 3DS.tenant-booking-web, consumer-mobile
booking-apple-paybooleanfalseR2Enable Apple Pay button in payment step.tenant-booking-web, consumer-mobile
booking-google-paybooleanfalseR2Enable Google Pay button.tenant-booking-web, consumer-mobile
booking-mobile-moneybooleanfalseR2Enable mobile money (e.g., M-Pesa, easypaisa) as a payment option.tenant-booking-web, consumer-mobile
desktop-ai-copilotbooleanfalseR2Enable operator copilot AI sidebar (HITL suggestions for pricing, ops).operator-desktop
desktop-sync-push-interval-secondsnumber30R1How frequently the desktop pushes outbox changes to BFF.operator-desktop
desktop-lock-offline-pin-fallbackbooleantrueR1Allow PIN-based offline key fallback when lock vendor is unreachable.operator-desktop
desktop-auto-update-promptbooleantrueR1Show auto-update banner when a new version is available.operator-desktop
opensearch-meta-searchbooleanfalseR2Route meta search to OpenSearch index (vs Postgres FTS). Toggle after R2 index is warm.consumer-web, consumer-mobile
telemetry-search-anon-ratenumber0.01R1Sampling rate (0–1) for anonymous search telemetry events.consumer-web, consumer-mobile
telemetry-interaction-ratenumber0.10R1Sampling rate for hover/scroll/map-pin interactions.consumer-web, consumer-mobile
guest-portalbooleanfalseR2Enable the /manage guest portal (mid-stay / post-stay) for tenants.guest-portal-web
mobile-key-blebooleanfalseR3Enable BLE mobile key (requires Wallet entitlement and lock vendor BLE SDK).consumer-mobile, staff-mobile
mobile-key-walletbooleanfalseR3Enable Apple/Google Wallet key pass provisioning.consumer-mobile
kiosk-self-checkinbooleanfalseR2Enable self-check-in kiosk mode (Electron kiosk).kiosk
ota-portalbooleanfalseR3Enable OTA partner portal surface.ota-portal-web
embed-widgetbooleanfalseR2Enable embeddable booking widget for tenant external sites.

4. Per-tenant toggles (from theme-config-service tenant config)

Flag keyTypeDefaultDescriptionSurface(s)
tenant.booking-flow.special-requestsbooleantrueShow special requests step in booking funnel.tenant-booking-web, consumer-mobile
tenant.booking-flow.company-bookingbooleanfalseEnable company/corporate booking fields (company name, VAT number).tenant-booking-web
tenant.booking-flow.loyalty-codebooleanfalseShow loyalty/promo code input in funnel.tenant-booking-web, consumer-mobile
tenant.booking-flow.child-policybooleantrueShow child age capture in guest-details step.tenant-booking-web, consumer-mobile
tenant.payment.cash-on-arrivalbooleantrueOffer cash-on-arrival as a payment option.tenant-booking-web, consumer-mobile
tenant.payment.sharia-compliantbooleanfalseEnable Sharia-compliant payment policy display (no interest, no conventional credit).tenant-booking-web
tenant.checkin-window.early-override-enabledbooleanfalseAllow front desk to check in guests before the configured window without supervisor approval.operator-desktop
tenant.housekeeping.ai-order-enabledbooleantrueEnable AI-suggested housekeeping order.operator-desktop
tenant.notifications.push-enabledbooleantrueEnable OS push notifications for operator desktop.operator-desktop
tenant.notifications.whatsapp-enabledbooleanfalseEnable WhatsApp channel for guest notifications. Requires WhatsApp Business API credentials.
tenant.reporting.tax-report-auto-exportbooleanfalseAutomatically export monthly tax report at month-end.operator-desktop
tenant.theme.rtl-defaultboolean(from locale)Force RTL layout regardless of browser locale.all
tenant.locale.numeral-variantstring"latin""latin" or "locale". Controls whether body copy uses locale numerals.all

5. Dev / CI / testing flags

These are never surfaced in production. Set via .env.local or CI env vars.

Flag keyTypeDefaultDescription
NEXT_PUBLIC_FF_FORCE_OFFLINEbooleanfalseForce consumer web into offline/degraded mode.
NEXT_PUBLIC_FF_SKIP_3DSbooleanfalseSkip 3DS challenge in booking funnel (test only).
REACT_NATIVE_FF_MOCK_BLEbooleanfalseMock BLE scanner for mobile key testing.
ELECTRON_FF_MOCK_LOCK_VENDORbooleanfalseReturn mock success/fail from lock vendor adapter.
ELECTRON_FF_FORCE_SYNC_CONFLICTbooleanfalseInject a synthetic sync conflict on next pull.

6. Hook usage

// Boolean flag
const showMap = useFeatureFlag("consumer-map-view");

// With tenant context (resolved server-side; do not call BFF from component)
const hasLoyaltyCode = useFeatureFlag("tenant.booking-flow.loyalty-code");

// Number flag
const holdTimeout = useFeatureFlagNumber("booking-hold-timeout-minutes");

The useFeatureFlag hook reads from the React Query cache populated at bootstrap. It does not make network calls during render.


7. Flag lifecycle

StageDescription
DraftFlag defined here, not yet in codebase.
ActiveFlag in codebase; defaultValue is the live production default.
GraduatedFeature permanently enabled; flag removed from code in the next release.
RetiredFeature permanently disabled; flag removed from code.

Flag cleanup PRs are required within 2 releases of graduation or retirement.


8. Open Questions

  • Should opensearch-meta-search have a gradual rollout rate (e.g., 10% → 50% → 100%) rather than binary? Would require a number flag + per-session sampling.
  • How should tenant flags override platform flags when they conflict? Current: tenant flag wins unless marked killSwitch: true at platform level.
  • Add a tenant.booking-flow.deposit-required flag for properties that require a deposit at booking time?

References