Documentation

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

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server

    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)

    Documentation

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

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server

    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)

    Documentation

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

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Profile Account Widgets
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Vertical Presets (SSOT)
    Ringization playbook
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel Deployment
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server

    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)
    Ring Platform Logo

    Loading documentation...

    Preparing Ring Platform content

    Ring Platform Logo

    Loading documentation...

    Preparing Ring Platform content

    Ring Platform Logo

    Loading documentation...

    Preparing Ring Platform content

    k8s-postgres-fcm Mode

    k8s-postgres-fcm is the recommended backend mode for production and local development. PostgreSQL handles all application data through one shared adapter pool; Firebase Admin SDK is initialized exclusively for FCM push and Apple Push. Use the Founder / Developer tabs in the docs sidebar to filter this page.

    What k8s-postgres-fcm gives you

    • Full ownership of application data — stored in PostgreSQL 16 on your infrastructure. No Firestore reads or writes.
    • Predictable connection usage — every feature module shares one adapter pool instead of opening private connections. Stable Postgres load as your clone grows.
    • Zero Firebase vendor lock for data — migration or provider change touches only the database connection string, not application code.
    • Optional push notifications — FCM and Apple Push remain available without exposing your database to Firebase.
    • Auth.js v5 integration — authentication tables live in PostgreSQL alongside business data.
    • PostGIS support — spatial queries for delivery radius, entity locations, event venues, and map-heavy clones.
    • Native WebSocket transport — tunnel uses native WSS via TUNNEL_HUB_MODE=k8s-postgres (or in-memory hub for local development).

    When to choose this mode

    ScenarioRecommended mode
    Self-hosted production on k3sk8s-postgres-fcm
    Local development with PostgreSQLk8s-postgres-fcm
    Cloud PostgreSQL (no K8s)supabase-fcm
    Rapid prototyping / Vercel Edgefirebase-full
    Push notifications neededany mode — FCM is optional in all three

    Cost profile

    • PostgreSQL — runs on your own infrastructure (Kubernetes StatefulSet, bare metal, or Docker). No per-query cost.
    • Firebase FCM — push notifications are free at any scale. Firebase project credentials are required only if you enable push.
    • No Firestore costs — Firestore is never used for application data in this mode. getAdminDb() returns a mock that never connects.

    Architecture

    Key decision points in code:

    • shouldUseFirebaseForDatabase() returns false — getAdminDb() returns a mock Firestore from build-mock.server.ts.
    • shouldInitializeFirebaseFCM() returns true — Firebase Admin SDK is initialized for push messaging only.
    • PostgreSQLAdapter owns the only new Pool() in the codebase (lib/database/adapters/PostgreSQLAdapter.ts). Feature modules must not instantiate private pools.
    • getSharedPgPool() (lib/database/shared-pg-pool.ts) is the sanctioned escape hatch for raw SQL (PostGIS). It calls initializeDatabase() then returns getDatabaseService().getPostgreSQLPool().

    Single connection pool (SSOT)

    SurfacePathUse
    CRUD / queriesdb().readDoc, createDoc, updateDoc, queryDocs, findById, query, …All application collections
    Raw SQL (PostGIS)getSharedPgPool()Spatial queries only — after

    Related documentation

    Backend modes and databases

    Data model

    Database selection guide

    Environment variables

    Firebase integration

    Local setup

    k8s-postgres-fcm Mode

    k8s-postgres-fcm is the recommended backend mode for production and local development. PostgreSQL handles all application data through one shared adapter pool; Firebase Admin SDK is initialized exclusively for FCM push and Apple Push. Use the Founder / Developer tabs in the docs sidebar to filter this page.

    What k8s-postgres-fcm gives you

    • Full ownership of application data — stored in PostgreSQL 16 on your infrastructure. No Firestore reads or writes.
    • Predictable connection usage — every feature module shares one adapter pool instead of opening private connections. Stable Postgres load as your clone grows.
    • Zero Firebase vendor lock for data — migration or provider change touches only the database connection string, not application code.
    • Optional push notifications — FCM and Apple Push remain available without exposing your database to Firebase.
    • Auth.js v5 integration — authentication tables live in PostgreSQL alongside business data.
    • PostGIS support — spatial queries for delivery radius, entity locations, event venues, and map-heavy clones.
    • Native WebSocket transport — tunnel uses native WSS via TUNNEL_HUB_MODE=k8s-postgres (or in-memory hub for local development).

    When to choose this mode

    ScenarioRecommended mode
    Self-hosted production on k3sk8s-postgres-fcm
    Local development with PostgreSQLk8s-postgres-fcm
    Cloud PostgreSQL (no K8s)supabase-fcm
    Rapid prototyping / Vercel Edgefirebase-full
    Push notifications neededany mode — FCM is optional in all three

    Cost profile

    • PostgreSQL — runs on your own infrastructure (Kubernetes StatefulSet, bare metal, or Docker). No per-query cost.
    • Firebase FCM — push notifications are free at any scale. Firebase project credentials are required only if you enable push.
    • No Firestore costs — Firestore is never used for application data in this mode. getAdminDb() returns a mock that never connects.

    Architecture

    Key decision points in code:

    • shouldUseFirebaseForDatabase() returns false — getAdminDb() returns a mock Firestore from build-mock.server.ts.
    • shouldInitializeFirebaseFCM() returns true — Firebase Admin SDK is initialized for push messaging only.
    • PostgreSQLAdapter owns the only new Pool() in the codebase (lib/database/adapters/PostgreSQLAdapter.ts). Feature modules must not instantiate private pools.
    • getSharedPgPool() (lib/database/shared-pg-pool.ts) is the sanctioned escape hatch for raw SQL (PostGIS). It calls initializeDatabase() then returns getDatabaseService().getPostgreSQLPool().

    Single connection pool (SSOT)

    SurfacePathUse
    CRUD / queriesdb().readDoc, createDoc, updateDoc, queryDocs, findById, query, …All application collections
    Raw SQL (PostGIS)getSharedPgPool()Spatial queries only — after

    Related documentation

    Backend modes and databases

    Data model

    Database selection guide

    Environment variables

    Firebase integration

    Local setup

    k8s-postgres-fcm Mode

    k8s-postgres-fcm is the recommended backend mode for production and local development. PostgreSQL handles all application data through one shared adapter pool; Firebase Admin SDK is initialized exclusively for FCM push and Apple Push. Use the Founder / Developer tabs in the docs sidebar to filter this page.

    What k8s-postgres-fcm gives you

    • Full ownership of application data — stored in PostgreSQL 16 on your infrastructure. No Firestore reads or writes.
    • Predictable connection usage — every feature module shares one adapter pool instead of opening private connections. Stable Postgres load as your clone grows.
    • Zero Firebase vendor lock for data — migration or provider change touches only the database connection string, not application code.
    • Optional push notifications — FCM and Apple Push remain available without exposing your database to Firebase.
    • Auth.js v5 integration — authentication tables live in PostgreSQL alongside business data.
    • PostGIS support — spatial queries for delivery radius, entity locations, event venues, and map-heavy clones.
    • Native WebSocket transport — tunnel uses native WSS via TUNNEL_HUB_MODE=k8s-postgres (or in-memory hub for local development).

    When to choose this mode

    ScenarioRecommended mode
    Self-hosted production on k3sk8s-postgres-fcm
    Local development with PostgreSQLk8s-postgres-fcm
    Cloud PostgreSQL (no K8s)supabase-fcm
    Rapid prototyping / Vercel Edgefirebase-full
    Push notifications neededany mode — FCM is optional in all three

    Cost profile

    • PostgreSQL — runs on your own infrastructure (Kubernetes StatefulSet, bare metal, or Docker). No per-query cost.
    • Firebase FCM — push notifications are free at any scale. Firebase project credentials are required only if you enable push.
    • No Firestore costs — Firestore is never used for application data in this mode. getAdminDb() returns a mock that never connects.

    Architecture

    Key decision points in code:

    • shouldUseFirebaseForDatabase() returns false — getAdminDb() returns a mock Firestore from build-mock.server.ts.
    • shouldInitializeFirebaseFCM() returns true — Firebase Admin SDK is initialized for push messaging only.
    • PostgreSQLAdapter owns the only new Pool() in the codebase (lib/database/adapters/PostgreSQLAdapter.ts). Feature modules must not instantiate private pools.
    • getSharedPgPool() (lib/database/shared-pg-pool.ts) is the sanctioned escape hatch for raw SQL (PostGIS). It calls initializeDatabase() then returns getDatabaseService().getPostgreSQLPool().

    Single connection pool (SSOT)

    SurfacePathUse
    CRUD / queriesdb().readDoc, createDoc, updateDoc, queryDocs, findById, query, …All application collections
    Raw SQL (PostGIS)getSharedPgPool()Spatial queries only — after

    Related documentation

    Backend modes and databases

    Data model

    Database selection guide

    Environment variables

    Firebase integration

    Local setup

    initializeDatabase()
    Pool ownerPostgreSQLAdapter.getPgPool()Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, native-token-oracle.ts, geolocation-service.ts). Those modules now route through db() or getSharedPgPool(). The validate-provider-ssot.sh gate allows new Pool( only under lib/database/.

    Required environment variables

    VariableRequiredDefaultDescription
    DB_BACKEND_MODEYes—Must be k8s-postgres-fcm
    DB_HOSTYeslocalhostPostgreSQL hostname
    DB_PORTNo5432PostgreSQL port
    DB_NAMEYesring_platformDatabase name
    DB_USERYesring_userDatabase user
    DB_PASSWORDYesring_dev_passwordDatabase password
    DB_POOL_SIZENo20Connection pool max size
    DB_TIMEOUTNo30000Connection timeout (ms)
    DB_RETRIESNo3Connection retry count
    DB_SSLNofalseEnable TLS for PostgreSQL
    DB_ENABLE_POSTGISNofalseRuntime flag; PostGIS extensions ship in data/schema.sql

    JSONB hybrid schema

    Most tables store business fields in a data JSONB column with top-level id, created_at, updated_at. Fully normalized tables (e.g. vendor_applications, vendor_profiles) list all columns in PostgreSQLAdapter.fieldMappings so the query builder uses direct column references.

    Exception — platform_settings hybrid table:

    PostgreSQLAdapter splits writes: public config into data, API keys into secrets, actor into updated_by. Consumers use db().readDoc / createDoc / updateDoc:

    • features/admin/platform-settings/platform-settings-service.ts — AI, branding, matcher namespaces
    • features/wallet/services/native-token-oracle.ts — web3 namespace oracle rates

    PostGIS and geolocation

    PostGIS extensions are created in data/schema.sql (CREATE EXTENSION IF NOT EXISTS postgis). Spatial queries use raw SQL because the doc-model cannot express GEOGRAPHY functions.

    lib/geolocation/geolocation-service.ts calls getSharedPgPool() — not a private Pool or direct DB_HOST / DB_PORT env reads:

    Use cases: store delivery radius, entity locations, opportunity venues, map-heavy clones (pet-friendly places, real estate, events).

    DatabaseService contract

    Prefer db() shorthand from @/lib/database in feature modules. Email CRM routes through features/email-crm/lib/jsonb-collection.ts, which delegates to db().*Doc.

    Local setup

    1. 1

      Start PostgreSQL

    2. 2

      Configure .env.local

    3. 3

      Apply schema and start

      Startup logs should show Database: PostgreSQL. For production-like realtime, set TUNNEL_HUB_MODE=k8s-postgres.

    K8s production setup

    1. Namespace — k8s/namespace.yaml isolates each ring clone.
    2. PostgreSQL — k8s/postgres.yaml deploys Postgres with uuid-ossp and btree_gin; service resolves as postgres.<namespace>.svc.cluster.local.
    3. Secrets / ConfigMap — DB_PASSWORD in Secret; DB_BACKEND_MODE, DB_HOST, pool tuning in ConfigMap (k8s/secrets.yaml).
    4. App deployment — k8s/deployment.yaml with health probes on /api/health, env from ConfigMap + Secret.
    5. Ingress — k8s/ingress.yaml with TLS, WebSocket support, rate limiting.

    FCM wiring notes

    1. Admin SDK — initialized when AUTH_FIREBASE_* env vars are present.
    2. Mock Firestore / Auth — getAdminDb() and getAdminAuth() return mocks; Auth.js v5 handles authentication.
    3. Client-side FCM — service worker (public/firebase-messaging-sw.js) connects to Firebase for push subscription regardless of backend mode.
    4. FCM tokens — stored in PostgreSQL fcm_tokens table via Server Action upsert.
    5. No Firebase env vars = no push — PostgreSQL still works.

    Comparison: k8s-postgres-fcm vs supabase-fcm

    Aspectk8s-postgres-fcmsupabase-fcm
    PostgreSQL hostYour K8s or localSupabase cloud
    SSLOptional (DB_SSL=false default)Required (DB_SSL=true)
    Connection poolDB_POOL_SIZE=20 defaultDB_POOL_SIZE=10 default
    Auth credentialsDB_USER / DB_PASSWORDSUPABASE_URL / SUPABASE_SERVICE_KEY
    Deploy targetK8s, Docker, bare metalSupabase platform
    Tunnel hubk8s-postgres or memorymemory
    AdapterSame PostgreSQLAdapter + shared poolSame PostgreSQLAdapter + shared pool
    text
    
    ┌─────────────────────────────────────────────────────────────┐
    │                     Next.js 16 App                           │
    │                                                              │
    │  ┌──────────────────────┐  ┌─────────────────────────────┐  │
    │  │  db() / DatabaseService│  │  Firebase Admin (FCM only) │  │
    │  │  PostgreSQLAdapter     │  │  getAdminDb() → mock        │  │
    │  │  • single pg.Pool      │  │  getAdminAuth() → mock      │  │
    │  │  • all CRUD / query    │  └─────────────────────────────┘  │
    │  │  • getSharedPgPool()   │                                   │
    │  │    (PostGIS escape)    │                                   │
    │  └──────────┬─────────────┘                                   │
    └─────────────┼─────────────────────────────────────────────────┘
                  │
                  ▼
         ┌─────────────────┐
         │  PostgreSQL 16   │
         │  + PostGIS       │
         │  (k8s or local)  │
         └─────────────────┘
    bash
    
    # Mode selection (REQUIRED — platform will not start without it)
    DB_BACKEND_MODE=k8s-postgres-fcm
    
    # PostgreSQL connection
    DB_HOST=localhost               # K8s: postgres.<namespace>.svc.cluster.local
    DB_PORT=5432
    DB_NAME=ring_platform
    DB_USER=ring_user
    DB_PASSWORD=ring_password_2024
    
    # Connection pool tuning (optional)
    DB_POOL_SIZE=20
    DB_TIMEOUT=30000
    DB_RETRIES=3
    DB_SSL=false
    DB_ENABLE_POSTGIS=false
    sql
    
    CREATE TABLE IF NOT EXISTS platform_settings (
        id VARCHAR(64) PRIMARY KEY,
        data JSONB NOT NULL DEFAULT '{}',
        secrets JSONB NOT NULL DEFAULT '{}',
        updated_by VARCHAR(255),
        created_at TIMESTAMPTZ DEFAULT NOW(),
        updated_at TIMESTAMPTZ DEFAULT NOW()
    );
    typescript
    
    import { getSharedPgPool } from '@/lib/database/shared-pg-pool'
    
    const pool = await getSharedPgPool()
    const result = await pool.query(sql, params)
    typescript
    
    import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
    
    await initializeDatabase()
    const db = getDatabaseService()
    
    const user = await db.findById('users', userId)
    await db.create('entities', { name: 'Acme', status: 'active' })
    
    await db.transaction(async (txn) => {
      await txn.create('orders', { total: 100 })
    })
    bash
    
    kubectl apply -f k8s/namespace.yaml
    kubectl apply -f k8s/postgres.yaml
    kubectl apply -f k8s/secrets.yaml
    kubectl apply -f k8s/deployment.yaml
    kubectl apply -f k8s/service.yaml
    kubectl apply -f k8s/ingress.yaml
    kubectl apply -f k8s/pvc-local-file-storage.yaml
    initializeDatabase()
    Pool ownerPostgreSQLAdapter.getPgPool()Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, native-token-oracle.ts, geolocation-service.ts). Those modules now route through db() or getSharedPgPool(). The validate-provider-ssot.sh gate allows new Pool( only under lib/database/.

    Required environment variables

    VariableRequiredDefaultDescription
    DB_BACKEND_MODEYes—Must be k8s-postgres-fcm
    DB_HOSTYeslocalhostPostgreSQL hostname
    DB_PORTNo5432PostgreSQL port
    DB_NAMEYesring_platformDatabase name
    DB_USERYesring_userDatabase user
    DB_PASSWORDYesring_dev_passwordDatabase password
    DB_POOL_SIZENo20Connection pool max size
    DB_TIMEOUTNo30000Connection timeout (ms)
    DB_RETRIESNo3Connection retry count
    DB_SSLNofalseEnable TLS for PostgreSQL
    DB_ENABLE_POSTGISNofalseRuntime flag; PostGIS extensions ship in data/schema.sql

    JSONB hybrid schema

    Most tables store business fields in a data JSONB column with top-level id, created_at, updated_at. Fully normalized tables (e.g. vendor_applications, vendor_profiles) list all columns in PostgreSQLAdapter.fieldMappings so the query builder uses direct column references.

    Exception — platform_settings hybrid table:

    PostgreSQLAdapter splits writes: public config into data, API keys into secrets, actor into updated_by. Consumers use db().readDoc / createDoc / updateDoc:

    • features/admin/platform-settings/platform-settings-service.ts — AI, branding, matcher namespaces
    • features/wallet/services/native-token-oracle.ts — web3 namespace oracle rates

    PostGIS and geolocation

    PostGIS extensions are created in data/schema.sql (CREATE EXTENSION IF NOT EXISTS postgis). Spatial queries use raw SQL because the doc-model cannot express GEOGRAPHY functions.

    lib/geolocation/geolocation-service.ts calls getSharedPgPool() — not a private Pool or direct DB_HOST / DB_PORT env reads:

    Use cases: store delivery radius, entity locations, opportunity venues, map-heavy clones (pet-friendly places, real estate, events).

    DatabaseService contract

    Prefer db() shorthand from @/lib/database in feature modules. Email CRM routes through features/email-crm/lib/jsonb-collection.ts, which delegates to db().*Doc.

    Local setup

    1. 1

      Start PostgreSQL

    2. 2

      Configure .env.local

    3. 3

      Apply schema and start

      Startup logs should show Database: PostgreSQL. For production-like realtime, set TUNNEL_HUB_MODE=k8s-postgres.

    K8s production setup

    1. Namespace — k8s/namespace.yaml isolates each ring clone.
    2. PostgreSQL — k8s/postgres.yaml deploys Postgres with uuid-ossp and btree_gin; service resolves as postgres.<namespace>.svc.cluster.local.
    3. Secrets / ConfigMap — DB_PASSWORD in Secret; DB_BACKEND_MODE, DB_HOST, pool tuning in ConfigMap (k8s/secrets.yaml).
    4. App deployment — k8s/deployment.yaml with health probes on /api/health, env from ConfigMap + Secret.
    5. Ingress — k8s/ingress.yaml with TLS, WebSocket support, rate limiting.

    FCM wiring notes

    1. Admin SDK — initialized when AUTH_FIREBASE_* env vars are present.
    2. Mock Firestore / Auth — getAdminDb() and getAdminAuth() return mocks; Auth.js v5 handles authentication.
    3. Client-side FCM — service worker (public/firebase-messaging-sw.js) connects to Firebase for push subscription regardless of backend mode.
    4. FCM tokens — stored in PostgreSQL fcm_tokens table via Server Action upsert.
    5. No Firebase env vars = no push — PostgreSQL still works.

    Comparison: k8s-postgres-fcm vs supabase-fcm

    Aspectk8s-postgres-fcmsupabase-fcm
    PostgreSQL hostYour K8s or localSupabase cloud
    SSLOptional (DB_SSL=false default)Required (DB_SSL=true)
    Connection poolDB_POOL_SIZE=20 defaultDB_POOL_SIZE=10 default
    Auth credentialsDB_USER / DB_PASSWORDSUPABASE_URL / SUPABASE_SERVICE_KEY
    Deploy targetK8s, Docker, bare metalSupabase platform
    Tunnel hubk8s-postgres or memorymemory
    AdapterSame PostgreSQLAdapter + shared poolSame PostgreSQLAdapter + shared pool
    text
    
    ┌─────────────────────────────────────────────────────────────┐
    │                     Next.js 16 App                           │
    │                                                              │
    │  ┌──────────────────────┐  ┌─────────────────────────────┐  │
    │  │  db() / DatabaseService│  │  Firebase Admin (FCM only) │  │
    │  │  PostgreSQLAdapter     │  │  getAdminDb() → mock        │  │
    │  │  • single pg.Pool      │  │  getAdminAuth() → mock      │  │
    │  │  • all CRUD / query    │  └─────────────────────────────┘  │
    │  │  • getSharedPgPool()   │                                   │
    │  │    (PostGIS escape)    │                                   │
    │  └──────────┬─────────────┘                                   │
    └─────────────┼─────────────────────────────────────────────────┘
                  │
                  ▼
         ┌─────────────────┐
         │  PostgreSQL 16   │
         │  + PostGIS       │
         │  (k8s or local)  │
         └─────────────────┘
    bash
    
    # Mode selection (REQUIRED — platform will not start without it)
    DB_BACKEND_MODE=k8s-postgres-fcm
    
    # PostgreSQL connection
    DB_HOST=localhost               # K8s: postgres.<namespace>.svc.cluster.local
    DB_PORT=5432
    DB_NAME=ring_platform
    DB_USER=ring_user
    DB_PASSWORD=ring_password_2024
    
    # Connection pool tuning (optional)
    DB_POOL_SIZE=20
    DB_TIMEOUT=30000
    DB_RETRIES=3
    DB_SSL=false
    DB_ENABLE_POSTGIS=false
    sql
    
    CREATE TABLE IF NOT EXISTS platform_settings (
        id VARCHAR(64) PRIMARY KEY,
        data JSONB NOT NULL DEFAULT '{}',
        secrets JSONB NOT NULL DEFAULT '{}',
        updated_by VARCHAR(255),
        created_at TIMESTAMPTZ DEFAULT NOW(),
        updated_at TIMESTAMPTZ DEFAULT NOW()
    );
    typescript
    
    import { getSharedPgPool } from '@/lib/database/shared-pg-pool'
    
    const pool = await getSharedPgPool()
    const result = await pool.query(sql, params)
    typescript
    
    import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
    
    await initializeDatabase()
    const db = getDatabaseService()
    
    const user = await db.findById('users', userId)
    await db.create('entities', { name: 'Acme', status: 'active' })
    
    await db.transaction(async (txn) => {
      await txn.create('orders', { total: 100 })
    })
    bash
    
    kubectl apply -f k8s/namespace.yaml
    kubectl apply -f k8s/postgres.yaml
    kubectl apply -f k8s/secrets.yaml
    kubectl apply -f k8s/deployment.yaml
    kubectl apply -f k8s/service.yaml
    kubectl apply -f k8s/ingress.yaml
    kubectl apply -f k8s/pvc-local-file-storage.yaml
    initializeDatabase()
    Pool ownerPostgreSQLAdapter.getPgPool()Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, native-token-oracle.ts, geolocation-service.ts). Those modules now route through db() or getSharedPgPool(). The validate-provider-ssot.sh gate allows new Pool( only under lib/database/.

    Required environment variables

    VariableRequiredDefaultDescription
    DB_BACKEND_MODEYes—Must be k8s-postgres-fcm
    DB_HOSTYeslocalhostPostgreSQL hostname
    DB_PORTNo5432PostgreSQL port
    DB_NAMEYesring_platformDatabase name
    DB_USERYesring_userDatabase user
    DB_PASSWORDYesring_dev_passwordDatabase password
    DB_POOL_SIZENo20Connection pool max size
    DB_TIMEOUTNo30000Connection timeout (ms)
    DB_RETRIESNo3Connection retry count
    DB_SSLNofalseEnable TLS for PostgreSQL
    DB_ENABLE_POSTGISNofalseRuntime flag; PostGIS extensions ship in data/schema.sql

    JSONB hybrid schema

    Most tables store business fields in a data JSONB column with top-level id, created_at, updated_at. Fully normalized tables (e.g. vendor_applications, vendor_profiles) list all columns in PostgreSQLAdapter.fieldMappings so the query builder uses direct column references.

    Exception — platform_settings hybrid table:

    PostgreSQLAdapter splits writes: public config into data, API keys into secrets, actor into updated_by. Consumers use db().readDoc / createDoc / updateDoc:

    • features/admin/platform-settings/platform-settings-service.ts — AI, branding, matcher namespaces
    • features/wallet/services/native-token-oracle.ts — web3 namespace oracle rates

    PostGIS and geolocation

    PostGIS extensions are created in data/schema.sql (CREATE EXTENSION IF NOT EXISTS postgis). Spatial queries use raw SQL because the doc-model cannot express GEOGRAPHY functions.

    lib/geolocation/geolocation-service.ts calls getSharedPgPool() — not a private Pool or direct DB_HOST / DB_PORT env reads:

    Use cases: store delivery radius, entity locations, opportunity venues, map-heavy clones (pet-friendly places, real estate, events).

    DatabaseService contract

    Prefer db() shorthand from @/lib/database in feature modules. Email CRM routes through features/email-crm/lib/jsonb-collection.ts, which delegates to db().*Doc.

    Local setup

    1. 1

      Start PostgreSQL

    2. 2

      Configure .env.local

    3. 3

      Apply schema and start

      Startup logs should show Database: PostgreSQL. For production-like realtime, set TUNNEL_HUB_MODE=k8s-postgres.

    K8s production setup

    1. Namespace — k8s/namespace.yaml isolates each ring clone.
    2. PostgreSQL — k8s/postgres.yaml deploys Postgres with uuid-ossp and btree_gin; service resolves as postgres.<namespace>.svc.cluster.local.
    3. Secrets / ConfigMap — DB_PASSWORD in Secret; DB_BACKEND_MODE, DB_HOST, pool tuning in ConfigMap (k8s/secrets.yaml).
    4. App deployment — k8s/deployment.yaml with health probes on /api/health, env from ConfigMap + Secret.
    5. Ingress — k8s/ingress.yaml with TLS, WebSocket support, rate limiting.

    FCM wiring notes

    1. Admin SDK — initialized when AUTH_FIREBASE_* env vars are present.
    2. Mock Firestore / Auth — getAdminDb() and getAdminAuth() return mocks; Auth.js v5 handles authentication.
    3. Client-side FCM — service worker (public/firebase-messaging-sw.js) connects to Firebase for push subscription regardless of backend mode.
    4. FCM tokens — stored in PostgreSQL fcm_tokens table via Server Action upsert.
    5. No Firebase env vars = no push — PostgreSQL still works.

    Comparison: k8s-postgres-fcm vs supabase-fcm

    Aspectk8s-postgres-fcmsupabase-fcm
    PostgreSQL hostYour K8s or localSupabase cloud
    SSLOptional (DB_SSL=false default)Required (DB_SSL=true)
    Connection poolDB_POOL_SIZE=20 defaultDB_POOL_SIZE=10 default
    Auth credentialsDB_USER / DB_PASSWORDSUPABASE_URL / SUPABASE_SERVICE_KEY
    Deploy targetK8s, Docker, bare metalSupabase platform
    Tunnel hubk8s-postgres or memorymemory
    AdapterSame PostgreSQLAdapter + shared poolSame PostgreSQLAdapter + shared pool
    text
    
    ┌─────────────────────────────────────────────────────────────┐
    │                     Next.js 16 App                           │
    │                                                              │
    │  ┌──────────────────────┐  ┌─────────────────────────────┐  │
    │  │  db() / DatabaseService│  │  Firebase Admin (FCM only) │  │
    │  │  PostgreSQLAdapter     │  │  getAdminDb() → mock        │  │
    │  │  • single pg.Pool      │  │  getAdminAuth() → mock      │  │
    │  │  • all CRUD / query    │  └─────────────────────────────┘  │
    │  │  • getSharedPgPool()   │                                   │
    │  │    (PostGIS escape)    │                                   │
    │  └──────────┬─────────────┘                                   │
    └─────────────┼─────────────────────────────────────────────────┘
                  │
                  ▼
         ┌─────────────────┐
         │  PostgreSQL 16   │
         │  + PostGIS       │
         │  (k8s or local)  │
         └─────────────────┘
    bash
    
    # Mode selection (REQUIRED — platform will not start without it)
    DB_BACKEND_MODE=k8s-postgres-fcm
    
    # PostgreSQL connection
    DB_HOST=localhost               # K8s: postgres.<namespace>.svc.cluster.local
    DB_PORT=5432
    DB_NAME=ring_platform
    DB_USER=ring_user
    DB_PASSWORD=ring_password_2024
    
    # Connection pool tuning (optional)
    DB_POOL_SIZE=20
    DB_TIMEOUT=30000
    DB_RETRIES=3
    DB_SSL=false
    DB_ENABLE_POSTGIS=false
    sql
    
    CREATE TABLE IF NOT EXISTS platform_settings (
        id VARCHAR(64) PRIMARY KEY,
        data JSONB NOT NULL DEFAULT '{}',
        secrets JSONB NOT NULL DEFAULT '{}',
        updated_by VARCHAR(255),
        created_at TIMESTAMPTZ DEFAULT NOW(),
        updated_at TIMESTAMPTZ DEFAULT NOW()
    );
    typescript
    
    import { getSharedPgPool } from '@/lib/database/shared-pg-pool'
    
    const pool = await getSharedPgPool()
    const result = await pool.query(sql, params)
    typescript
    
    import { initializeDatabase, getDatabaseService } from '@/lib/database/DatabaseService'
    
    await initializeDatabase()
    const db = getDatabaseService()
    
    const user = await db.findById('users', userId)
    await db.create('entities', { name: 'Acme', status: 'active' })
    
    await db.transaction(async (txn) => {
      await txn.create('orders', { total: 100 })
    })
    bash
    
    kubectl apply -f k8s/namespace.yaml
    kubectl apply -f k8s/postgres.yaml
    kubectl apply -f k8s/secrets.yaml
    kubectl apply -f k8s/deployment.yaml
    kubectl apply -f k8s/service.yaml
    kubectl apply -f k8s/ingress.yaml
    kubectl apply -f k8s/pvc-local-file-storage.yaml
    1. Docs
    2. /Backend Services
    3. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 min listen

    1. Docs
    2. /Backend Services
    3. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 min listen

    1. Docs
    2. /Backend Services
    3. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 min listen