Predictu
Getting Started

Architecture

The Predictu Casino Integration is composed of four deployable services backed by a shared Supabase project and powered by eight core engines. This page describes how the pieces fit together, how data flows between systems, and the technology choices behind each layer.

System Overview

The following diagram shows the full system topology. The casino operator site (left) embeds the Predictu iframe app and optionally receives S2S callbacks. The Predictu platform (right) runs the trading engines, admin dashboards, and shared database.

Casino Operator Site                    Predictu Platform
┌──────────────────────┐               ┌─────────────────────────────────────────┐
│                      │   iframe      │  Iframe App (app.predictu.com)   │
│   operator-site.com  │◄────────────►│  ├── PostMessage Bridge                 │
│   (casino shell)     │   postMsg     │  ├── Branding Injector                  │
│                      │               │  ├── Casino Trading UI                  │
└──────────┬───────────┘               │  └── Auth (embed-init / login)          │
           │                           └───────────────┬─────────────────────────┘
           │  S2S Callbacks                            │
           │  (JWT-signed POST)                        │  Supabase (shared)
           ▼                           ┌───────────────┴─────────────────────────┐
┌──────────────────────┐               │  Core Engines                           │
│  Operator Backend    │               │  ├── Spread Engine (dynamic pricing)    │
│  ├── /api/callback   │◄─────────────│  ├── Risk Engine (5-wall defence)       │
│  ├── Player Wallets  │   BET_MAKE    │  ├── Trade Executor (atomic trades)    │
│  └── Transaction Log │   BET_WIN     │  ├── Resolution Engine (settlement)    │
└──────────────────────┘   BET_SELL    │  ├── Transaction Log (audit trail)     │
                           BALANCE     │  ├── Wallet Adapter (S2S callbacks)    │
                                       │  ├── User Scoring (sharp detection)    │
                                       │  └── Market Selection (tradeability)   │
                                       └─────────────────────────────────────────┘

Admin Dashboards
┌─────────────────────────────────────────────────────────────────────────────────┐
│  God Mode (predictu-admin)               │  Operator (predictu-operator)        │
│  ├── Onboard new operators               │  ├── Branding & embed config         │
│  ├── Global analytics & revenue          │  ├── User & trade management         │
│  ├── Risk monitoring & circuit breakers  │  ├── Revenue & invoicing             │
│  └── User scoring & tier management      │  └── Risk events & exposure          │
└─────────────────────────────────────────────────────────────────────────────────┘

The Four Services

Iframe App

The primary user-facing application. It renders the complete prediction market experience: market discovery, event detail pages, trading tickets, portfolio and position management, and the watchlist. When loaded inside an <iframe> with ?embed=true&operator=slug, it activates embed mode:

  • The PostMessage Bridge component listens for predictu:init messages from the parent window to authenticate the user and set initial balance.
  • The Branding Injector component fetches the operator's branding from Supabase and injects CSS custom properties on <html>, reskinning the entire UI.
  • Navigation chrome (top bar, sidebar) hides in embed mode to fit seamlessly within the casino shell.
  • Trade and balance events are relayed to the parent via predictu:trade and predictu:balance PostMessages.

God Mode (Internal Admin)

The Predictu-internal operations dashboard. Only accessible to Predictu staff with the god_mode role. Provides:

  • Operator onboarding - create new operators, generate API keys, configure S2S callback URLs.
  • Global analytics - total volume, revenue, active users, market count across all operators.
  • Risk monitoring - real-time global exposure, per-market exposure heatmap, risk event feed, circuit breaker toggles.
  • User management - search any user across operators, view scoring data, change tiers, review trade history.
  • Scoring configuration - tune scoring weights, thresholds, and auto-restriction rules.

Operator Dashboard

The per-operator self-service panel. Each operator admin logs in and sees only their own scoped data. Provides:

  • Branding editor - live preview of 17 color tokens, logo/banner/favicon uploads, border radius control.
  • Embed configuration - manage allowed iframe origins, get embed code snippets.
  • S2S setup - register callback URL, download public key, test with sandbox mode, view callback logs.
  • User management - view operator-scoped players, their trades, positions, and balances.
  • Revenue dashboard - GGR, net revenue, settlement P&L, invoicing summaries.
  • Market controls - enable/disable individual markets, set custom spreads, configure per-market exposure caps.

Casino Demo

A standalone Next.js app that replicates a casino operator shell. It embeds the iframe app with the default dark casino theme and demonstrates the complete integration: PostMessage initialization, balance sync, deposit/withdraw commands, and trade notifications. Used for sales demos and as a reference implementation for new operators.

Shared Supabase Backend

All four services connect to a single Supabase project. The database schema is organized into the following key table groups:

Table GroupKey TablesPurpose
Users & Authusers, user_scoresPlayer profiles, casino balances, tier assignments, composite scoring data.
Operatorsoperators, operator_api_config, iframe_originsOperator records, S2S callback config (URL, keys, retry settings, IP whitelist), allowed embed origins.
Tradingcasino_trades, casino_positions, casino_exposureTrade records, open/closed/resolved positions, per-market exposure tracking.
Settlementsettlements, position_settlementsMarket resolution records, per-position payout breakdowns.
Transaction Logs2s_transactionsImmutable audit trail of every S2S callback (debits, credits, settlements, refunds, balance checks).
S2Ss2s_transactions, s2s_callback_logOutbound S2S transaction records and per-attempt HTTP callback logs.
Riskrisk_events, market_controls, blocked_categories, casino_configRisk event feed, per-market enable/disable and custom spread overrides, category blocks, tier limits, circuit breaker config.
Operator isolation. Every user-facing table includes an operator_id column. Supabase Row-Level Security (RLS) policies enforce that an operator can only read/write rows belonging to their own operator_id. God Mode bypasses RLS using a service role key.

Data Flow

Iframe PostMessage Flow

When a casino operator embeds the Predictu iframe, data flows bidirectionally via the browser's window.postMessage API. The protocol is structured and typed.

1
Parent loads iframe. The operator's page creates an <iframe> with src="https://app.predictu.com?embed=true&operator=example-casino".
2
Parent sends predictu:init. Once the iframe loads, the parent sends the player's identity, current balance, and operator ID via PostMessage.
3
Iframe authenticates and applies branding. The PostMessage Bridge processes the init message, the Branding Injector fetches and applies the operator's theme, and the iframe sends back a predictu:ready signal.
4
Ongoing sync. The iframe sends predictu:balance on every balance change, predictu:trade on every executed trade, and predictu:navigate on every route change. The parent can send predictu:deposit and predictu:withdraw to adjust the player's balance.

S2S Callback Flow

Every wallet operation triggers a signed HTTP callback from Predictu to the operator’s backend.

1
Player initiates trade. The Trade Executor resolves the Wallet Adapter for the user’s operator and instantiates the S2S adapter with the operator’s callback configuration.
2
S2S Dispatcher builds request. The dispatcher creates a request envelope containing the method (BET_MAKE), a UUID request ID (idempotency key), and method-specific params (player ID, amount in subunits, bet details).
3
JWT signed and sent. The request body is SHA-256 hashed, and a JWT is signed with the operator's RSA private key. The POST is sent with Authorization: Bearer <jwt>, X-Request-Id, and X-Predictu-Method headers.
4
Operator processes and responds. The operator validates the JWT, processes the wallet operation, and returns a JSON response with status (OK, INSUFFICIENT_FUNDS, PLAYER_NOT_FOUND, DUPLICATE_TRANSACTION, ERROR), balance, and optionally their own transaction_id.
5
Retries on failure. HTTP errors and ERROR status responses trigger exponential backoff retries (configurable max retries and base backoff). Business errors (INSUFFICIENT_FUNDS, PLAYER_NOT_FOUND) are terminal and not retried.

Core Engines

Eight purpose-built engines handle every aspect of the trading lifecycle. Each engine is a standalone TypeScript module with no side effects at import time.

EngineModuleResponsibility
Spread Enginespread-engine.tsCalculates ask/bid prices from a mid price. Applies a configurable base spread (default 4%) that widens for extreme prices (near 0 or 100), low liquidity, exposure imbalance, and sharp bettor adjustment. Returns mid, ask, bid, spread percentage, and spread in cents.
Risk Enginerisk-engine.ts5-wall defence system. Wall 1: per-trade limits by user tier (max trade, daily volume, per-market, max positions). Wall 2: per-market exposure caps. Wall 3: per-category caps. Wall 4: global exposure cap. Wall 5: circuit breakers (daily loss halt, emergency halt). Every rejection is logged as a risk event.
Trade Executortrade-executor.tsOrchestrates buy and sell execution. Validates tradeability, runs risk assessment, calculates spread-adjusted price, debits/credits via Wallet Adapter, records trade, upserts position (with optimistic concurrency), updates exposure, and updates wagering stats. Uses compensating transactions to rollback on partial failure.
Resolution Engineresolution-engine.tsDetects market resolution from Polymarket, finds all open positions, calculates payouts ($1 per winning share), credits winners and notifies losers via Wallet Adapter, records settlement and position_settlement rows, and computes casino profit. Also handles market voiding (full cost basis refund).
Transaction Logs2s-transaction-log.tsImmutable audit trail of every S2S callback. Every balance operation writes to s2s_transactions with type, amount, balance_after, request ID, operator response, and status. Used for reconciliation, dispute resolution, and compliance.
Wallet Adapterwallet-adapter.tsAbstraction layer over balance operations. Routes all wallet operations through S2S callbacks to the operator’s server. Common interface: getBalance, debitForBet, creditForWin, notifyLoss, creditForSell, refund, rollback.
Market Selection Enginemarket-selection-engine.tsDetermines which markets are tradeable. Checks minimum liquidity ($5,000 default), price range (5c–95c), blocked categories, manual disable via market_controls, and per-market exposure caps. Also provides custom spread override lookup.
User Scoring Engineuser-scoring-engine.tsClassifies bettors on a 0–100 composite score. Five weighted dimensions: win rate (30%), edge captured (25%), timing (15%), sizing (15%), diversification (15%). Classifications: recreational (<40), moderate (40–70), sharp (70–85), professional (85+). Auto-restricts players above the auto-restrict threshold (default 90). Runs daily via cron.

Engine Interaction Flow

The engines compose in a specific order during trade execution:

Player clicks "Buy YES"
       │
       ▼
  Market Selection Engine
  ├── Liquidity check
  ├── Price range check
  ├── Blocked category check
  └── Market control check
       │ (tradeable? ✓)
       ▼
  Risk Engine (5 walls)
  ├── Wall 1: Per-trade/user limits
  ├── Wall 2: Per-market exposure
  ├── Wall 3: Per-category exposure
  ├── Wall 4: Global exposure
  └── Wall 5: Circuit breakers
       │ (allowed? ✓)
       ▼
  Spread Engine
  ├── Base spread calculation
  ├── Edge/liquidity/exposure adjustments
  └── Sharp bettor premium (from User Scoring)
       │ (ask price: 54.2c)
       ▼
  Trade Executor
  ├── Wallet Adapter → debitForBet()
  │   └── S2S Dispatcher → Operator Callback
  ├── Record casino_trades row
  ├── Upsert casino_positions (optimistic lock)
  ├── Update casino_exposure
  └── Update wagering stats
       │
       ▼
  PostMessage Bridge
  └── Send predictu:trade to parent

Tech Stack

LayerTechnologyDetails
FrameworkNext.js 15 (App Router)All four services are Next.js apps using the App Router. Server Components for pages, Client Components for interactive widgets. API Routes handle server-side logic.
LanguageTypeScript 5Strict mode, full type coverage across all engines, API routes, and components. Shared type definitions in src/types/index.ts.
DatabaseSupabase (PostgreSQL)Single Supabase project with Row-Level Security for operator isolation. Real-time subscriptions for live updates. Edge Functions for cron jobs (scoring, resolution).
AuthSupabase Auth + JWTStandard Supabase Auth for standalone mode. Custom JWT-based embed auth for iframe mode (operator passes user identity via PostMessage). S2S uses RSA-signed JWTs for callback authentication.
State ManagementZustandLightweight stores for client-side state: casino-store (operator branding, embed mode, user session), and component-level stores.
Server StateReact Query (TanStack Query)All data fetching, caching, and real-time invalidation. Custom hooks (use-casino-balance, use-casino-positions, use-casino-trading) wrap React Query with domain-specific logic.
StylingTailwind CSS + CSS Custom PropertiesTailwind for layout and utility classes. CSS custom properties (--primary, --background, etc.) for runtime theming via the Branding Injector.
DeploymentVercelAll four services deploy to Vercel with automatic preview deployments on PR. Serverless functions for API routes. Edge middleware for auth and operator resolution.
CommunicationPostMessage + REST + S2SIframe ↔ parent via typed PostMessage protocol. Client ↔ server via REST API routes. Server ↔ operator via JWT-signed S2S callbacks.

Security Model

Operator Isolation

Every row in the database that contains user data includes an operator_id foreign key. Supabase RLS policies restrict reads and writes to rows matching the authenticated user's operator. The God Mode service bypasses RLS using the Supabase service role key to query across all operators.

Iframe Origin Validation

The PostMessage bridge validates the event.origin of every incoming message against the operator's registered origin whitelist (stored in iframe_origins). In production, messages from unknown origins are silently dropped. In development, localhost origins are allowed for convenience.

S2S JWT Authentication

Every S2S callback carries a JWT in the Authorization header. The JWT is signed with the operator's RSA private key (stored encrypted in the database) and includes claims for issuer (predictu), subject (operator ID), request ID (JTI), method, and a SHA-256 digest of the request body. Operators verify the JWT using Predictu's published public key.

Idempotency

Every S2S request includes a request_id (UUID) that serves as an idempotency key. If the operator receives the same request_id twice (e.g., due to retries), they should return DUPLICATE_TRANSACTION without processing the request again. Predictu also checks for existing successful transactions before dispatching.