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.104.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
    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)
    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
    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)
    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
    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)
    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

    News Kingdom architecture

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Founders get the business flow and the operator knobs. Developers get the services, routes, env vars and a local test run.

    News Kingdom is the promotion layer on top of Ring's news module. Members with the member role or higher write articles that live on their personal space at /{username}/{slug}. An author can then ask for a place on the main /news feed. Ring scores the article, asks the author to pay a price based on that score, and sends it to platform admins in Telegram for a final yes or no.

    Every promotion request moves through one field on the article, mainPageStatus:

    StageWhat happensmainPageStatus
    SubmitAuthor asks for main-page placementsubmitted
    ScoreAI or heuristic scores ethics, spam, duplicates and meritai_scored
    Hard blockEthics too low, spam or duplicate risk too highpublished_blog_only
    CheckoutPrice is set and a PaymentConductor checkout openspayment_pending
    PaidWebhook confirms payment, admins get a Telegram cardawaiting_admin_approval
    ApprovedArticle goes site-wide on the main feedpublished_main
    RejectedAdmin declines. The article stays on the author's blogrejected

    Why this matters for your clone

    • Members bring the content and pay for reach. You run a newspaper without hiring a newsroom. Authors who want the front page pay for it, and you keep the final editorial call.
    • The bad stuff gets filtered first. Spam, duplicates and low-ethics posts are stopped by scoring before anyone pays and before an admin spends time on them.
    • You approve from your phone. Every paid submission reaches your admins as a Telegram message with Approve, Reject, View and Counter buttons.
    • Good stories pay back. When an admin approves a member-written story, Ring queues a reward credit for the author and starts translations into your other locales.

    Member blogs

    Where the article lives before promotion: the author's personal URL.

    News module

    The main feed, categories, likes and the editor that authors use.

    PaymentConductor

    Module map (verified in ring/web)

    ConcernPath
    Status machine, audit, approve and rejectfeatures/news/services/news-promotion-workflow.ts
    OpenRouter scoring and pricefeatures/news/services/news-scoring-service.ts
    Duplicate heuristicfeatures/news/services/news-duplicate-check.ts
    Checkout facade (purpose: 'news_promotion')features/news/services/news-payment-service.ts
    Paid webhook handlerslib/payments/conductor/handlers/news-promotion.ts, news-promotion-paypal.ts
    Admin Telegram card and keyboardfeatures/news/services/news-telegram-approval.ts
    Telegram callback handling (wired from )

    Known gaps

    Related documentation

    Related documentation

    Member Blogs

    Same-workflow: where the article lives before and after a promotion request.

    News Module

    Prerequisite: the news module, editor and main feed that News Kingdom promotes into.

    PaymentConductor architecture

    Depends-on: how the news_promotion checkout and webhooks are routed.

    Manage via Telegram

    See-also: the admin bot that carries the Approve, Reject and Counter buttons.

    1. Docs
    2. /Architecture
    3. /News Kingdom architecture

    Updated Oct 1, 20266 min listen

    News Kingdom architecture

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Founders get the business flow and the operator knobs. Developers get the services, routes, env vars and a local test run.

    News Kingdom is the promotion layer on top of Ring's news module. Members with the member role or higher write articles that live on their personal space at /{username}/{slug}. An author can then ask for a place on the main /news feed. Ring scores the article, asks the author to pay a price based on that score, and sends it to platform admins in Telegram for a final yes or no.

    Every promotion request moves through one field on the article, mainPageStatus:

    StageWhat happensmainPageStatus
    SubmitAuthor asks for main-page placementsubmitted
    ScoreAI or heuristic scores ethics, spam, duplicates and meritai_scored
    Hard blockEthics too low, spam or duplicate risk too highpublished_blog_only
    CheckoutPrice is set and a PaymentConductor checkout openspayment_pending
    PaidWebhook confirms payment, admins get a Telegram cardawaiting_admin_approval
    ApprovedArticle goes site-wide on the main feedpublished_main
    RejectedAdmin declines. The article stays on the author's blogrejected

    Why this matters for your clone

    • Members bring the content and pay for reach. You run a newspaper without hiring a newsroom. Authors who want the front page pay for it, and you keep the final editorial call.
    • The bad stuff gets filtered first. Spam, duplicates and low-ethics posts are stopped by scoring before anyone pays and before an admin spends time on them.
    • You approve from your phone. Every paid submission reaches your admins as a Telegram message with Approve, Reject, View and Counter buttons.
    • Good stories pay back. When an admin approves a member-written story, Ring queues a reward credit for the author and starts translations into your other locales.

    Member blogs

    Where the article lives before promotion: the author's personal URL.

    News module

    The main feed, categories, likes and the editor that authors use.

    PaymentConductor

    Module map (verified in ring/web)

    ConcernPath
    Status machine, audit, approve and rejectfeatures/news/services/news-promotion-workflow.ts
    OpenRouter scoring and pricefeatures/news/services/news-scoring-service.ts
    Duplicate heuristicfeatures/news/services/news-duplicate-check.ts
    Checkout facade (purpose: 'news_promotion')features/news/services/news-payment-service.ts
    Paid webhook handlerslib/payments/conductor/handlers/news-promotion.ts, news-promotion-paypal.ts
    Admin Telegram card and keyboardfeatures/news/services/news-telegram-approval.ts
    Telegram callback handling (wired from )

    Known gaps

    Related documentation

    Related documentation

    Member Blogs

    Same-workflow: where the article lives before and after a promotion request.

    News Module

    Prerequisite: the news module, editor and main feed that News Kingdom promotes into.

    PaymentConductor architecture

    Depends-on: how the news_promotion checkout and webhooks are routed.

    Manage via Telegram

    See-also: the admin bot that carries the Approve, Reject and Counter buttons.

    1. Docs
    2. /Architecture
    3. /News Kingdom architecture

    Updated Oct 1, 20266 min listen

    News Kingdom architecture

    Use the Founder / Developer tabs in the docs sidebar to filter this page. Founders get the business flow and the operator knobs. Developers get the services, routes, env vars and a local test run.

    News Kingdom is the promotion layer on top of Ring's news module. Members with the member role or higher write articles that live on their personal space at /{username}/{slug}. An author can then ask for a place on the main /news feed. Ring scores the article, asks the author to pay a price based on that score, and sends it to platform admins in Telegram for a final yes or no.

    Every promotion request moves through one field on the article, mainPageStatus:

    StageWhat happensmainPageStatus
    SubmitAuthor asks for main-page placementsubmitted
    ScoreAI or heuristic scores ethics, spam, duplicates and meritai_scored
    Hard blockEthics too low, spam or duplicate risk too highpublished_blog_only
    CheckoutPrice is set and a PaymentConductor checkout openspayment_pending
    PaidWebhook confirms payment, admins get a Telegram cardawaiting_admin_approval
    ApprovedArticle goes site-wide on the main feedpublished_main
    RejectedAdmin declines. The article stays on the author's blogrejected

    Why this matters for your clone

    • Members bring the content and pay for reach. You run a newspaper without hiring a newsroom. Authors who want the front page pay for it, and you keep the final editorial call.
    • The bad stuff gets filtered first. Spam, duplicates and low-ethics posts are stopped by scoring before anyone pays and before an admin spends time on them.
    • You approve from your phone. Every paid submission reaches your admins as a Telegram message with Approve, Reject, View and Counter buttons.
    • Good stories pay back. When an admin approves a member-written story, Ring queues a reward credit for the author and starts translations into your other locales.

    Member blogs

    Where the article lives before promotion: the author's personal URL.

    News module

    The main feed, categories, likes and the editor that authors use.

    PaymentConductor

    Module map (verified in ring/web)

    ConcernPath
    Status machine, audit, approve and rejectfeatures/news/services/news-promotion-workflow.ts
    OpenRouter scoring and pricefeatures/news/services/news-scoring-service.ts
    Duplicate heuristicfeatures/news/services/news-duplicate-check.ts
    Checkout facade (purpose: 'news_promotion')features/news/services/news-payment-service.ts
    Paid webhook handlerslib/payments/conductor/handlers/news-promotion.ts, news-promotion-paypal.ts
    Admin Telegram card and keyboardfeatures/news/services/news-telegram-approval.ts
    Telegram callback handling (wired from )

    Known gaps

    Related documentation

    Related documentation

    Member Blogs

    Same-workflow: where the article lives before and after a promotion request.

    News Module

    Prerequisite: the news module, editor and main feed that News Kingdom promotes into.

    PaymentConductor architecture

    Depends-on: how the news_promotion checkout and webhooks are routed.

    Manage via Telegram

    See-also: the admin bot that carries the Approve, Reject and Counter buttons.

    1. Docs
    2. /Architecture
    3. /News Kingdom architecture

    Updated Oct 1, 20266 min listen

    The checkout that charges the promotion fee with your configured processor.

    Manage via Telegram

    The admin bot that carries the approval buttons.

    From member blog to main page

    Typical scenarios

    1. Community paper. A city clone lets local members post on their blogs and charges a small fee for front-page stories. Admins approve the best ones in Telegram.
    2. Expert network. A professional clone keeps merit high: strong articles get a low price, weak ones get priced out.
    3. Moderated campaign hub. A movement clone relies on the hard-block rules so spam never reaches the admin queue.

    Operator knobs

    You set prices in UAH with server env vars. If you leave them unset, these defaults apply:

    SettingDefaultEffect
    Base price50 UAHMinimum price for a high-merit article
    Scale2000 UAHHow much the price rises as merit drops
    Maximum5000 UAHPrice cap
    Low-merit floormerit below 0.25Price is raised to at least 4999 UAH
    Admin counter-offer+25%Telegram Counter button sets a new amount the author must pay
    lib/telegram/admin-bot/news-callback-handler.ts
    app/api/telegram/admin-bot/webhook/route.ts
    Member promotion UIfeatures/news/components/news-promotion-panel.tsx on /my-news
    Status and field typesfeatures/news/types/index.ts (MainPageStatus, NewsContentType, NewsVisibility)
    JSONB row mapperlib/news/map-news-document.ts
    Brand and absolute URLslib/site-branding.ts (localeBlogArticleUrl, absoluteSiteUrl)

    Routes

    Method and pathAuthRole
    POST /api/news/promotion/submitSession, article author onlyMarks promoteToMainPage, scores, opens checkout
    PATCH /api/news/promotion/submitHeader x-news-promotion-secretDev or test: mark paid and notify admins
    POST /api/payments/{wayforpay,stripe,paypal}/webhookProcessor signatureCanonical paid confirmation
    POST /api/news/promotion/wayforpay-webhookProcessor payloadDeprecated delegate to the canonical WayForPay webhook
    POST /api/mcp/v1/news/[id]/{score,approve,reject,publish}Service token (withMcpGuard)Agent and MCP operations on the same workflow

    Scoring rules

    scoreNewsForMainPage() calls OpenRouter when OPENROUTER_API_KEY is set. Without a key it keeps heuristic defaults (ethics 0.7, spam 0.2, merit 0.5) and records model: 'heuristic'.

    • Duplicate risk: 0.95 if another article shares slug or siteWideSlug. 0.9 if a published article (first 200) has the same title.
    • Hard block: ethics < NEWS_ETHICS_MIN or spamRisk > NEWS_SPAM_MAX or duplicateRisk > NEWS_DUPLICATE_MAX. Result: published_blog_only and no checkout.
    • Price: clamp(base, max, base + (1 - merit) * scale). Merit below NEWS_MERIT_SOFT_REJECT raises the price to at least 4999. Low merit does not change the status.

    Environment variables

    None of the NEWS_* keys are in env.local.template. They are read in code with these defaults.

    VariableDefaultRead in
    NEWS_PROMO_BASE_UAH / NEWS_PROMO_SCALE_UAH / NEWS_PROMO_MAX_UAH50 / 2000 / 5000news-scoring-service.ts
    NEWS_ETHICS_MIN / NEWS_SPAM_MAX / NEWS_DUPLICATE_MAX0.35 / 0.85 / 0.9news-scoring-service.ts
    NEWS_MERIT_SOFT_REJECT0.25scoring and workflow
    OPENROUTER_API_KEY, OPENROUTER_MODEL_PRIMARY, OPENROUTER_MODEL_FALLBACKunsetnews-scoring-service.ts
    PAYMENT_NEWS_PROCESSORfalls back to PAYMENT_DEFAULT_PROCESSOR, then wayforpaylib/payments/payment.config.ts
    NEWS_PROMOTION_WEBHOOK_SECRETunsetPATCH /api/news/promotion/submit
    ADMIN_BOT_TOKENunsetTelegram callback answers

    Data

    All four tables use the JSONB document model (id, data jsonb, timestamps) in data/schema.sql (flattened v4.1.0): news, news_categories, news_likes, news_submission_audit. Every transition appends to data.mainPageStatusHistory and writes one news_submission_audit row. data/migrations/002_news_content_schema.sql and 003_news_kingdom_upgrade.sql are historical. Fresh installs apply schema.sql only.

    Approval side effects

    approveMainPagePublication() sets status: 'published' and visibility: 'site-wide'. For user-authored stories it then queues enqueueRewardCreditAddEvent({ trigger: 'newsStoryApproved' }) and fans out generateArticleTranslations(). Telegram recipients are users with a platform admin role and a communication.telegramId.

    Local test run

    1. 1

      Apply the schema and create a member article

      Apply data/schema.sql and sign in as a user with role member or higher. Create an article at /news/create. It is stamped with blogUsername and appears at /{username}/{slug} once published.

    2. 2

      Submit for promotion

      The response carries status, aiScore, and either a redirect or payment fields for the checkout.

    3. 3

      Simulate payment in dev

    4. 4

      Approve

      Press Approve on the Telegram card, or call POST /api/mcp/v1/news/<id>/approve with a service token. Check that mainPageStatus is published_main and that a news_submission_audit row exists.

    The checkout that charges the promotion fee with your configured processor.

    Manage via Telegram

    The admin bot that carries the approval buttons.

    From member blog to main page

    Typical scenarios

    1. Community paper. A city clone lets local members post on their blogs and charges a small fee for front-page stories. Admins approve the best ones in Telegram.
    2. Expert network. A professional clone keeps merit high: strong articles get a low price, weak ones get priced out.
    3. Moderated campaign hub. A movement clone relies on the hard-block rules so spam never reaches the admin queue.

    Operator knobs

    You set prices in UAH with server env vars. If you leave them unset, these defaults apply:

    SettingDefaultEffect
    Base price50 UAHMinimum price for a high-merit article
    Scale2000 UAHHow much the price rises as merit drops
    Maximum5000 UAHPrice cap
    Low-merit floormerit below 0.25Price is raised to at least 4999 UAH
    Admin counter-offer+25%Telegram Counter button sets a new amount the author must pay
    lib/telegram/admin-bot/news-callback-handler.ts
    app/api/telegram/admin-bot/webhook/route.ts
    Member promotion UIfeatures/news/components/news-promotion-panel.tsx on /my-news
    Status and field typesfeatures/news/types/index.ts (MainPageStatus, NewsContentType, NewsVisibility)
    JSONB row mapperlib/news/map-news-document.ts
    Brand and absolute URLslib/site-branding.ts (localeBlogArticleUrl, absoluteSiteUrl)

    Routes

    Method and pathAuthRole
    POST /api/news/promotion/submitSession, article author onlyMarks promoteToMainPage, scores, opens checkout
    PATCH /api/news/promotion/submitHeader x-news-promotion-secretDev or test: mark paid and notify admins
    POST /api/payments/{wayforpay,stripe,paypal}/webhookProcessor signatureCanonical paid confirmation
    POST /api/news/promotion/wayforpay-webhookProcessor payloadDeprecated delegate to the canonical WayForPay webhook
    POST /api/mcp/v1/news/[id]/{score,approve,reject,publish}Service token (withMcpGuard)Agent and MCP operations on the same workflow

    Scoring rules

    scoreNewsForMainPage() calls OpenRouter when OPENROUTER_API_KEY is set. Without a key it keeps heuristic defaults (ethics 0.7, spam 0.2, merit 0.5) and records model: 'heuristic'.

    • Duplicate risk: 0.95 if another article shares slug or siteWideSlug. 0.9 if a published article (first 200) has the same title.
    • Hard block: ethics < NEWS_ETHICS_MIN or spamRisk > NEWS_SPAM_MAX or duplicateRisk > NEWS_DUPLICATE_MAX. Result: published_blog_only and no checkout.
    • Price: clamp(base, max, base + (1 - merit) * scale). Merit below NEWS_MERIT_SOFT_REJECT raises the price to at least 4999. Low merit does not change the status.

    Environment variables

    None of the NEWS_* keys are in env.local.template. They are read in code with these defaults.

    VariableDefaultRead in
    NEWS_PROMO_BASE_UAH / NEWS_PROMO_SCALE_UAH / NEWS_PROMO_MAX_UAH50 / 2000 / 5000news-scoring-service.ts
    NEWS_ETHICS_MIN / NEWS_SPAM_MAX / NEWS_DUPLICATE_MAX0.35 / 0.85 / 0.9news-scoring-service.ts
    NEWS_MERIT_SOFT_REJECT0.25scoring and workflow
    OPENROUTER_API_KEY, OPENROUTER_MODEL_PRIMARY, OPENROUTER_MODEL_FALLBACKunsetnews-scoring-service.ts
    PAYMENT_NEWS_PROCESSORfalls back to PAYMENT_DEFAULT_PROCESSOR, then wayforpaylib/payments/payment.config.ts
    NEWS_PROMOTION_WEBHOOK_SECRETunsetPATCH /api/news/promotion/submit
    ADMIN_BOT_TOKENunsetTelegram callback answers

    Data

    All four tables use the JSONB document model (id, data jsonb, timestamps) in data/schema.sql (flattened v4.1.0): news, news_categories, news_likes, news_submission_audit. Every transition appends to data.mainPageStatusHistory and writes one news_submission_audit row. data/migrations/002_news_content_schema.sql and 003_news_kingdom_upgrade.sql are historical. Fresh installs apply schema.sql only.

    Approval side effects

    approveMainPagePublication() sets status: 'published' and visibility: 'site-wide'. For user-authored stories it then queues enqueueRewardCreditAddEvent({ trigger: 'newsStoryApproved' }) and fans out generateArticleTranslations(). Telegram recipients are users with a platform admin role and a communication.telegramId.

    Local test run

    1. 1

      Apply the schema and create a member article

      Apply data/schema.sql and sign in as a user with role member or higher. Create an article at /news/create. It is stamped with blogUsername and appears at /{username}/{slug} once published.

    2. 2

      Submit for promotion

      The response carries status, aiScore, and either a redirect or payment fields for the checkout.

    3. 3

      Simulate payment in dev

    4. 4

      Approve

      Press Approve on the Telegram card, or call POST /api/mcp/v1/news/<id>/approve with a service token. Check that mainPageStatus is published_main and that a news_submission_audit row exists.

    The checkout that charges the promotion fee with your configured processor.

    Manage via Telegram

    The admin bot that carries the approval buttons.

    From member blog to main page

    Typical scenarios

    1. Community paper. A city clone lets local members post on their blogs and charges a small fee for front-page stories. Admins approve the best ones in Telegram.
    2. Expert network. A professional clone keeps merit high: strong articles get a low price, weak ones get priced out.
    3. Moderated campaign hub. A movement clone relies on the hard-block rules so spam never reaches the admin queue.

    Operator knobs

    You set prices in UAH with server env vars. If you leave them unset, these defaults apply:

    SettingDefaultEffect
    Base price50 UAHMinimum price for a high-merit article
    Scale2000 UAHHow much the price rises as merit drops
    Maximum5000 UAHPrice cap
    Low-merit floormerit below 0.25Price is raised to at least 4999 UAH
    Admin counter-offer+25%Telegram Counter button sets a new amount the author must pay
    lib/telegram/admin-bot/news-callback-handler.ts
    app/api/telegram/admin-bot/webhook/route.ts
    Member promotion UIfeatures/news/components/news-promotion-panel.tsx on /my-news
    Status and field typesfeatures/news/types/index.ts (MainPageStatus, NewsContentType, NewsVisibility)
    JSONB row mapperlib/news/map-news-document.ts
    Brand and absolute URLslib/site-branding.ts (localeBlogArticleUrl, absoluteSiteUrl)

    Routes

    Method and pathAuthRole
    POST /api/news/promotion/submitSession, article author onlyMarks promoteToMainPage, scores, opens checkout
    PATCH /api/news/promotion/submitHeader x-news-promotion-secretDev or test: mark paid and notify admins
    POST /api/payments/{wayforpay,stripe,paypal}/webhookProcessor signatureCanonical paid confirmation
    POST /api/news/promotion/wayforpay-webhookProcessor payloadDeprecated delegate to the canonical WayForPay webhook
    POST /api/mcp/v1/news/[id]/{score,approve,reject,publish}Service token (withMcpGuard)Agent and MCP operations on the same workflow

    Scoring rules

    scoreNewsForMainPage() calls OpenRouter when OPENROUTER_API_KEY is set. Without a key it keeps heuristic defaults (ethics 0.7, spam 0.2, merit 0.5) and records model: 'heuristic'.

    • Duplicate risk: 0.95 if another article shares slug or siteWideSlug. 0.9 if a published article (first 200) has the same title.
    • Hard block: ethics < NEWS_ETHICS_MIN or spamRisk > NEWS_SPAM_MAX or duplicateRisk > NEWS_DUPLICATE_MAX. Result: published_blog_only and no checkout.
    • Price: clamp(base, max, base + (1 - merit) * scale). Merit below NEWS_MERIT_SOFT_REJECT raises the price to at least 4999. Low merit does not change the status.

    Environment variables

    None of the NEWS_* keys are in env.local.template. They are read in code with these defaults.

    VariableDefaultRead in
    NEWS_PROMO_BASE_UAH / NEWS_PROMO_SCALE_UAH / NEWS_PROMO_MAX_UAH50 / 2000 / 5000news-scoring-service.ts
    NEWS_ETHICS_MIN / NEWS_SPAM_MAX / NEWS_DUPLICATE_MAX0.35 / 0.85 / 0.9news-scoring-service.ts
    NEWS_MERIT_SOFT_REJECT0.25scoring and workflow
    OPENROUTER_API_KEY, OPENROUTER_MODEL_PRIMARY, OPENROUTER_MODEL_FALLBACKunsetnews-scoring-service.ts
    PAYMENT_NEWS_PROCESSORfalls back to PAYMENT_DEFAULT_PROCESSOR, then wayforpaylib/payments/payment.config.ts
    NEWS_PROMOTION_WEBHOOK_SECRETunsetPATCH /api/news/promotion/submit
    ADMIN_BOT_TOKENunsetTelegram callback answers

    Data

    All four tables use the JSONB document model (id, data jsonb, timestamps) in data/schema.sql (flattened v4.1.0): news, news_categories, news_likes, news_submission_audit. Every transition appends to data.mainPageStatusHistory and writes one news_submission_audit row. data/migrations/002_news_content_schema.sql and 003_news_kingdom_upgrade.sql are historical. Fresh installs apply schema.sql only.

    Approval side effects

    approveMainPagePublication() sets status: 'published' and visibility: 'site-wide'. For user-authored stories it then queues enqueueRewardCreditAddEvent({ trigger: 'newsStoryApproved' }) and fans out generateArticleTranslations(). Telegram recipients are users with a platform admin role and a communication.telegramId.

    Local test run

    1. 1

      Apply the schema and create a member article

      Apply data/schema.sql and sign in as a user with role member or higher. Create an article at /news/create. It is stamped with blogUsername and appears at /{username}/{slug} once published.

    2. 2

      Submit for promotion

      The response carries status, aiScore, and either a redirect or payment fields for the checkout.

    3. 3

      Simulate payment in dev

    4. 4

      Approve

      Press Approve on the Telegram card, or call POST /api/mcp/v1/news/<id>/approve with a service token. Check that mainPageStatus is published_main and that a news_submission_audit row exists.