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​