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:initmessages 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:tradeandpredictu:balancePostMessages.
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 Group | Key Tables | Purpose |
|---|---|---|
| Users & Auth | users, user_scores | Player profiles, casino balances, tier assignments, composite scoring data. |
| Operators | operators, operator_api_config, iframe_origins | Operator records, S2S callback config (URL, keys, retry settings, IP whitelist), allowed embed origins. |
| Trading | casino_trades, casino_positions, casino_exposure | Trade records, open/closed/resolved positions, per-market exposure tracking. |
| Settlement | settlements, position_settlements | Market resolution records, per-position payout breakdowns. |
| Transaction Log | s2s_transactions | Immutable audit trail of every S2S callback (debits, credits, settlements, refunds, balance checks). |
| S2S | s2s_transactions, s2s_callback_log | Outbound S2S transaction records and per-attempt HTTP callback logs. |
| Risk | risk_events, market_controls, blocked_categories, casino_config | Risk event feed, per-market enable/disable and custom spread overrides, category blocks, tier limits, circuit breaker config. |
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.
<iframe> with src="https://app.predictu.com?embed=true&operator=example-casino".predictu:init. Once the iframe loads, the parent sends the player's identity, current balance, and operator ID via PostMessage.predictu:ready signal.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.
BET_MAKE), a UUID request ID (idempotency key), and method-specific params (player ID, amount in subunits, bet details).Authorization: Bearer <jwt>, X-Request-Id, and X-Predictu-Method headers.status (OK, INSUFFICIENT_FUNDS, PLAYER_NOT_FOUND, DUPLICATE_TRANSACTION, ERROR), balance, and optionally their own transaction_id.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.
| Engine | Module | Responsibility |
|---|---|---|
| Spread Engine | spread-engine.ts | Calculates 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 Engine | risk-engine.ts | 5-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 Executor | trade-executor.ts | Orchestrates 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 Engine | resolution-engine.ts | Detects 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 Log | s2s-transaction-log.ts | Immutable 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 Adapter | wallet-adapter.ts | Abstraction 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 Engine | market-selection-engine.ts | Determines 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 Engine | user-scoring-engine.ts | Classifies 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 parentTech Stack
| Layer | Technology | Details |
|---|---|---|
| Framework | Next.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. |
| Language | TypeScript 5 | Strict mode, full type coverage across all engines, API routes, and components. Shared type definitions in src/types/index.ts. |
| Database | Supabase (PostgreSQL) | Single Supabase project with Row-Level Security for operator isolation. Real-time subscriptions for live updates. Edge Functions for cron jobs (scoring, resolution). |
| Auth | Supabase Auth + JWT | Standard 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 Management | Zustand | Lightweight stores for client-side state: casino-store (operator branding, embed mode, user session), and component-level stores. |
| Server State | React 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. |
| Styling | Tailwind CSS + CSS Custom Properties | Tailwind for layout and utility classes. CSS custom properties (--primary, --background, etc.) for runtime theming via the Branding Injector. |
| Deployment | Vercel | All four services deploy to Vercel with automatic preview deployments on PR. Serverless functions for API routes. Edge middleware for auth and operator resolution. |
| Communication | PostMessage + REST + S2S | Iframe ↔ 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.
