OpportunitiesEntities
Docs
    Ring Platform

    Decentralized Self-building Future

    Sign In
    Entities
    Opportunities
    Store
    Docs
    Platform Concepts
    RING EconomySonoratek LLCGlobal ImpactAI Meets Web3
    Get Started
    Quick StartCalculatorRoadmap
    Privacy|Contact
    v1.106.26|Sonoratek LLC

    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
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    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
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    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
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    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
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    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
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    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
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    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
    Membership & Credits
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Credit Rewards
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    Page Curtain
    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
    Project configuration
    Public environment variables
    Order Lab secrets
    WalletConnect Project ID (Reown Cloud)
    Supported services
    NODUS wiki (project knowledge)
    Configuration playbook
    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
    News Kingdom architecture
    Backend Services
    Firebase Integration
    Development
    Ring MCP Server
    OSS vs enterprise

    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 Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

    Page Curtain

    The page curtain is a slim banner (about 10% of the screen height) that drops in after a visitor stops scrolling. It shows one of two kinds of offer: Add to Home Screen for the app, or a product a vendor has chosen to advertise. On news articles and member blog posts, a product added from the curtain can earn the page's author a commission when the shopper later pays.

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.

    Status (2026-10-26): Shipped in Layer 1 and configurable per clone through the curtain block in ring-config.json. The queue now honors earlier dismissals, so a visitor who chose Not today does not see the same banner again on the next page.

    Without the curtainWith the curtain
    Install prompts depend on the browserRing offers Add to Home Screen itself, once per policy window
    Authors cannot earn from products they mentionAuthors earn additive credit on paid carts that started from their page
    Vendors pay for ads elsewhereVendors opt a product in and set a 1% to 10% author commission

    Why this matters for your community

    Writers bring readers; vendors want buyers. The curtain connects the two without a separate ad system. A vendor flags a product, a writer links it or writes about the same topic, and when a reader buys it, the writer is rewarded in platform credit. The vendor's payout is not reduced: the commission is minted as extra credit.

    News

    Article pages are where product offers appear.

    Member Blog

    Member posts get the same product offers and author commission.

    Store

    Vendors switch on curtain advertising in the product form.

    Typical scenarios

    • A food blogger links a farm's honey in an article. Readers see the honey in the curtain, tap Add to cart, and pay within 30 days. The blogger receives 5% of that line as credit.
    • A first-time visitor on mobile reads three pages. On one of them the curtain offers Add to Home Screen. They tap Not today and do not see it again for 24 hours.
    • A reader taps Not for me on a product. That product never drops for them again.
    From article to author commission

    Where to watch results

    • Vendors: the earnings page shows a 7 day curtain funnel: shown, resurfaced, CTA, not today, not for me, scroll away, paid, and credits minted.
    • Authors: My News (/my-news) shows the same 7 day funnel for their own pages plus curtain credits earned.
    • Operators: the admin analytics page shows the same funnel for the whole clone, next to the active curtain policy.

    Configuration

    ring-config.template.json ships these defaults. Override any key in your clone's ring-config.json. Values are read by getCurtainPolicy() in lib/ring-config-core.ts and validated by parseCurtainPolicy in features/curtain/lib/policy.ts.

    KeyDefaultAllowed values
    curtain.enabledtruefalse turns every offer off
    curtain.productSourcepage_then_poolpage_then_pool, page_only, pool_only
    curtain.maxImpressionsPerDay51 to 50 per offer, per UTC day
    curtain.notTodayHours241 to 168

    How offers are chosen

    in returns up to three offers ():

    Related documentation

    Multi-Vendor Store

    Depends-on: products, cart and paid orders that trigger commission.

    News Module

    Same-workflow: article pages that host product offers.

    Member Blogs

    Same-workflow: member posts host the same offers.

    Credit Rewards

    See-also: credit rewards that must be enabled for commission.

    Referral Codes (Refcodes)

    1. Docs
    2. /Features
    3. /Page Curtain

    Updated Oct 26, 20265 min listen

    Page Curtain

    The page curtain is a slim banner (about 10% of the screen height) that drops in after a visitor stops scrolling. It shows one of two kinds of offer: Add to Home Screen for the app, or a product a vendor has chosen to advertise. On news articles and member blog posts, a product added from the curtain can earn the page's author a commission when the shopper later pays.

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.

    Status (2026-10-26): Shipped in Layer 1 and configurable per clone through the curtain block in ring-config.json. The queue now honors earlier dismissals, so a visitor who chose Not today does not see the same banner again on the next page.

    Without the curtainWith the curtain
    Install prompts depend on the browserRing offers Add to Home Screen itself, once per policy window
    Authors cannot earn from products they mentionAuthors earn additive credit on paid carts that started from their page
    Vendors pay for ads elsewhereVendors opt a product in and set a 1% to 10% author commission

    Why this matters for your community

    Writers bring readers; vendors want buyers. The curtain connects the two without a separate ad system. A vendor flags a product, a writer links it or writes about the same topic, and when a reader buys it, the writer is rewarded in platform credit. The vendor's payout is not reduced: the commission is minted as extra credit.

    News

    Article pages are where product offers appear.

    Member Blog

    Member posts get the same product offers and author commission.

    Store

    Vendors switch on curtain advertising in the product form.

    Typical scenarios

    • A food blogger links a farm's honey in an article. Readers see the honey in the curtain, tap Add to cart, and pay within 30 days. The blogger receives 5% of that line as credit.
    • A first-time visitor on mobile reads three pages. On one of them the curtain offers Add to Home Screen. They tap Not today and do not see it again for 24 hours.
    • A reader taps Not for me on a product. That product never drops for them again.
    From article to author commission

    Where to watch results

    • Vendors: the earnings page shows a 7 day curtain funnel: shown, resurfaced, CTA, not today, not for me, scroll away, paid, and credits minted.
    • Authors: My News (/my-news) shows the same 7 day funnel for their own pages plus curtain credits earned.
    • Operators: the admin analytics page shows the same funnel for the whole clone, next to the active curtain policy.

    Configuration

    ring-config.template.json ships these defaults. Override any key in your clone's ring-config.json. Values are read by getCurtainPolicy() in lib/ring-config-core.ts and validated by parseCurtainPolicy in features/curtain/lib/policy.ts.

    KeyDefaultAllowed values
    curtain.enabledtruefalse turns every offer off
    curtain.productSourcepage_then_poolpage_then_pool, page_only, pool_only
    curtain.maxImpressionsPerDay51 to 50 per offer, per UTC day
    curtain.notTodayHours241 to 168

    How offers are chosen

    in returns up to three offers ():

    Related documentation

    Multi-Vendor Store

    Depends-on: products, cart and paid orders that trigger commission.

    News Module

    Same-workflow: article pages that host product offers.

    Member Blogs

    Same-workflow: member posts host the same offers.

    Credit Rewards

    See-also: credit rewards that must be enabled for commission.

    Referral Codes (Refcodes)

    1. Docs
    2. /Features
    3. /Page Curtain

    Updated Oct 26, 20265 min listen

    Page Curtain

    The page curtain is a slim banner (about 10% of the screen height) that drops in after a visitor stops scrolling. It shows one of two kinds of offer: Add to Home Screen for the app, or a product a vendor has chosen to advertise. On news articles and member blog posts, a product added from the curtain can earn the page's author a commission when the shopper later pays.

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Shared sections apply to both audiences.

    Status (2026-10-26): Shipped in Layer 1 and configurable per clone through the curtain block in ring-config.json. The queue now honors earlier dismissals, so a visitor who chose Not today does not see the same banner again on the next page.

    Without the curtainWith the curtain
    Install prompts depend on the browserRing offers Add to Home Screen itself, once per policy window
    Authors cannot earn from products they mentionAuthors earn additive credit on paid carts that started from their page
    Vendors pay for ads elsewhereVendors opt a product in and set a 1% to 10% author commission

    Why this matters for your community

    Writers bring readers; vendors want buyers. The curtain connects the two without a separate ad system. A vendor flags a product, a writer links it or writes about the same topic, and when a reader buys it, the writer is rewarded in platform credit. The vendor's payout is not reduced: the commission is minted as extra credit.

    News

    Article pages are where product offers appear.

    Member Blog

    Member posts get the same product offers and author commission.

    Store

    Vendors switch on curtain advertising in the product form.

    Typical scenarios

    • A food blogger links a farm's honey in an article. Readers see the honey in the curtain, tap Add to cart, and pay within 30 days. The blogger receives 5% of that line as credit.
    • A first-time visitor on mobile reads three pages. On one of them the curtain offers Add to Home Screen. They tap Not today and do not see it again for 24 hours.
    • A reader taps Not for me on a product. That product never drops for them again.
    From article to author commission

    Where to watch results

    • Vendors: the earnings page shows a 7 day curtain funnel: shown, resurfaced, CTA, not today, not for me, scroll away, paid, and credits minted.
    • Authors: My News (/my-news) shows the same 7 day funnel for their own pages plus curtain credits earned.
    • Operators: the admin analytics page shows the same funnel for the whole clone, next to the active curtain policy.

    Configuration

    ring-config.template.json ships these defaults. Override any key in your clone's ring-config.json. Values are read by getCurtainPolicy() in lib/ring-config-core.ts and validated by parseCurtainPolicy in features/curtain/lib/policy.ts.

    KeyDefaultAllowed values
    curtain.enabledtruefalse turns every offer off
    curtain.productSourcepage_then_poolpage_then_pool, page_only, pool_only
    curtain.maxImpressionsPerDay51 to 50 per offer, per UTC day
    curtain.notTodayHours241 to 168

    How offers are chosen

    in returns up to three offers ():

    Related documentation

    Multi-Vendor Store

    Depends-on: products, cart and paid orders that trigger commission.

    News Module

    Same-workflow: article pages that host product offers.

    Member Blogs

    Same-workflow: member posts host the same offers.

    Credit Rewards

    See-also: credit rewards that must be enabled for commission.

    Referral Codes (Refcodes)

    1. Docs
    2. /Features
    3. /Page Curtain

    Updated Oct 26, 20265 min listen

    requestCurtainDrop
    features/curtain/actions.ts
    CURTAIN_QUEUE_MAX
    1. Skip entirely when the policy is off or the path starts with /admin, /login, /register, /auth, /vendor or /store/checkout.
    2. Product offers only on pages that pass a newsArticleId through CurtainPageProvider (news articles and member blog posts). The article must be published and visible to the viewer under canViewNewsArticle.
    3. Page products are /store/<id> links found in the article body. Pool products are active products with curtainAdvertiseEnabled. Each must be advertised, active, priced, not the viewer's own vendor product, and not suppressed.
    4. Sort: linked products first, then topic overlap with the article's category and tags, then fewer impressions today.
    5. Fill a remaining slot with Add to Home Screen when the browser is not already running the installed app.

    curtain-host.tsx shows the queue and applies canShowOffer to every queued item, which is the fix for dismissed banners coming back.

    Visitor responses

    ResponseEffect
    CTAAdd to cart (product) or install prompt (A2HS)
    Not todaySuppressed until notTodayHours from now
    Not for meSuppressed forever for that offer
    Scroll awayLogged, no suppression

    Preferences live in localStorage (ring_curtain_prefs) and, for signed-in members, in the curtain_preferences table (migration 050, part of schema.sql) via syncCurtainPrefs.

    Attribution and commission

    • Each product offer carries an HMAC offerToken from features/curtain/lib/offer-token.ts, signed with AUTH_SECRET. It binds offer, product, author and article.
    • CTA touches are kept in the ring_curtain_attr cookie and storage (30 day window, at most 12 touches).
    • Checkout keeps only touches that pass verifyCurtainTouches. Author and percent come from the token and the live product, never from the browser.
    • After payment, mintCurtainAuthorCommission in features/curtain/services/commission.ts adds credit to the author: line subtotal times the product's percent (1 to 10, default 5). It skips self-purchase, the order's referrer, duplicates, and runs only when credit rewards are enabled.
    • Events curtain_shown, curtain_response, curtain_cta, curtain_suppress and curtain_paid feed getCurtainFunnel in features/analytics/lib/curtain-analytics.ts.

    Vendor opt-in

    The vendor product form includes AdvertiseProductWidget (features/curtain/components/advertise-product-widget.tsx). It writes curtainAdvertise: { enabled, commissionPercent } and curtainAdvertiseEnabled on the product. A product without a price cannot be advertised.

    Frequently asked questions

    Impact

    Does the commission come out of the vendor's payout?

    No. It is additive credit minted to the author, recorded with source curtain_author.

    Migration

    Do I need a migration?

    Not on a fresh install. Existing databases need 050_curtain_offers.sql for curtain_preferences and the analytics index.

    Ops

    How do I turn the curtain off for one clone?

    Set "curtain": { "enabled": false } in that clone's ring-config.json and rebuild.

    See-also: referral attribution, which shares the 30 day window.

    requestCurtainDrop
    features/curtain/actions.ts
    CURTAIN_QUEUE_MAX
    1. Skip entirely when the policy is off or the path starts with /admin, /login, /register, /auth, /vendor or /store/checkout.
    2. Product offers only on pages that pass a newsArticleId through CurtainPageProvider (news articles and member blog posts). The article must be published and visible to the viewer under canViewNewsArticle.
    3. Page products are /store/<id> links found in the article body. Pool products are active products with curtainAdvertiseEnabled. Each must be advertised, active, priced, not the viewer's own vendor product, and not suppressed.
    4. Sort: linked products first, then topic overlap with the article's category and tags, then fewer impressions today.
    5. Fill a remaining slot with Add to Home Screen when the browser is not already running the installed app.

    curtain-host.tsx shows the queue and applies canShowOffer to every queued item, which is the fix for dismissed banners coming back.

    Visitor responses

    ResponseEffect
    CTAAdd to cart (product) or install prompt (A2HS)
    Not todaySuppressed until notTodayHours from now
    Not for meSuppressed forever for that offer
    Scroll awayLogged, no suppression

    Preferences live in localStorage (ring_curtain_prefs) and, for signed-in members, in the curtain_preferences table (migration 050, part of schema.sql) via syncCurtainPrefs.

    Attribution and commission

    • Each product offer carries an HMAC offerToken from features/curtain/lib/offer-token.ts, signed with AUTH_SECRET. It binds offer, product, author and article.
    • CTA touches are kept in the ring_curtain_attr cookie and storage (30 day window, at most 12 touches).
    • Checkout keeps only touches that pass verifyCurtainTouches. Author and percent come from the token and the live product, never from the browser.
    • After payment, mintCurtainAuthorCommission in features/curtain/services/commission.ts adds credit to the author: line subtotal times the product's percent (1 to 10, default 5). It skips self-purchase, the order's referrer, duplicates, and runs only when credit rewards are enabled.
    • Events curtain_shown, curtain_response, curtain_cta, curtain_suppress and curtain_paid feed getCurtainFunnel in features/analytics/lib/curtain-analytics.ts.

    Vendor opt-in

    The vendor product form includes AdvertiseProductWidget (features/curtain/components/advertise-product-widget.tsx). It writes curtainAdvertise: { enabled, commissionPercent } and curtainAdvertiseEnabled on the product. A product without a price cannot be advertised.

    Frequently asked questions

    Impact

    Does the commission come out of the vendor's payout?

    No. It is additive credit minted to the author, recorded with source curtain_author.

    Migration

    Do I need a migration?

    Not on a fresh install. Existing databases need 050_curtain_offers.sql for curtain_preferences and the analytics index.

    Ops

    How do I turn the curtain off for one clone?

    Set "curtain": { "enabled": false } in that clone's ring-config.json and rebuild.

    See-also: referral attribution, which shares the 30 day window.

    requestCurtainDrop
    features/curtain/actions.ts
    CURTAIN_QUEUE_MAX
    1. Skip entirely when the policy is off or the path starts with /admin, /login, /register, /auth, /vendor or /store/checkout.
    2. Product offers only on pages that pass a newsArticleId through CurtainPageProvider (news articles and member blog posts). The article must be published and visible to the viewer under canViewNewsArticle.
    3. Page products are /store/<id> links found in the article body. Pool products are active products with curtainAdvertiseEnabled. Each must be advertised, active, priced, not the viewer's own vendor product, and not suppressed.
    4. Sort: linked products first, then topic overlap with the article's category and tags, then fewer impressions today.
    5. Fill a remaining slot with Add to Home Screen when the browser is not already running the installed app.

    curtain-host.tsx shows the queue and applies canShowOffer to every queued item, which is the fix for dismissed banners coming back.

    Visitor responses

    ResponseEffect
    CTAAdd to cart (product) or install prompt (A2HS)
    Not todaySuppressed until notTodayHours from now
    Not for meSuppressed forever for that offer
    Scroll awayLogged, no suppression

    Preferences live in localStorage (ring_curtain_prefs) and, for signed-in members, in the curtain_preferences table (migration 050, part of schema.sql) via syncCurtainPrefs.

    Attribution and commission

    • Each product offer carries an HMAC offerToken from features/curtain/lib/offer-token.ts, signed with AUTH_SECRET. It binds offer, product, author and article.
    • CTA touches are kept in the ring_curtain_attr cookie and storage (30 day window, at most 12 touches).
    • Checkout keeps only touches that pass verifyCurtainTouches. Author and percent come from the token and the live product, never from the browser.
    • After payment, mintCurtainAuthorCommission in features/curtain/services/commission.ts adds credit to the author: line subtotal times the product's percent (1 to 10, default 5). It skips self-purchase, the order's referrer, duplicates, and runs only when credit rewards are enabled.
    • Events curtain_shown, curtain_response, curtain_cta, curtain_suppress and curtain_paid feed getCurtainFunnel in features/analytics/lib/curtain-analytics.ts.

    Vendor opt-in

    The vendor product form includes AdvertiseProductWidget (features/curtain/components/advertise-product-widget.tsx). It writes curtainAdvertise: { enabled, commissionPercent } and curtainAdvertiseEnabled on the product. A product without a price cannot be advertised.

    Frequently asked questions

    Impact

    Does the commission come out of the vendor's payout?

    No. It is additive credit minted to the author, recorded with source curtain_author.

    Migration

    Do I need a migration?

    Not on a fresh install. Existing databases need 050_curtain_offers.sql for curtain_preferences and the analytics index.

    Ops

    How do I turn the curtain off for one clone?

    Set "curtain": { "enabled": false } in that clone's ring-config.json and rebuild.

    See-also: referral attribution, which shares the 30 day window.