Documentation

    Documentation

    Documentation

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. /
    2. /Firebase Integration

    Updated Jul 1, 202610 min listen

    1. /
    2. /Firebase Integration

    Updated Jul 1, 202610 min listen

    1. /
    2. /Firebase Integration

    Updated Jul 1, 202610 min listen

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Architecture
    Data Model
    Real Time
    Discovery Mutation Sync
    Security
    Backend Services
    k8s-postgres-fcm Mode
    Firebase Integration
    Features
    Authentication
    Email AI-CRM
    Entities
    Opportunities
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Tunnel Protocol
    Wallet & Credit System
    Multi-Vendor Store
    Referral Codes (Refcodes)
    Affiliate & Referral Enablement
    Payments Overview
    PaymentConductor
    SubscriptionConductor
    VideoConductor
    News Module - Digital Newspaper Experience
    Member Blogs
    Public Profile Pages
    Username Reservation System
    Scientific Editor
    Locale System
    Security & Compliance
    NFT Marketplace
    Token Staking System
    Performance Optimization Patterns
    Mobile Experience
    Wallet
    Wallet Security Tips
    API
    Authentication
    Email AI-CRM API
    Entities
    Opportunities
    Messaging API
    Notifications API
    Wallet API
    Store API
    Admin API
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Branding
    Themes
    Web3
    Token launch jurisdictions
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Development
    Local Setup
    Code Structure
    Documentation components
    Community tooling
    Ring MCP Server
    Generative Images (ImageConductor)
    Autonomous Newsroom (Grok)
    OSS vs enterprise
    Whitelabel Navigation
    Best Practices
    Workflow
    Code Style
    Performance
    Testing
    Deployment
    Debugging
    Contributing
    MCP
    ring-image-create
    ring-video-create
    Examples
    Quick Start
    White Label
    Real World
    Integrations
    Ethereum wallets (Wagmi v3)

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    Docs
    Backend Services
    Docs
    Backend Services
    Docs
    Backend Services

    Firebase Integration

    Use the Founder / Developer tabs in the docs sidebar to filter this page for your role.

    Ring Platform uses Firebase in two separate paths — a server-side Admin SDK v14 (for FCM push, Firestore, Auth, Storage, App Check, and Remote Config) and a client-side browser SDK (for FCM push token registration, Auth state, Firestore real-time, and AI Logic). The two paths use different credential strategies and different SDK versions.

    This page covers the full Firebase 14 Enterprise stack in firebase-full ring-db mode: pipeline operations, change streams, text/geo search, FCM Admin HTTP v1 bridge, SSOT atomic writes, AI Logic, Firebase Hosting config, and React 19 cache()-native helpers.

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    Local setup

    Why firebase-full mode?

    firebase-full is a first-class ring-db mode (elevated from prototyping on 2026-06-30). In this mode, all application data lives in Firebase Firestore, push notifications use Firebase Cloud Messaging, file uploads can use Firebase Storage, and the Firebase Hosting service can serve your Ring clone with a single firebase deploy command.

    What this means for your clone

    CapabilityWhat you get
    No PostgreSQL neededAll data lives in Firestore — no database server to manage, no connection pooling, no SSL certs
    Unlimited scaleFirebase manages read, write, and concurrency limits — your clone auto-scales
    Push notificationsFCM web push with React hooks, service worker, and Server Action token upsert (see Push notifications FCM)
    AI-powered featuresFirebase AI Logic for Gemini 2.5/3.x text generation, chat, and embedding — directly from the SDK
    Single deploy targetfirebase deploy --only hosting deploys your Ring clone to Firebase Hosting with TLS, CDN, and rewrite rules baked in

    Server-side files

    FilePurposeCredential strategy
    lib/firebase-admin.server.tsFirebase Admin SDK v14 singleton — FCM, Auth, Firestore, Storage, App Check, Remote Config, Vertex AIADC-first — Application Default Credentials on Cloud Run / GKE / Cloud Functions; falls back to explicit cert() from AUTH_FIREBASE_* env vars for local dev / CI
    lib/database/adapters/FirebaseAdapter.tsFirestore IDatabaseService implementation — full CRUD, query, batch, transaction, plus 19 Enterprise methodsADC-first — cert() fallback when credentials present in backend config (matches firebase-admin.server.ts)
    lib/services/firebase-service-manager.tsReact 19 cache()-deduplicated SSOT read helpers + atomic write primitives for credit_balance, subscription_ledger, payment_transactions, wallet_transactions, desk_orders, ordersDelegates to getAdminDb()
    lib/firebase/build-mock.server.tsMock services during Next.js SSG build + k8s-postgres-fcm fallback — prevents unnecessary Firebase connectionsReturns mocks without connecting

    Client-side files

    Firebase Integration

    Use the Founder / Developer tabs in the docs sidebar to filter this page for your role.

    Ring Platform uses Firebase in two separate paths — a server-side Admin SDK v14 (for FCM push, Firestore, Auth, Storage, App Check, and Remote Config) and a client-side browser SDK (for FCM push token registration, Auth state, Firestore real-time, and AI Logic). The two paths use different credential strategies and different SDK versions.

    This page covers the full Firebase 14 Enterprise stack in firebase-full ring-db mode: pipeline operations, change streams, text/geo search, FCM Admin HTTP v1 bridge, SSOT atomic writes, AI Logic, Firebase Hosting config, and React 19 cache()-native helpers.

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    Local setup

    Why firebase-full mode?

    firebase-full is a first-class ring-db mode (elevated from prototyping on 2026-06-30). In this mode, all application data lives in Firebase Firestore, push notifications use Firebase Cloud Messaging, file uploads can use Firebase Storage, and the Firebase Hosting service can serve your Ring clone with a single firebase deploy command.

    What this means for your clone

    CapabilityWhat you get
    No PostgreSQL neededAll data lives in Firestore — no database server to manage, no connection pooling, no SSL certs
    Unlimited scaleFirebase manages read, write, and concurrency limits — your clone auto-scales
    Push notificationsFCM web push with React hooks, service worker, and Server Action token upsert (see Push notifications FCM)
    AI-powered featuresFirebase AI Logic for Gemini 2.5/3.x text generation, chat, and embedding — directly from the SDK
    Single deploy targetfirebase deploy --only hosting deploys your Ring clone to Firebase Hosting with TLS, CDN, and rewrite rules baked in

    Server-side files

    FilePurposeCredential strategy
    lib/firebase-admin.server.tsFirebase Admin SDK v14 singleton — FCM, Auth, Firestore, Storage, App Check, Remote Config, Vertex AIADC-first — Application Default Credentials on Cloud Run / GKE / Cloud Functions; falls back to explicit cert() from AUTH_FIREBASE_* env vars for local dev / CI
    lib/database/adapters/FirebaseAdapter.tsFirestore IDatabaseService implementation — full CRUD, query, batch, transaction, plus 19 Enterprise methodsADC-first — cert() fallback when credentials present in backend config (matches firebase-admin.server.ts)
    lib/services/firebase-service-manager.tsReact 19 cache()-deduplicated SSOT read helpers + atomic write primitives for credit_balance, subscription_ledger, payment_transactions, wallet_transactions, desk_orders, ordersDelegates to getAdminDb()
    lib/firebase/build-mock.server.tsMock services during Next.js SSG build + k8s-postgres-fcm fallback — prevents unnecessary Firebase connectionsReturns mocks without connecting

    Client-side files

    Firebase Integration

    Use the Founder / Developer tabs in the docs sidebar to filter this page for your role.

    Ring Platform uses Firebase in two separate paths — a server-side Admin SDK v14 (for FCM push, Firestore, Auth, Storage, App Check, and Remote Config) and a client-side browser SDK (for FCM push token registration, Auth state, Firestore real-time, and AI Logic). The two paths use different credential strategies and different SDK versions.

    This page covers the full Firebase 14 Enterprise stack in firebase-full ring-db mode: pipeline operations, change streams, text/geo search, FCM Admin HTTP v1 bridge, SSOT atomic writes, AI Logic, Firebase Hosting config, and React 19 cache()-native helpers.

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    Local setup

    Why firebase-full mode?

    firebase-full is a first-class ring-db mode (elevated from prototyping on 2026-06-30). In this mode, all application data lives in Firebase Firestore, push notifications use Firebase Cloud Messaging, file uploads can use Firebase Storage, and the Firebase Hosting service can serve your Ring clone with a single firebase deploy command.

    What this means for your clone

    CapabilityWhat you get
    No PostgreSQL neededAll data lives in Firestore — no database server to manage, no connection pooling, no SSL certs
    Unlimited scaleFirebase manages read, write, and concurrency limits — your clone auto-scales
    Push notificationsFCM web push with React hooks, service worker, and Server Action token upsert (see Push notifications FCM)
    AI-powered featuresFirebase AI Logic for Gemini 2.5/3.x text generation, chat, and embedding — directly from the SDK
    Single deploy targetfirebase deploy --only hosting deploys your Ring clone to Firebase Hosting with TLS, CDN, and rewrite rules baked in

    Server-side files

    FilePurposeCredential strategy
    lib/firebase-admin.server.tsFirebase Admin SDK v14 singleton — FCM, Auth, Firestore, Storage, App Check, Remote Config, Vertex AIADC-first — Application Default Credentials on Cloud Run / GKE / Cloud Functions; falls back to explicit cert() from AUTH_FIREBASE_* env vars for local dev / CI
    lib/database/adapters/FirebaseAdapter.tsFirestore IDatabaseService implementation — full CRUD, query, batch, transaction, plus 19 Enterprise methodsADC-first — cert() fallback when credentials present in backend config (matches firebase-admin.server.ts)
    lib/services/firebase-service-manager.tsReact 19 cache()-deduplicated SSOT read helpers + atomic write primitives for credit_balance, subscription_ledger, payment_transactions, wallet_transactions, desk_orders, ordersDelegates to getAdminDb()
    lib/firebase/build-mock.server.tsMock services during Next.js SSG build + k8s-postgres-fcm fallback — prevents unnecessary Firebase connectionsReturns mocks without connecting

    Client-side files

    No vendor lock for authAuth.js v5 handles all authentication — Firebase Auth is not used
    Local emulatorNEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 auto-connects to the Firebase Local Emulator Suite for offline development

    Architecture overview

    firebase-full ring-db mode architecture

    What Firebase features are NOT used

    • Firebase Authentication — Auth.js v5 handles all auth (Google, Apple, email, crypto wallet)
    • Firebase Realtime Database — Tunnel (lib/tunnel) is the canonical real-time transport; RTDB exports exist but are unused
    • FirebaseUI / client auth SDK — there is no lib/firebase.ts; lib/firebase-client.ts is the only client entry point

    Cost profile

    • Firestore — pay-per-read/write/delete. React 19 cache() reduces per-request read operations by 3–7×
    • FCM — free at any scale
    • Firebase App Check — included in Spark/Blaze plans
    • Firebase Hosting — free tier includes 10 GB storage, 100 GB/month transfer
    • AI Logic (Gemini) — usage-based via Vertex AI pricing; optional to enable
    • No PostgreSQL costs — no database server to provision or maintain
    FilePurpose
    lib/firebase-client.tsBrowser Firebase app init with 9 React 19 cache()-wrapped lazy getters — getFirebaseClientApp, getFirebaseFirestoreClient, getFirebaseAuthClient, getFirebaseStorageClient, getFirebaseAIClient
    hooks/use-fcm.ts'use client' hook wrapping getMessaging + getToken + onMessage with Server Action upsert
    components/providers/fcm-provider.tsxReact context provider for FCM state across the component tree
    public/firebase-messaging-sw.jsService worker — Firebase compat SDK v12.9.0 loaded from CDN; handles background push events

    Planned: hooks/use-firebase.ts — use-firebase hook family (useFirebaseApp, useFirestore, useFirebaseAuth, useFirestoreDoc, useFirestoreCollection, useFirestoreCache) following the same pattern as use-fcm.ts.

    Firebase Admin SDK (firebase-admin.server.ts)

    The Admin SDK uses ADC-first initialization with explicit cert() fallback. This means it works out of the box on Cloud Run, Cloud Functions, and GKE without setting AUTH_FIREBASE_CLIENT_EMAIL or AUTH_FIREBASE_PRIVATE_KEY — only AUTH_FIREBASE_PROJECT_ID is needed.

    The singleton survives Next.js dev HMR via globalThis.__RING_FIREBASE_ADMIN_APP__:

    Exports

    ExportReturnsActive inBuild-mock fallback
    getAdminApp()AppAlwaysYes
    getAdminDb()Firestorefirebase-full onlymock in other modes
    getAdminAuth()Authfirebase-full onlymock in other modes
    getAdminMessaging()MessagingAll modes (FCM)Yes — via MockMessaging
    getAdminStorage()Storagefirebase-full (planned)Yes — via MockStorage
    getAdminAppCheck()AppCheckfirebase-fullYes — via MockAppCheck
    getAdminRemoteConfig()RemoteConfigfirebase-fullYes — via MockRemoteConfig
    getAdminRtdb()Databasefirebase-full (legacy)Yes — via MockDatabase

    Utility exports: initializeAdminApp(), isAdminAppInitialized(), getAdminAppMetrics(), configureAdminDb(settings), _resetAdminAppForTests()

    In k8s-postgres-fcm and supabase-fcm modes, getAdminDb() / getAdminAuth() return mock instances from build-mock.server.ts. The mock satisfies TypeScript types but never connects to Firebase. This prevents Firebase initialization when only PostgreSQL is active.

    Environment variable cleaning (important):

    The SDK aggressively cleans env var inputs — strips surrounding quotes, converts \\n to real newlines, trims whitespace. The env.local.template convention of double-quoting AUTH_FIREBASE_PRIVATE_KEY is intentional:

    Client-side Firebase (lib/firebase-client.ts)

    Provides 9 React 19 cache()-wrapped lazy getters that are SSR-safe and window-guarded:

    GetterReturnsLazy import
    getFirebaseClientApp()FirebaseAppfirebase/app
    getFirebaseFirestoreClient()Firestorefirebase/firestore
    getFirebaseAuthClient()Authfirebase/auth
    getFirebaseStorageClient()Storage (async)firebase/storage
    getFirebaseAIClient()AI (async)firebase/ai

    Emulator support: Set NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 in .env.local to auto-connect Firestore / Auth / Storage to the Firebase Local Emulator Suite (default ports 8080, 9099, 9199). Or call connectFirebaseClientEmulator({ firestorePort?, authPort?, storagePort? }) for finer control.

    Backward-compat: The module still exports { app, db, auth } as direct named exports for hooks/use-fcm.ts and other legacy code. New code should prefer the getFirebase*Client() getters for React 19 cache() deduplication and SSR safety.

    Utility exports: validateFirebaseConfig() — returns true when all required NEXT_PUBLIC_FIREBASE_* vars are present and non-placeholder; isFcmConfigured() — returns true when config AND VAPID key are valid; isFirebaseClientReady() — SSR-safe runtime check.

    FirebaseAdapter (FirebaseAdapter.ts)

    The FirebaseAdapter implements IDatabaseService and is used only when DB_BACKEND_MODE=firebase-full. It uses Application Default Credentials (ADC) by default, falling back to explicit cert() when credentials are present in the backend config:

    IDatabaseService contract

    Firebase 14 Enterprise features (new)

    The adapter now exposes 19 new methods across 6 domains:

    1. Native accessors — raw Admin SDK instances

    2. Pipeline operations — subquery joins, bulk update/delete via Firebase 14 Enterprise:

    3. Change streams — ordered insert/update/delete events (cache invalidation, audit logs):

    4. Text search — full-text search via Cloud Firestore Enterprise text indexes:

    5. Geospatial search — radius search via Enterprise geo indexes:

    FCM Admin bridge (new)

    Server-side push notifications via HTTP v1 API. Lazy-initializes getAdminMessaging():

    All sendFcm* methods use sendEach() (not legacy sendMulticast()) and include automatic reactive cleanup of invalid tokens (messaging/registration-token-not-registered and messaging/invalid-argument errors mark tokens as invalid in the fcm_tokens collection).

    SSOT atomic writes bridge (new)

    Delegates to lib/services/firebase-service-manager.ts atomic helpers — single Firestore transaction, ACID, prevents read-modify-write race:

    Connection diagnostics

    Useful for /api/admin/firebase-health endpoints and CI smoke tests.

    firebase-service-manager (firebase-service-manager.ts)

    Provides 30+ cached Firebase operations using React 19 cache() for per-request deduplication. SSOT-aligned to the canonical collection shapes:

    Domain-specific SSOT helpers

    HelperSSOT collectionPurpose
    getCachedUserCreditBalanceusers/{userId}.credit_balanceCached fiat USD credit balance
    getCachedCreditTransactionsusers/{userId}.credit_transactions[]Credit transaction history
    getCachedLatestSubscriptionsubscription_ledgerLatest subscription (any status)
    getCachedActiveSubscriptionsubscription_ledgerActive subscription (ACTIVE + active)
    getCachedSubscriptionsDuesubscription_ledgerDue-for-payment batch (cron)
    getCachedPaymentTransactionpayment_transactionsBy order_reference
    getCachedWalletTransactionswallet_transactionsPer-user activity feed
    getCachedDeskOrderdesk_ordersCredit ↔ native-token conversion
    getCachedUserOrdersordersUser order history

    Legacy aliases preserved

    Build-time optimization

    All cached functions are only called when getAdminDb() returns real Firestore. During build (SSG), the mock Firestore methods (now including MockMessaging, MockStorage, MockAppCheck, MockRemoteConfig) return safe empty results. The cache() layer deduplicates within each request, reducing redundant Firestore reads by 3–7×.

    AI Logic bridge (new)

    Server-side Gemini 2.5/3.x text generation and chat via Firebase AI Logic. Requires optional dependency firebase-admin/vertexai + @google-cloud/vertexai. Without them, helpers return a clear not-supported error (no crash).

    For the client-side equivalent in the browser, use getFirebaseAIClient() from lib/firebase-client.ts:

    Firebase Hosting bridge (new)

    firebase-full mode supports Firebase Hosting as a production deployment target alongside k3s, Vercel, and Docker. Module-level helpers generate a canonical firebase.json:

    The default rewrites map the canonical Ring routes:

    • /api/** → Cloud Function / App Hosting backend
    • /auth/** → Auth.js handlers
    • /trpc/** → tRPC (when present)
    • /tunnel/** → Tunnel WebSocket/SFE handler
    • /_actions/** → React 19 Server Actions
    • firebase-messaging-sw.js → Service Worker
    • ** → SPA fallback (/index.html)

    Default headers include Cache-Control: immutable for .js/.css, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, and Permissions-Policy.

    Build-time mocking (build-mock.server.ts)

    During Next.js static generation (NEXT_PHASE=phase-production-build), Firebase Admin calls are intercepted by mock services. Now includes 7 mock classes (up from 3):

    Mock classMethodsAdmin SDK type
    MockFirestorecollection, doc, get, set, update, delete, batch, runTransactionFirestore
    MockAuthgetUser, createUser, updateUser, deleteUser, listUsers, verifyIdTokenAuth
    MockDatabaseref, push, set, update, remove, once, on, orderByChild, ...Database
    MockMessagingsend, sendEach, sendAll, sendToTopic, subscribeToTopic, unsubscribeFromTopicMessaging
    MockStoragebucket, file, upload, getFiles, delete, getMetadata + defaultBucketStorage
    MockAppCheckcreateToken, verifyTokenAppCheck
    MockRemoteConfiggetTemplate, publishTemplate, rollback, listVersionsRemoteConfig

    This prevents ~22 redundant Firebase Admin initializations during SSG and reduces build time by approximately 31%.

    Environment variables

    Admin SDK (server-side)

    Client SDK (browser-side)

    Key differences between the two Firebase paths

    AspectAdmin SDK (firebase-admin.server.ts)Adapter (FirebaseAdapter.ts)
    CredentialsADC-first — cert() fallback when AUTH_FIREBASE_* presentADC-first — matches Admin SDK pattern
    Env varsOnly AUTH_FIREBASE_PROJECT_ID for ADC; all 3 for cert()Only AUTH_FIREBASE_PROJECT_ID for ADC
    Active inAll DB_BACKEND_MODE values (FCM always available)Only firebase-full
    Mock in postgres-primaryYes — all services returned as mocksNo — adapter not registered
    Import pathfirebase-admin/app, firebase-admin/firestore, firebase-admin/messaging, etc.Same (delegates via dynamic import)
    Versionv14 (named subpath imports)v14
    React 19 nativeglobalThis HMR-safe singleton + per-service observabilitycache()-wrapped lazy getters + async methods for use() + Suspense
    Enterprise featuresPipeline, change streams, text/geo, AI Logic (via Vertex AI)All of the above as adapter methods + module-level exports

    Related documentation

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    k8s-postgres-fcm mode

    Local setup

    typescript
    
    import { cert, initializeApp, type App } from 'firebase-admin/app'
    
    // globalThis-backed singleton prevents "already exists" errors on HMR
    const globalForAdmin = globalThis as typeof globalThis & {
      __RING_FIREBASE_ADMIN_APP__?: App
    }
    
    export function getAdminApp(): App {
      if (globalForAdmin.__RING_FIREBASE_ADMIN_APP__) {
        return globalForAdmin.__RING_FIREBASE_ADMIN_APP__
      }
      if (getApps().length > 0) {
        const app = getApps()[0]
        globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
        return app
      }
    
      // Check if we have explicit credentials for cert() fallback
      const hasExplicitCreds =
        !!process.env.AUTH_FIREBASE_PROJECT_ID &&
        !!process.env.AUTH_FIREBASE_CLIENT_EMAIL &&
        !!process.env.AUTH_FIREBASE_PRIVATE_KEY
    
      const appOptions = hasExplicitCreds
        ? { credential: cert({ /* cleaned env vars */ }) }
        : {} // ADC auto-detected from environment
    
      const app = initializeApp(appOptions)
      globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
      return app
    }
    bash
    
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n"
    typescript
    
    import { getFirebaseClientApp, getFirebaseFirestoreClient } from '@/lib/firebase-client'
    
    // SSR-safe: returns undefined during server render
    const app = getFirebaseClientApp()
    const db = getFirebaseFirestoreClient()
    typescript
    
    import { isEmulatorEnabled, connectFirebaseClientEmulator } from '@/lib/firebase-client'
    
    if (isEmulatorEnabled()) {
      await connectFirebaseClientEmulator()
    }
    typescript
    
    // Production (Cloud Run, GKE, Cloud Functions):
    //   ADC auto-detects from the environment — no manual cert() needed.
    //   Only AUTH_FIREBASE_PROJECT_ID required.
    
    // Local dev / CI:
    //   Falls back to explicit cert() when credentials.clientEmail and
    //   credentials.privateKey are present in the backend config.
    typescript
    
    // Single document
    await db.findById('users', userId)
    // => { success, data: T | null, error? }
    
    // Query with filters, ordering, pagination
    await db.query({
      collection: 'entities',
      filters: [{ field: 'status', operator: 'eq', value: 'active' }],
      orderBy: [{ field: 'createdAt', direction: 'desc' }],
      pagination: { limit: 20, offset: 0 },
    })
    
    // Mutations
    await db.create('users', data)
    await db.update('users', id, { name: 'New' })
    await db.delete('users', id)
    
    // Transactions
    await db.transaction(async (txn) => { ... })
    typescript
    
    const native = await adapter.getNativeFirestore()
    const auth = await adapter.getNativeAuth()
    const messaging = await adapter.getNativeMessaging()
    const storage = await adapter.getNativeStorage()
    const appCheck = await adapter.getNativeAppCheck()
    typescript
    
    const result = await adapter.runPipeline('orders', [
      { where: { field: 'status', op: '==', value: 'paid' } },
      { aggregate: { sum: { field: 'amount' }, as: 'total' } },
      { limit: 10 },
    ])
    typescript
    
    const { data } = await adapter.onCollectionChange('subscription_ledger', (event) => {
      if (event.type === 'modified') {
        publishToChannel(event.doc.id, 'subscription:update', event.doc)
      }
    })
    typescript
    
    const { data } = await adapter.textSearch('entities', 'organic coffee', {
      field: 'description',
      limit: 20,
    })
    typescript
    
    const { data } = await adapter.geoSearch('entities',
      { latitude: 49.44, longitude: 32.06 },
      50, // radius in km
      { field: 'location', limit: 20 },
    )
    typescript
    
    // Single FCM message
    await adapter.sendFcmMessage({
      token: 'fcm-registration-token',
      notification: { title: 'Hello', body: 'World' },
      webpush: { fcmOptions: { link: 'https://ring-platform.org/notifications' } },
    })
    
    // Multi-token fan-out to a specific user (auto-cleans dead tokens)
    await adapter.sendFcmToUser(userId, { title: 'New message', body: 'Text' },
      { route: '/messages/123' },
    )
    
    // Topic broadcast
    await adapter.sendFcmToTopic('global-announcements', { title: 'Maintenance', body: 'Tonight 2-4 AM' })
    
    // Token validation
    const { data } = await adapter.validateFcmToken(token)
    if (!data?.valid) { /* remove from DB */ }
    
    // Reactive cleanup
    await adapter.cleanupInvalidFcmTokens(['dead-token-1', 'dead-token-2'])
    typescript
    
    // Atomic credit balance adjust (fiat USD)
    await adapter.creditBalanceAdjust(userId, -10, { id: 'ct_xxx', description: 'Membership fee' })
    
    // Atomic subscription status transition (ledger + user doc)
    await adapter.subscriptionStatusUpdate(subId, 'cancelled',
      { membership: { tier: 'SUBSCRIBER' } },
      { cancelled_at: Date.now(), auto_renew: false },
    )
    
    // Atomic payment transaction status append (status_history prevention)
    await adapter.paymentStatusAppend(orderRef, 'paid', { processor_payload: { ... } })
    typescript
    
    const diagnostics = await adapter.diagnoseConnection()
    // => { backendMode, firebaseConfigured, services: { firestore: true, ... }, errors: [] }
    typescript
    
    getUserCreditTransactions: getCachedCreditTransactions, // legacy → SSOT
    getActiveSubscriptions: getCachedSubscriptionsDue,       // legacy → SSOT
    getUserOrders: getCachedUserOrders,                      // legacy → SSOT
    typescript
    
    import { aiGenerateText, aiChatCompletion } from '@/lib/database/adapters/FirebaseAdapter'
    
    // Text generation
    const result = await aiGenerateText({
      prompt: 'What is the Ring Platform?',
      model: 'gemini-2.5-flash',
      temperature: 0.7,
      maxOutputTokens: 1024,
    })
    
    if (result.success) {
      console.log(result.data!.text, result.data!.usage)
    }
    
    // Multi-turn chat
    const chatResult = await aiChatCompletion({
      model: 'gemini-2.5-flash',
      messages: [
        { role: 'system', content: 'You are a Ring Platform expert.' },
        { role: 'user', content: 'How do I set up firebase-full?' },
      ],
    })
    typescript
    
    import { getFirebaseAIClient } from '@/lib/firebase-client'
    
    const ai = await getFirebaseAIClient()
    if (ai) {
      const result = await ai.getGenerativeModel({ model: 'gemini-2.5-flash' }).generateContent('...')
    }
    typescript
    
    import { generateFirebaseJson, getRingHostingRewrites } from '@/lib/database/adapters/FirebaseAdapter'
    import { writeFile } from 'fs/promises'
    
    const config = generateFirebaseJson({
      public: 'out',
      rewrites: getRingHostingRewrites(), // /api/** → /api, /auth/** → /auth, ** → /index.html
    })
    
    await writeFile('firebase.json', JSON.stringify(config, null, 2))
    // Then: firebase deploy --only hosting
    bash
    
    # For ADC (Cloud Run / GKE / Cloud Functions — recommended):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    
    # For explicit cert() fallback (local dev / CI — all three required):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    AUTH_FIREBASE_CLIENT_EMAIL=your_firebase_client_email
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
    
    # Optional:
    FIREBASE_DATABASE_URL=https://your-project-default-rtdb.firebaseio.com
    FIREBASE_STORAGE_BUCKET=your-project.appspot.com
    FIREBASE_PRIVATE_KEY_ID=your_firebase_private_key_id
    FIREBASE_CLIENT_ID=your_firebase_client_id
    FIREBASE_FIRESTORE_DEBUG=true
    bash
    
    NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_firebase_project_id
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id
    NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
    NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-YCZKPV315E
    NEXT_PUBLIC_FIREBASE_VAPID_KEY=your_vapid_key
    NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1  # Enables Firebase Local Emulator Suite auto-connect
    No vendor lock for authAuth.js v5 handles all authentication — Firebase Auth is not used
    Local emulatorNEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 auto-connects to the Firebase Local Emulator Suite for offline development

    Architecture overview

    firebase-full ring-db mode architecture

    What Firebase features are NOT used

    • Firebase Authentication — Auth.js v5 handles all auth (Google, Apple, email, crypto wallet)
    • Firebase Realtime Database — Tunnel (lib/tunnel) is the canonical real-time transport; RTDB exports exist but are unused
    • FirebaseUI / client auth SDK — there is no lib/firebase.ts; lib/firebase-client.ts is the only client entry point

    Cost profile

    • Firestore — pay-per-read/write/delete. React 19 cache() reduces per-request read operations by 3–7×
    • FCM — free at any scale
    • Firebase App Check — included in Spark/Blaze plans
    • Firebase Hosting — free tier includes 10 GB storage, 100 GB/month transfer
    • AI Logic (Gemini) — usage-based via Vertex AI pricing; optional to enable
    • No PostgreSQL costs — no database server to provision or maintain
    FilePurpose
    lib/firebase-client.tsBrowser Firebase app init with 9 React 19 cache()-wrapped lazy getters — getFirebaseClientApp, getFirebaseFirestoreClient, getFirebaseAuthClient, getFirebaseStorageClient, getFirebaseAIClient
    hooks/use-fcm.ts'use client' hook wrapping getMessaging + getToken + onMessage with Server Action upsert
    components/providers/fcm-provider.tsxReact context provider for FCM state across the component tree
    public/firebase-messaging-sw.jsService worker — Firebase compat SDK v12.9.0 loaded from CDN; handles background push events

    Planned: hooks/use-firebase.ts — use-firebase hook family (useFirebaseApp, useFirestore, useFirebaseAuth, useFirestoreDoc, useFirestoreCollection, useFirestoreCache) following the same pattern as use-fcm.ts.

    Firebase Admin SDK (firebase-admin.server.ts)

    The Admin SDK uses ADC-first initialization with explicit cert() fallback. This means it works out of the box on Cloud Run, Cloud Functions, and GKE without setting AUTH_FIREBASE_CLIENT_EMAIL or AUTH_FIREBASE_PRIVATE_KEY — only AUTH_FIREBASE_PROJECT_ID is needed.

    The singleton survives Next.js dev HMR via globalThis.__RING_FIREBASE_ADMIN_APP__:

    Exports

    ExportReturnsActive inBuild-mock fallback
    getAdminApp()AppAlwaysYes
    getAdminDb()Firestorefirebase-full onlymock in other modes
    getAdminAuth()Authfirebase-full onlymock in other modes
    getAdminMessaging()MessagingAll modes (FCM)Yes — via MockMessaging
    getAdminStorage()Storagefirebase-full (planned)Yes — via MockStorage
    getAdminAppCheck()AppCheckfirebase-fullYes — via MockAppCheck
    getAdminRemoteConfig()RemoteConfigfirebase-fullYes — via MockRemoteConfig
    getAdminRtdb()Databasefirebase-full (legacy)Yes — via MockDatabase

    Utility exports: initializeAdminApp(), isAdminAppInitialized(), getAdminAppMetrics(), configureAdminDb(settings), _resetAdminAppForTests()

    In k8s-postgres-fcm and supabase-fcm modes, getAdminDb() / getAdminAuth() return mock instances from build-mock.server.ts. The mock satisfies TypeScript types but never connects to Firebase. This prevents Firebase initialization when only PostgreSQL is active.

    Environment variable cleaning (important):

    The SDK aggressively cleans env var inputs — strips surrounding quotes, converts \\n to real newlines, trims whitespace. The env.local.template convention of double-quoting AUTH_FIREBASE_PRIVATE_KEY is intentional:

    Client-side Firebase (lib/firebase-client.ts)

    Provides 9 React 19 cache()-wrapped lazy getters that are SSR-safe and window-guarded:

    GetterReturnsLazy import
    getFirebaseClientApp()FirebaseAppfirebase/app
    getFirebaseFirestoreClient()Firestorefirebase/firestore
    getFirebaseAuthClient()Authfirebase/auth
    getFirebaseStorageClient()Storage (async)firebase/storage
    getFirebaseAIClient()AI (async)firebase/ai

    Emulator support: Set NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 in .env.local to auto-connect Firestore / Auth / Storage to the Firebase Local Emulator Suite (default ports 8080, 9099, 9199). Or call connectFirebaseClientEmulator({ firestorePort?, authPort?, storagePort? }) for finer control.

    Backward-compat: The module still exports { app, db, auth } as direct named exports for hooks/use-fcm.ts and other legacy code. New code should prefer the getFirebase*Client() getters for React 19 cache() deduplication and SSR safety.

    Utility exports: validateFirebaseConfig() — returns true when all required NEXT_PUBLIC_FIREBASE_* vars are present and non-placeholder; isFcmConfigured() — returns true when config AND VAPID key are valid; isFirebaseClientReady() — SSR-safe runtime check.

    FirebaseAdapter (FirebaseAdapter.ts)

    The FirebaseAdapter implements IDatabaseService and is used only when DB_BACKEND_MODE=firebase-full. It uses Application Default Credentials (ADC) by default, falling back to explicit cert() when credentials are present in the backend config:

    IDatabaseService contract

    Firebase 14 Enterprise features (new)

    The adapter now exposes 19 new methods across 6 domains:

    1. Native accessors — raw Admin SDK instances

    2. Pipeline operations — subquery joins, bulk update/delete via Firebase 14 Enterprise:

    3. Change streams — ordered insert/update/delete events (cache invalidation, audit logs):

    4. Text search — full-text search via Cloud Firestore Enterprise text indexes:

    5. Geospatial search — radius search via Enterprise geo indexes:

    FCM Admin bridge (new)

    Server-side push notifications via HTTP v1 API. Lazy-initializes getAdminMessaging():

    All sendFcm* methods use sendEach() (not legacy sendMulticast()) and include automatic reactive cleanup of invalid tokens (messaging/registration-token-not-registered and messaging/invalid-argument errors mark tokens as invalid in the fcm_tokens collection).

    SSOT atomic writes bridge (new)

    Delegates to lib/services/firebase-service-manager.ts atomic helpers — single Firestore transaction, ACID, prevents read-modify-write race:

    Connection diagnostics

    Useful for /api/admin/firebase-health endpoints and CI smoke tests.

    firebase-service-manager (firebase-service-manager.ts)

    Provides 30+ cached Firebase operations using React 19 cache() for per-request deduplication. SSOT-aligned to the canonical collection shapes:

    Domain-specific SSOT helpers

    HelperSSOT collectionPurpose
    getCachedUserCreditBalanceusers/{userId}.credit_balanceCached fiat USD credit balance
    getCachedCreditTransactionsusers/{userId}.credit_transactions[]Credit transaction history
    getCachedLatestSubscriptionsubscription_ledgerLatest subscription (any status)
    getCachedActiveSubscriptionsubscription_ledgerActive subscription (ACTIVE + active)
    getCachedSubscriptionsDuesubscription_ledgerDue-for-payment batch (cron)
    getCachedPaymentTransactionpayment_transactionsBy order_reference
    getCachedWalletTransactionswallet_transactionsPer-user activity feed
    getCachedDeskOrderdesk_ordersCredit ↔ native-token conversion
    getCachedUserOrdersordersUser order history

    Legacy aliases preserved

    Build-time optimization

    All cached functions are only called when getAdminDb() returns real Firestore. During build (SSG), the mock Firestore methods (now including MockMessaging, MockStorage, MockAppCheck, MockRemoteConfig) return safe empty results. The cache() layer deduplicates within each request, reducing redundant Firestore reads by 3–7×.

    AI Logic bridge (new)

    Server-side Gemini 2.5/3.x text generation and chat via Firebase AI Logic. Requires optional dependency firebase-admin/vertexai + @google-cloud/vertexai. Without them, helpers return a clear not-supported error (no crash).

    For the client-side equivalent in the browser, use getFirebaseAIClient() from lib/firebase-client.ts:

    Firebase Hosting bridge (new)

    firebase-full mode supports Firebase Hosting as a production deployment target alongside k3s, Vercel, and Docker. Module-level helpers generate a canonical firebase.json:

    The default rewrites map the canonical Ring routes:

    • /api/** → Cloud Function / App Hosting backend
    • /auth/** → Auth.js handlers
    • /trpc/** → tRPC (when present)
    • /tunnel/** → Tunnel WebSocket/SFE handler
    • /_actions/** → React 19 Server Actions
    • firebase-messaging-sw.js → Service Worker
    • ** → SPA fallback (/index.html)

    Default headers include Cache-Control: immutable for .js/.css, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, and Permissions-Policy.

    Build-time mocking (build-mock.server.ts)

    During Next.js static generation (NEXT_PHASE=phase-production-build), Firebase Admin calls are intercepted by mock services. Now includes 7 mock classes (up from 3):

    Mock classMethodsAdmin SDK type
    MockFirestorecollection, doc, get, set, update, delete, batch, runTransactionFirestore
    MockAuthgetUser, createUser, updateUser, deleteUser, listUsers, verifyIdTokenAuth
    MockDatabaseref, push, set, update, remove, once, on, orderByChild, ...Database
    MockMessagingsend, sendEach, sendAll, sendToTopic, subscribeToTopic, unsubscribeFromTopicMessaging
    MockStoragebucket, file, upload, getFiles, delete, getMetadata + defaultBucketStorage
    MockAppCheckcreateToken, verifyTokenAppCheck
    MockRemoteConfiggetTemplate, publishTemplate, rollback, listVersionsRemoteConfig

    This prevents ~22 redundant Firebase Admin initializations during SSG and reduces build time by approximately 31%.

    Environment variables

    Admin SDK (server-side)

    Client SDK (browser-side)

    Key differences between the two Firebase paths

    AspectAdmin SDK (firebase-admin.server.ts)Adapter (FirebaseAdapter.ts)
    CredentialsADC-first — cert() fallback when AUTH_FIREBASE_* presentADC-first — matches Admin SDK pattern
    Env varsOnly AUTH_FIREBASE_PROJECT_ID for ADC; all 3 for cert()Only AUTH_FIREBASE_PROJECT_ID for ADC
    Active inAll DB_BACKEND_MODE values (FCM always available)Only firebase-full
    Mock in postgres-primaryYes — all services returned as mocksNo — adapter not registered
    Import pathfirebase-admin/app, firebase-admin/firestore, firebase-admin/messaging, etc.Same (delegates via dynamic import)
    Versionv14 (named subpath imports)v14
    React 19 nativeglobalThis HMR-safe singleton + per-service observabilitycache()-wrapped lazy getters + async methods for use() + Suspense
    Enterprise featuresPipeline, change streams, text/geo, AI Logic (via Vertex AI)All of the above as adapter methods + module-level exports

    Related documentation

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    k8s-postgres-fcm mode

    Local setup

    typescript
    
    import { cert, initializeApp, type App } from 'firebase-admin/app'
    
    // globalThis-backed singleton prevents "already exists" errors on HMR
    const globalForAdmin = globalThis as typeof globalThis & {
      __RING_FIREBASE_ADMIN_APP__?: App
    }
    
    export function getAdminApp(): App {
      if (globalForAdmin.__RING_FIREBASE_ADMIN_APP__) {
        return globalForAdmin.__RING_FIREBASE_ADMIN_APP__
      }
      if (getApps().length > 0) {
        const app = getApps()[0]
        globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
        return app
      }
    
      // Check if we have explicit credentials for cert() fallback
      const hasExplicitCreds =
        !!process.env.AUTH_FIREBASE_PROJECT_ID &&
        !!process.env.AUTH_FIREBASE_CLIENT_EMAIL &&
        !!process.env.AUTH_FIREBASE_PRIVATE_KEY
    
      const appOptions = hasExplicitCreds
        ? { credential: cert({ /* cleaned env vars */ }) }
        : {} // ADC auto-detected from environment
    
      const app = initializeApp(appOptions)
      globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
      return app
    }
    bash
    
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n"
    typescript
    
    import { getFirebaseClientApp, getFirebaseFirestoreClient } from '@/lib/firebase-client'
    
    // SSR-safe: returns undefined during server render
    const app = getFirebaseClientApp()
    const db = getFirebaseFirestoreClient()
    typescript
    
    import { isEmulatorEnabled, connectFirebaseClientEmulator } from '@/lib/firebase-client'
    
    if (isEmulatorEnabled()) {
      await connectFirebaseClientEmulator()
    }
    typescript
    
    // Production (Cloud Run, GKE, Cloud Functions):
    //   ADC auto-detects from the environment — no manual cert() needed.
    //   Only AUTH_FIREBASE_PROJECT_ID required.
    
    // Local dev / CI:
    //   Falls back to explicit cert() when credentials.clientEmail and
    //   credentials.privateKey are present in the backend config.
    typescript
    
    // Single document
    await db.findById('users', userId)
    // => { success, data: T | null, error? }
    
    // Query with filters, ordering, pagination
    await db.query({
      collection: 'entities',
      filters: [{ field: 'status', operator: 'eq', value: 'active' }],
      orderBy: [{ field: 'createdAt', direction: 'desc' }],
      pagination: { limit: 20, offset: 0 },
    })
    
    // Mutations
    await db.create('users', data)
    await db.update('users', id, { name: 'New' })
    await db.delete('users', id)
    
    // Transactions
    await db.transaction(async (txn) => { ... })
    typescript
    
    const native = await adapter.getNativeFirestore()
    const auth = await adapter.getNativeAuth()
    const messaging = await adapter.getNativeMessaging()
    const storage = await adapter.getNativeStorage()
    const appCheck = await adapter.getNativeAppCheck()
    typescript
    
    const result = await adapter.runPipeline('orders', [
      { where: { field: 'status', op: '==', value: 'paid' } },
      { aggregate: { sum: { field: 'amount' }, as: 'total' } },
      { limit: 10 },
    ])
    typescript
    
    const { data } = await adapter.onCollectionChange('subscription_ledger', (event) => {
      if (event.type === 'modified') {
        publishToChannel(event.doc.id, 'subscription:update', event.doc)
      }
    })
    typescript
    
    const { data } = await adapter.textSearch('entities', 'organic coffee', {
      field: 'description',
      limit: 20,
    })
    typescript
    
    const { data } = await adapter.geoSearch('entities',
      { latitude: 49.44, longitude: 32.06 },
      50, // radius in km
      { field: 'location', limit: 20 },
    )
    typescript
    
    // Single FCM message
    await adapter.sendFcmMessage({
      token: 'fcm-registration-token',
      notification: { title: 'Hello', body: 'World' },
      webpush: { fcmOptions: { link: 'https://ring-platform.org/notifications' } },
    })
    
    // Multi-token fan-out to a specific user (auto-cleans dead tokens)
    await adapter.sendFcmToUser(userId, { title: 'New message', body: 'Text' },
      { route: '/messages/123' },
    )
    
    // Topic broadcast
    await adapter.sendFcmToTopic('global-announcements', { title: 'Maintenance', body: 'Tonight 2-4 AM' })
    
    // Token validation
    const { data } = await adapter.validateFcmToken(token)
    if (!data?.valid) { /* remove from DB */ }
    
    // Reactive cleanup
    await adapter.cleanupInvalidFcmTokens(['dead-token-1', 'dead-token-2'])
    typescript
    
    // Atomic credit balance adjust (fiat USD)
    await adapter.creditBalanceAdjust(userId, -10, { id: 'ct_xxx', description: 'Membership fee' })
    
    // Atomic subscription status transition (ledger + user doc)
    await adapter.subscriptionStatusUpdate(subId, 'cancelled',
      { membership: { tier: 'SUBSCRIBER' } },
      { cancelled_at: Date.now(), auto_renew: false },
    )
    
    // Atomic payment transaction status append (status_history prevention)
    await adapter.paymentStatusAppend(orderRef, 'paid', { processor_payload: { ... } })
    typescript
    
    const diagnostics = await adapter.diagnoseConnection()
    // => { backendMode, firebaseConfigured, services: { firestore: true, ... }, errors: [] }
    typescript
    
    getUserCreditTransactions: getCachedCreditTransactions, // legacy → SSOT
    getActiveSubscriptions: getCachedSubscriptionsDue,       // legacy → SSOT
    getUserOrders: getCachedUserOrders,                      // legacy → SSOT
    typescript
    
    import { aiGenerateText, aiChatCompletion } from '@/lib/database/adapters/FirebaseAdapter'
    
    // Text generation
    const result = await aiGenerateText({
      prompt: 'What is the Ring Platform?',
      model: 'gemini-2.5-flash',
      temperature: 0.7,
      maxOutputTokens: 1024,
    })
    
    if (result.success) {
      console.log(result.data!.text, result.data!.usage)
    }
    
    // Multi-turn chat
    const chatResult = await aiChatCompletion({
      model: 'gemini-2.5-flash',
      messages: [
        { role: 'system', content: 'You are a Ring Platform expert.' },
        { role: 'user', content: 'How do I set up firebase-full?' },
      ],
    })
    typescript
    
    import { getFirebaseAIClient } from '@/lib/firebase-client'
    
    const ai = await getFirebaseAIClient()
    if (ai) {
      const result = await ai.getGenerativeModel({ model: 'gemini-2.5-flash' }).generateContent('...')
    }
    typescript
    
    import { generateFirebaseJson, getRingHostingRewrites } from '@/lib/database/adapters/FirebaseAdapter'
    import { writeFile } from 'fs/promises'
    
    const config = generateFirebaseJson({
      public: 'out',
      rewrites: getRingHostingRewrites(), // /api/** → /api, /auth/** → /auth, ** → /index.html
    })
    
    await writeFile('firebase.json', JSON.stringify(config, null, 2))
    // Then: firebase deploy --only hosting
    bash
    
    # For ADC (Cloud Run / GKE / Cloud Functions — recommended):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    
    # For explicit cert() fallback (local dev / CI — all three required):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    AUTH_FIREBASE_CLIENT_EMAIL=your_firebase_client_email
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
    
    # Optional:
    FIREBASE_DATABASE_URL=https://your-project-default-rtdb.firebaseio.com
    FIREBASE_STORAGE_BUCKET=your-project.appspot.com
    FIREBASE_PRIVATE_KEY_ID=your_firebase_private_key_id
    FIREBASE_CLIENT_ID=your_firebase_client_id
    FIREBASE_FIRESTORE_DEBUG=true
    bash
    
    NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_firebase_project_id
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id
    NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
    NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-YCZKPV315E
    NEXT_PUBLIC_FIREBASE_VAPID_KEY=your_vapid_key
    NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1  # Enables Firebase Local Emulator Suite auto-connect
    No vendor lock for authAuth.js v5 handles all authentication — Firebase Auth is not used
    Local emulatorNEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 auto-connects to the Firebase Local Emulator Suite for offline development

    Architecture overview

    firebase-full ring-db mode architecture

    What Firebase features are NOT used

    • Firebase Authentication — Auth.js v5 handles all auth (Google, Apple, email, crypto wallet)
    • Firebase Realtime Database — Tunnel (lib/tunnel) is the canonical real-time transport; RTDB exports exist but are unused
    • FirebaseUI / client auth SDK — there is no lib/firebase.ts; lib/firebase-client.ts is the only client entry point

    Cost profile

    • Firestore — pay-per-read/write/delete. React 19 cache() reduces per-request read operations by 3–7×
    • FCM — free at any scale
    • Firebase App Check — included in Spark/Blaze plans
    • Firebase Hosting — free tier includes 10 GB storage, 100 GB/month transfer
    • AI Logic (Gemini) — usage-based via Vertex AI pricing; optional to enable
    • No PostgreSQL costs — no database server to provision or maintain
    FilePurpose
    lib/firebase-client.tsBrowser Firebase app init with 9 React 19 cache()-wrapped lazy getters — getFirebaseClientApp, getFirebaseFirestoreClient, getFirebaseAuthClient, getFirebaseStorageClient, getFirebaseAIClient
    hooks/use-fcm.ts'use client' hook wrapping getMessaging + getToken + onMessage with Server Action upsert
    components/providers/fcm-provider.tsxReact context provider for FCM state across the component tree
    public/firebase-messaging-sw.jsService worker — Firebase compat SDK v12.9.0 loaded from CDN; handles background push events

    Planned: hooks/use-firebase.ts — use-firebase hook family (useFirebaseApp, useFirestore, useFirebaseAuth, useFirestoreDoc, useFirestoreCollection, useFirestoreCache) following the same pattern as use-fcm.ts.

    Firebase Admin SDK (firebase-admin.server.ts)

    The Admin SDK uses ADC-first initialization with explicit cert() fallback. This means it works out of the box on Cloud Run, Cloud Functions, and GKE without setting AUTH_FIREBASE_CLIENT_EMAIL or AUTH_FIREBASE_PRIVATE_KEY — only AUTH_FIREBASE_PROJECT_ID is needed.

    The singleton survives Next.js dev HMR via globalThis.__RING_FIREBASE_ADMIN_APP__:

    Exports

    ExportReturnsActive inBuild-mock fallback
    getAdminApp()AppAlwaysYes
    getAdminDb()Firestorefirebase-full onlymock in other modes
    getAdminAuth()Authfirebase-full onlymock in other modes
    getAdminMessaging()MessagingAll modes (FCM)Yes — via MockMessaging
    getAdminStorage()Storagefirebase-full (planned)Yes — via MockStorage
    getAdminAppCheck()AppCheckfirebase-fullYes — via MockAppCheck
    getAdminRemoteConfig()RemoteConfigfirebase-fullYes — via MockRemoteConfig
    getAdminRtdb()Databasefirebase-full (legacy)Yes — via MockDatabase

    Utility exports: initializeAdminApp(), isAdminAppInitialized(), getAdminAppMetrics(), configureAdminDb(settings), _resetAdminAppForTests()

    In k8s-postgres-fcm and supabase-fcm modes, getAdminDb() / getAdminAuth() return mock instances from build-mock.server.ts. The mock satisfies TypeScript types but never connects to Firebase. This prevents Firebase initialization when only PostgreSQL is active.

    Environment variable cleaning (important):

    The SDK aggressively cleans env var inputs — strips surrounding quotes, converts \\n to real newlines, trims whitespace. The env.local.template convention of double-quoting AUTH_FIREBASE_PRIVATE_KEY is intentional:

    Client-side Firebase (lib/firebase-client.ts)

    Provides 9 React 19 cache()-wrapped lazy getters that are SSR-safe and window-guarded:

    GetterReturnsLazy import
    getFirebaseClientApp()FirebaseAppfirebase/app
    getFirebaseFirestoreClient()Firestorefirebase/firestore
    getFirebaseAuthClient()Authfirebase/auth
    getFirebaseStorageClient()Storage (async)firebase/storage
    getFirebaseAIClient()AI (async)firebase/ai

    Emulator support: Set NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1 in .env.local to auto-connect Firestore / Auth / Storage to the Firebase Local Emulator Suite (default ports 8080, 9099, 9199). Or call connectFirebaseClientEmulator({ firestorePort?, authPort?, storagePort? }) for finer control.

    Backward-compat: The module still exports { app, db, auth } as direct named exports for hooks/use-fcm.ts and other legacy code. New code should prefer the getFirebase*Client() getters for React 19 cache() deduplication and SSR safety.

    Utility exports: validateFirebaseConfig() — returns true when all required NEXT_PUBLIC_FIREBASE_* vars are present and non-placeholder; isFcmConfigured() — returns true when config AND VAPID key are valid; isFirebaseClientReady() — SSR-safe runtime check.

    FirebaseAdapter (FirebaseAdapter.ts)

    The FirebaseAdapter implements IDatabaseService and is used only when DB_BACKEND_MODE=firebase-full. It uses Application Default Credentials (ADC) by default, falling back to explicit cert() when credentials are present in the backend config:

    IDatabaseService contract

    Firebase 14 Enterprise features (new)

    The adapter now exposes 19 new methods across 6 domains:

    1. Native accessors — raw Admin SDK instances

    2. Pipeline operations — subquery joins, bulk update/delete via Firebase 14 Enterprise:

    3. Change streams — ordered insert/update/delete events (cache invalidation, audit logs):

    4. Text search — full-text search via Cloud Firestore Enterprise text indexes:

    5. Geospatial search — radius search via Enterprise geo indexes:

    FCM Admin bridge (new)

    Server-side push notifications via HTTP v1 API. Lazy-initializes getAdminMessaging():

    All sendFcm* methods use sendEach() (not legacy sendMulticast()) and include automatic reactive cleanup of invalid tokens (messaging/registration-token-not-registered and messaging/invalid-argument errors mark tokens as invalid in the fcm_tokens collection).

    SSOT atomic writes bridge (new)

    Delegates to lib/services/firebase-service-manager.ts atomic helpers — single Firestore transaction, ACID, prevents read-modify-write race:

    Connection diagnostics

    Useful for /api/admin/firebase-health endpoints and CI smoke tests.

    firebase-service-manager (firebase-service-manager.ts)

    Provides 30+ cached Firebase operations using React 19 cache() for per-request deduplication. SSOT-aligned to the canonical collection shapes:

    Domain-specific SSOT helpers

    HelperSSOT collectionPurpose
    getCachedUserCreditBalanceusers/{userId}.credit_balanceCached fiat USD credit balance
    getCachedCreditTransactionsusers/{userId}.credit_transactions[]Credit transaction history
    getCachedLatestSubscriptionsubscription_ledgerLatest subscription (any status)
    getCachedActiveSubscriptionsubscription_ledgerActive subscription (ACTIVE + active)
    getCachedSubscriptionsDuesubscription_ledgerDue-for-payment batch (cron)
    getCachedPaymentTransactionpayment_transactionsBy order_reference
    getCachedWalletTransactionswallet_transactionsPer-user activity feed
    getCachedDeskOrderdesk_ordersCredit ↔ native-token conversion
    getCachedUserOrdersordersUser order history

    Legacy aliases preserved

    Build-time optimization

    All cached functions are only called when getAdminDb() returns real Firestore. During build (SSG), the mock Firestore methods (now including MockMessaging, MockStorage, MockAppCheck, MockRemoteConfig) return safe empty results. The cache() layer deduplicates within each request, reducing redundant Firestore reads by 3–7×.

    AI Logic bridge (new)

    Server-side Gemini 2.5/3.x text generation and chat via Firebase AI Logic. Requires optional dependency firebase-admin/vertexai + @google-cloud/vertexai. Without them, helpers return a clear not-supported error (no crash).

    For the client-side equivalent in the browser, use getFirebaseAIClient() from lib/firebase-client.ts:

    Firebase Hosting bridge (new)

    firebase-full mode supports Firebase Hosting as a production deployment target alongside k3s, Vercel, and Docker. Module-level helpers generate a canonical firebase.json:

    The default rewrites map the canonical Ring routes:

    • /api/** → Cloud Function / App Hosting backend
    • /auth/** → Auth.js handlers
    • /trpc/** → tRPC (when present)
    • /tunnel/** → Tunnel WebSocket/SFE handler
    • /_actions/** → React 19 Server Actions
    • firebase-messaging-sw.js → Service Worker
    • ** → SPA fallback (/index.html)

    Default headers include Cache-Control: immutable for .js/.css, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, and Permissions-Policy.

    Build-time mocking (build-mock.server.ts)

    During Next.js static generation (NEXT_PHASE=phase-production-build), Firebase Admin calls are intercepted by mock services. Now includes 7 mock classes (up from 3):

    Mock classMethodsAdmin SDK type
    MockFirestorecollection, doc, get, set, update, delete, batch, runTransactionFirestore
    MockAuthgetUser, createUser, updateUser, deleteUser, listUsers, verifyIdTokenAuth
    MockDatabaseref, push, set, update, remove, once, on, orderByChild, ...Database
    MockMessagingsend, sendEach, sendAll, sendToTopic, subscribeToTopic, unsubscribeFromTopicMessaging
    MockStoragebucket, file, upload, getFiles, delete, getMetadata + defaultBucketStorage
    MockAppCheckcreateToken, verifyTokenAppCheck
    MockRemoteConfiggetTemplate, publishTemplate, rollback, listVersionsRemoteConfig

    This prevents ~22 redundant Firebase Admin initializations during SSG and reduces build time by approximately 31%.

    Environment variables

    Admin SDK (server-side)

    Client SDK (browser-side)

    Key differences between the two Firebase paths

    AspectAdmin SDK (firebase-admin.server.ts)Adapter (FirebaseAdapter.ts)
    CredentialsADC-first — cert() fallback when AUTH_FIREBASE_* presentADC-first — matches Admin SDK pattern
    Env varsOnly AUTH_FIREBASE_PROJECT_ID for ADC; all 3 for cert()Only AUTH_FIREBASE_PROJECT_ID for ADC
    Active inAll DB_BACKEND_MODE values (FCM always available)Only firebase-full
    Mock in postgres-primaryYes — all services returned as mocksNo — adapter not registered
    Import pathfirebase-admin/app, firebase-admin/firestore, firebase-admin/messaging, etc.Same (delegates via dynamic import)
    Versionv14 (named subpath imports)v14
    React 19 nativeglobalThis HMR-safe singleton + per-service observabilitycache()-wrapped lazy getters + async methods for use() + Suspense
    Enterprise featuresPipeline, change streams, text/geo, AI Logic (via Vertex AI)All of the above as adapter methods + module-level exports

    Related documentation

    Backend modes

    Push notifications (FCM)

    Environment variables

    Authentication

    k8s-postgres-fcm mode

    Local setup

    typescript
    
    import { cert, initializeApp, type App } from 'firebase-admin/app'
    
    // globalThis-backed singleton prevents "already exists" errors on HMR
    const globalForAdmin = globalThis as typeof globalThis & {
      __RING_FIREBASE_ADMIN_APP__?: App
    }
    
    export function getAdminApp(): App {
      if (globalForAdmin.__RING_FIREBASE_ADMIN_APP__) {
        return globalForAdmin.__RING_FIREBASE_ADMIN_APP__
      }
      if (getApps().length > 0) {
        const app = getApps()[0]
        globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
        return app
      }
    
      // Check if we have explicit credentials for cert() fallback
      const hasExplicitCreds =
        !!process.env.AUTH_FIREBASE_PROJECT_ID &&
        !!process.env.AUTH_FIREBASE_CLIENT_EMAIL &&
        !!process.env.AUTH_FIREBASE_PRIVATE_KEY
    
      const appOptions = hasExplicitCreds
        ? { credential: cert({ /* cleaned env vars */ }) }
        : {} // ADC auto-detected from environment
    
      const app = initializeApp(appOptions)
      globalForAdmin.__RING_FIREBASE_ADMIN_APP__ = app
      return app
    }
    bash
    
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----\n"
    typescript
    
    import { getFirebaseClientApp, getFirebaseFirestoreClient } from '@/lib/firebase-client'
    
    // SSR-safe: returns undefined during server render
    const app = getFirebaseClientApp()
    const db = getFirebaseFirestoreClient()
    typescript
    
    import { isEmulatorEnabled, connectFirebaseClientEmulator } from '@/lib/firebase-client'
    
    if (isEmulatorEnabled()) {
      await connectFirebaseClientEmulator()
    }
    typescript
    
    // Production (Cloud Run, GKE, Cloud Functions):
    //   ADC auto-detects from the environment — no manual cert() needed.
    //   Only AUTH_FIREBASE_PROJECT_ID required.
    
    // Local dev / CI:
    //   Falls back to explicit cert() when credentials.clientEmail and
    //   credentials.privateKey are present in the backend config.
    typescript
    
    // Single document
    await db.findById('users', userId)
    // => { success, data: T | null, error? }
    
    // Query with filters, ordering, pagination
    await db.query({
      collection: 'entities',
      filters: [{ field: 'status', operator: 'eq', value: 'active' }],
      orderBy: [{ field: 'createdAt', direction: 'desc' }],
      pagination: { limit: 20, offset: 0 },
    })
    
    // Mutations
    await db.create('users', data)
    await db.update('users', id, { name: 'New' })
    await db.delete('users', id)
    
    // Transactions
    await db.transaction(async (txn) => { ... })
    typescript
    
    const native = await adapter.getNativeFirestore()
    const auth = await adapter.getNativeAuth()
    const messaging = await adapter.getNativeMessaging()
    const storage = await adapter.getNativeStorage()
    const appCheck = await adapter.getNativeAppCheck()
    typescript
    
    const result = await adapter.runPipeline('orders', [
      { where: { field: 'status', op: '==', value: 'paid' } },
      { aggregate: { sum: { field: 'amount' }, as: 'total' } },
      { limit: 10 },
    ])
    typescript
    
    const { data } = await adapter.onCollectionChange('subscription_ledger', (event) => {
      if (event.type === 'modified') {
        publishToChannel(event.doc.id, 'subscription:update', event.doc)
      }
    })
    typescript
    
    const { data } = await adapter.textSearch('entities', 'organic coffee', {
      field: 'description',
      limit: 20,
    })
    typescript
    
    const { data } = await adapter.geoSearch('entities',
      { latitude: 49.44, longitude: 32.06 },
      50, // radius in km
      { field: 'location', limit: 20 },
    )
    typescript
    
    // Single FCM message
    await adapter.sendFcmMessage({
      token: 'fcm-registration-token',
      notification: { title: 'Hello', body: 'World' },
      webpush: { fcmOptions: { link: 'https://ring-platform.org/notifications' } },
    })
    
    // Multi-token fan-out to a specific user (auto-cleans dead tokens)
    await adapter.sendFcmToUser(userId, { title: 'New message', body: 'Text' },
      { route: '/messages/123' },
    )
    
    // Topic broadcast
    await adapter.sendFcmToTopic('global-announcements', { title: 'Maintenance', body: 'Tonight 2-4 AM' })
    
    // Token validation
    const { data } = await adapter.validateFcmToken(token)
    if (!data?.valid) { /* remove from DB */ }
    
    // Reactive cleanup
    await adapter.cleanupInvalidFcmTokens(['dead-token-1', 'dead-token-2'])
    typescript
    
    // Atomic credit balance adjust (fiat USD)
    await adapter.creditBalanceAdjust(userId, -10, { id: 'ct_xxx', description: 'Membership fee' })
    
    // Atomic subscription status transition (ledger + user doc)
    await adapter.subscriptionStatusUpdate(subId, 'cancelled',
      { membership: { tier: 'SUBSCRIBER' } },
      { cancelled_at: Date.now(), auto_renew: false },
    )
    
    // Atomic payment transaction status append (status_history prevention)
    await adapter.paymentStatusAppend(orderRef, 'paid', { processor_payload: { ... } })
    typescript
    
    const diagnostics = await adapter.diagnoseConnection()
    // => { backendMode, firebaseConfigured, services: { firestore: true, ... }, errors: [] }
    typescript
    
    getUserCreditTransactions: getCachedCreditTransactions, // legacy → SSOT
    getActiveSubscriptions: getCachedSubscriptionsDue,       // legacy → SSOT
    getUserOrders: getCachedUserOrders,                      // legacy → SSOT
    typescript
    
    import { aiGenerateText, aiChatCompletion } from '@/lib/database/adapters/FirebaseAdapter'
    
    // Text generation
    const result = await aiGenerateText({
      prompt: 'What is the Ring Platform?',
      model: 'gemini-2.5-flash',
      temperature: 0.7,
      maxOutputTokens: 1024,
    })
    
    if (result.success) {
      console.log(result.data!.text, result.data!.usage)
    }
    
    // Multi-turn chat
    const chatResult = await aiChatCompletion({
      model: 'gemini-2.5-flash',
      messages: [
        { role: 'system', content: 'You are a Ring Platform expert.' },
        { role: 'user', content: 'How do I set up firebase-full?' },
      ],
    })
    typescript
    
    import { getFirebaseAIClient } from '@/lib/firebase-client'
    
    const ai = await getFirebaseAIClient()
    if (ai) {
      const result = await ai.getGenerativeModel({ model: 'gemini-2.5-flash' }).generateContent('...')
    }
    typescript
    
    import { generateFirebaseJson, getRingHostingRewrites } from '@/lib/database/adapters/FirebaseAdapter'
    import { writeFile } from 'fs/promises'
    
    const config = generateFirebaseJson({
      public: 'out',
      rewrites: getRingHostingRewrites(), // /api/** → /api, /auth/** → /auth, ** → /index.html
    })
    
    await writeFile('firebase.json', JSON.stringify(config, null, 2))
    // Then: firebase deploy --only hosting
    bash
    
    # For ADC (Cloud Run / GKE / Cloud Functions — recommended):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    
    # For explicit cert() fallback (local dev / CI — all three required):
    AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
    AUTH_FIREBASE_CLIENT_EMAIL=your_firebase_client_email
    AUTH_FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
    
    # Optional:
    FIREBASE_DATABASE_URL=https://your-project-default-rtdb.firebaseio.com
    FIREBASE_STORAGE_BUCKET=your-project.appspot.com
    FIREBASE_PRIVATE_KEY_ID=your_firebase_private_key_id
    FIREBASE_CLIENT_ID=your_firebase_client_id
    FIREBASE_FIRESTORE_DEBUG=true
    bash
    
    NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_auth_domain
    NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_firebase_project_id
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_storage_bucket
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_messaging_sender_id
    NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
    NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=G-YCZKPV315E
    NEXT_PUBLIC_FIREBASE_VAPID_KEY=your_vapid_key
    NEXT_PUBLIC_FIREBASE_USE_EMULATOR=1  # Enables Firebase Local Emulator Suite auto-connect