Skip to content

Production Configuration Setup

Last reviewed: March 22, 2026

This runbook explains how BeFam separates local and production runtime configuration safely.

Runtime Layers

  1. GitHub Environment production (vars + secrets)
  2. Firestore runtime overrides (runtimeConfig/global, non-secret only)
  3. Flutter build-time defines (--dart-define)

Staging Deployment Scope

staging deploy pipeline is sandbox-only and deploys only: - Firebase resources in staging project (firestore:rules, firestore:indexes, storage, functions) - Web hosting bundle - Android release AAB + iOS signed IPA artifacts - Optional staged mobile publish: Android internal/closed track and iOS TestFlight

staging must never publish to production mobile channels. Use STAGING_MOBILE_PUBLISH_ENABLED=true to enable staged mobile publish.

Supported OS Versions

Current production baseline: - iOS: 15.0+ - Android: API 24+ (Android 7.0+)

Where this is enforced: - iOS deployment target: mobile/befam/ios/Podfile - Android min SDK: mobile/befam/android/app/build.gradle.kts (via flutter.minSdkVersion)

Important constraints from the current stack: - Flutter 3.41.x defaults to Android min SDK 24. - Firebase iOS plugins in use require iOS deployment target 15.0.

If you need to support lower OS versions: - run a dependency compatibility audit first - pin/downgrade affected plugins (if available) - re-test phone auth, Firebase messaging, billing, and print/export flows on real devices

Required Keys

Required vars (Functions runtime): - FIREBASE_PROJECT_ID - FIREBASE_FUNCTIONS_REGION - FIRESTORE_DATABASE_ID ((default) for staging, befam for production in current setup) - APP_TIMEZONE - APP_RUNTIME_CONFIG_COLLECTION - APP_RUNTIME_CONFIG_DOC_ID - EXPIRE_INVITES_JOB_SCHEDULE - EVENT_REMINDER_JOB_SCHEDULE - EVENT_REMINDER_LOOKAHEAD_MINUTES - EVENT_REMINDER_SCAN_LIMIT - EVENT_REMINDER_GRACE_MINUTES - BILLING_SUBSCRIPTION_REMINDER_JOB_SCHEDULE - BILLING_PENDING_TIMEOUT_JOB_SCHEDULE - BILLING_DELINQUENCY_JOB_SCHEDULE - BILLING_CONTACT_NOTICE_JOB_SCHEDULE - BILLING_PENDING_TIMEOUT_MINUTES - BILLING_PENDING_TIMEOUT_LIMIT - BILLING_DELINQUENCY_GRACE_DAYS - BILLING_DELINQUENCY_LIMIT - BILLING_DELINQUENCY_REMINDER_DAYS - BILLING_CONTACT_NOTICE_BATCH_LIMIT - BILLING_CONTACT_NOTICE_REQUIRE_ENDPOINTS - BILLING_CONTACT_NOTICE_WEBHOOK_TIMEOUT_MS - BILLING_CONTACT_NOTICE_WEBHOOK_MAX_RETRIES - BILLING_CONTACT_NOTICE_WEBHOOK_BACKOFF_MS - NOTIFICATION_PUSH_ENABLED - NOTIFICATION_EMAIL_ENABLED - NOTIFICATION_EMAIL_COLLECTION - NOTIFICATION_DEFAULT_PUSH_ENABLED - NOTIFICATION_DEFAULT_EMAIL_ENABLED - NOTIFICATION_ALLOW_NON_OTP_SMS - NOTIFICATION_EVENT_MAX_AUDIENCE - BILLING_CONTACT_SMS_WEBHOOK_URL - BILLING_CONTACT_EMAIL_WEBHOOK_URL - CALLABLE_ENFORCE_APP_CHECK - OTP_PROVIDER - OTP_ALLOWED_DIAL_CODES - OTP_TWILIO_VERIFY_SERVICE_SID - OTP_TWILIO_TIMEOUT_MS - OTP_TWILIO_MAX_RETRIES - OTP_TWILIO_BACKOFF_MS - GOOGLE_PLAY_PACKAGE_NAME - BILLING_IAP_ALLOW_TEST_MOCK - BILLING_IAP_APPLE_VERIFY_TIMEOUT_MS - BILLING_IAP_APPLE_VERIFY_MAX_RETRIES - BILLING_IAP_APPLE_VERIFY_BACKOFF_MS - GOOGLE_IAP_RTDN_AUDIENCE - GOOGLE_IAP_RTDN_SERVICE_ACCOUNT_EMAIL - BILLING_PRICING_CACHE_MS

IAP product IDs are loaded from Firestore collection subscriptionPackages (storeProductIds.ios and storeProductIds.android), not from environment variables.

Required deploy secrets (OIDC + billing): - GCP_WORKLOAD_IDENTITY_PROVIDER - GCP_SERVICE_ACCOUNT_EMAIL - BILLING_WEBHOOK_SECRET - APPLE_SHARED_SECRET

Required when OTP_PROVIDER=twilio: - OTP_TWILIO_ACCOUNT_SID - OTP_TWILIO_AUTH_TOKEN - OTP_TWILIO_VERIFY_SERVICE_SID

Optional secrets: - CARD_WEBHOOK_SECRET - BILLING_CONTACT_NOTICE_WEBHOOK_TOKEN - APPLE_IAP_WEBHOOK_BEARER_TOKEN - GOOGLE_IAP_WEBHOOK_BEARER_TOKEN

Required release signing secrets: - ANDROID_RELEASE_KEYSTORE_BASE64 - ANDROID_RELEASE_KEYSTORE_PASSWORD - ANDROID_RELEASE_KEY_ALIAS - ANDROID_RELEASE_KEY_PASSWORD - IOS_P12_BASE64 - IOS_P12_PASSWORD - IOS_PROVISIONING_PROFILE_BASE64 - IOS_TEAM_ID

Required for staged mobile publish (if enabled): - GOOGLE_PLAY_SERVICE_ACCOUNT_JSON (or fallback FIREBASE_SERVICE_ACCOUNT) - APP_STORE_CONNECT_ISSUER_ID - APP_STORE_CONNECT_API_KEY_ID - APP_STORE_CONNECT_API_PRIVATE_KEY

Mobile/Web build vars (GitHub vars, optional): - BEFAM_ALLOW_BUNDLED_FIREBASE_OPTIONS - BEFAM_FIREBASE_* - BEFAM_FIREBASE_FUNCTIONS_REGION - BEFAM_DEFAULT_TIMEZONE - BEFAM_INVALID_CHECKOUT_HOSTS - BEFAM_ENABLE_APP_CHECK - BEFAM_APP_CHECK_WEB_RECAPTCHA_SITE_KEY - BEFAM_ALLOW_FIREBASE_PHONE_FALLBACK (must stay false in production) - BEFAM_BILLING_PENDING_TIMEOUT_MINUTES

Key Release Checks

Before production deploy: - verify required vars/secrets exist - verify production Firestore database matches FIRESTORE_DATABASE_ID (current production value: befam) - verify staging Firestore database stays (default) and staging project id stays be-fam-3ab23 - verify Apple + Google Play product IDs match published store subscriptions - verify BILLING_IAP_ALLOW_TEST_MOCK=false in production - verify BILLING_ENABLE_LEGACY_CARD_FLOW=false in production - verify CALLABLE_ENFORCE_APP_CHECK=true in production - verify branch protection is enabled on staging and main with required checks - rotate any leaked secret immediately (especially APPLE_SHARED_SECRET) before release - keep manual settlement disabled unless explicitly needed - verify subscriptionPackages has active FREE/BASE/PLUS/PRO (preflight blocks release if missing)

After deploy: - verify runtime env file step succeeded - verify runtime override sync succeeded - verify subscription package catalog sync succeeded - run auth + billing smoke tests on real devices

Code-owned Firestore baseline

Production and staging deployments now sync subscriptionPackages from:

  • firebase/functions/config/subscription-packages.catalog.json

Sync/check command:

cd firebase/functions
npm ci
FIREBASE_PROJECT_ID=<project-id> npm run config:subscription-packages:check
FIREBASE_PROJECT_ID=<project-id> npm run config:subscription-packages:sync