Documentation

    Documentation

    Documentation

    Ring Platform Logo

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

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

    Ring Platform Logo

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

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

    Ring Platform Logo

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

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

    1. /
    2. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 min listen

    1. /
    2. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 min listen

    1. /
    2. /k8s-postgres-fcm Mode

    Updated Jul 7, 20266 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

    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 initializeDatabase()

    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 initializeDatabase()

    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 initializeDatabase()

    Related documentation

    Backend modes and databases

    Data model

    Database selection guide

    Environment variables

    Firebase integration

    Local setup

    Pool owner
    PostgreSQLAdapter.getPgPool()
    Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, ring-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/ring-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
    Pool owner
    PostgreSQLAdapter.getPgPool()
    Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, ring-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/ring-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
    Pool owner
    PostgreSQLAdapter.getPgPool()
    Internal; exposed via getSharedPgPool()

    A 2026-07-07 audit eliminated three rogue pg.Pool instances in feature modules (platform-settings-service.ts, ring-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/ring-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