Skip to content

Instantly share code, notes, and snippets.

@UrbanChrisy
Last active September 17, 2025 21:19
Show Gist options
  • Select an option

  • Save UrbanChrisy/2eb64ea2e5e2aef26498a63c3592391c to your computer and use it in GitHub Desktop.

Select an option

Save UrbanChrisy/2eb64ea2e5e2aef26498a63c3592391c to your computer and use it in GitHub Desktop.

Looti Platform Architecture Plan

Overview

The Looti platform enables a marketplace where:

  • Mini app developers (like Alex) can integrate rewards into their apps using the Rewards SDK
  • Game developers (like Bob) can monetize their games using the Game Dev SDK
  • End users enjoy games and earn rewards through the Looti ecosystem

Core Architecture Principles

1. Decentralized Hosting

  • Games are hosted on developer's own infrastructure (URLs)
  • Reduces Looti's server load
  • Developers maintain control over their game code

2. Centralized Rewards

  • Looti backend manages all token/reward distribution
  • Ensures security and prevents fraud
  • Single source of truth for user balances

3. Farcaster Integration

  • Authentication handled through Farcaster
  • User identity flows through Farcaster mini apps
  • Social graph integration for viral growth

Diagram 1: Rewards SDK Dependency Hierarchy

graph TD
    MA[Mini App<br/>Alex's App] --> RS[looti/sdk<br/>Rewards SDK]
    RS --> IPC[looti/ipc<br/>IPC Client]
    RS --> UI[UI Components<br/>Modal/Notifications]
    RS --> LB[Looti Backend API]
    
    LB --> RM[Rewards Manager]
    LB --> GM[Game Registry]
    LB --> TB[Token Balance]
    LB --> FC[Farcaster Auth]
    LB --> SES[Session Manager]
    
    MA --> GI[Game iFrame]
    GI -.->|PostMessage via IPC| RS
    
    IPC --> SDKClient[SDKClient Class]
    SDKClient --> MV[Message Validation<br/>Zod Schemas]

    style MA fill:#fff3e0
    style RS fill:#f3e5f5
    style IPC fill:#e1f5fe
    style LB fill:#e8f5e9
    style GI fill:#fce4ec
Loading

Rewards SDK Key Features

  • Built on @looti/sdk: Extends existing modal and IPC functionality
  • Type-safe IPC: Uses existing @looti/ipc for secure communication
  • Session Management: Server-validated sessions with JWT tokens
  • Reward Validation: All rewards validated server-side with idempotency
  • Farcaster Native: Integrated FID authentication

Diagram 2: Game Developer SDK Dependency Hierarchy

graph TD
    GC[Game Code<br/>Bob's Game] --> GS[looti/game-sdk]
    GS --> GIC[Game IPC Client]
    GS --> API[Looti API Client]
    GS --> SEC[Security Module<br/>HMAC/JWT]
    
    GIC --> PM[PostMessage Handler]
    GIC --> MV[Message Validation]
    
    API --> VM[Session Validation]
    API --> AM[Analytics Module]
    API --> MM[Monetization Manager]
    API --> GR[Game Registry]
    
    SEC --> SIG[Request Signing]
    SEC --> IK[Idempotency Keys]
    
    GC --> GH[Game Hosting<br/>developer-url.com]
    
    PM -.->|IPC Messages| PS[Parent SDK]

    style GC fill:#fce4ec
    style GS fill:#f3e5f5
    style GIC fill:#e1f5fe
    style API fill:#e8f5e9
    style GH fill:#e3f2fd
    style SEC fill:#ffccbc
Loading

Game Dev SDK Key Features

  • Session-based Auth: JWT tokens passed via URL params from parent
  • Built-in Security: HMAC signing and idempotency key generation
  • IPC Communication: Standardized message format with parent frame
  • Analytics Tracking: Automatic event capture and reporting
  • Rate Limiting: Client-side throttling with server validation
  • TypeScript First: Full type safety for all game events

Diagram 3: Complete Ecosystem Architecture with Security

graph TD
    subgraph "Client Layer"
        U[Farcaster Users]
        MA[Mini Apps]
    end
    
    subgraph "SDK Layer"
        RS[Rewards SDK]
        GS[Game Dev SDK]
    end
    
    subgraph "Security Layer"
        SES[Session Manager]
        VAL[Validation Engine]
        RL[Rate Limiter]
        IK[Idempotency Store]
    end
    
    subgraph "Looti Platform"
        API[API Gateway]
        AUTH[Farcaster Auth]
        RE[Rewards Engine]
        GR[Game Registry]
        AN[Analytics]
        FL[Fraud Detection]
    end
    
    subgraph "External Games"
        G1[Bob's Game]
        G2[Carol's Game]
        G3[More Games...]
    end
    
    subgraph "Storage"
        DB[(Database)]
        RD[(Redis Cache)]
    end
    
    U --> MA
    MA --> RS
    RS --> SES
    SES --> API
    
    G1 --> GS
    G2 --> GS
    G3 --> GS
    GS --> VAL
    VAL --> API
    
    API --> RL
    API --> IK
    API --> AUTH
    API --> RE
    API --> GR
    API --> AN
    API --> FL
    
    RE --> DB
    GR --> DB
    AN --> DB
    IK --> RD
    RL --> RD
    SES --> RD
    
    MA -.->|iframe + token| G1
    MA -.->|iframe + token| G2

    style U fill:#e1f5fe
    style MA fill:#fff3e0
    style RS fill:#f3e5f5
    style GS fill:#f3e5f5
    style VAL fill:#ffccbc
    style API fill:#e8f5e9
    style G1 fill:#fce4ec
    style DB fill:#f0f0f0
    style RD fill:#ffebee
Loading

Diagram 4: Session Token Flow Sequence

sequenceDiagram
    participant User as User
    participant MA as Mini App
    participant SDK as Rewards SDK
    participant LB as Looti Backend
    participant Redis as Redis Cache
    participant GI as Game iFrame
    participant GS as Game SDK
    participant GH as Game Host

    Note over User,GH: Initial Session Creation Phase
    
    User->>MA: 1. Click "Play Game"
    MA->>SDK: 2. showGame(gameId)
    SDK->>SDK: 3. Get Farcaster FID
    
    SDK->>LB: 4. POST /api/session/create<br/>{gameId, fid, appId}
    Note right of SDK: Headers:<br/>X-App-Id: mini_app_id<br/>X-Farcaster-FID: user_fid
    
    LB->>LB: 5. Validate app & user
    LB->>LB: 6. Generate session token<br/>& unique sessionId
    
    Note over LB,Redis: Session Storage
    LB->>Redis: 7. Store session<br/>TTL: 30 minutes
    Note right of Redis: Key: session:abc123<br/>Data: {userId, gameId,<br/>appId, nonce, expires}
    
    LB-->>SDK: 8. Return session<br/>{sessionId, token, gameUrl}
    
    Note over SDK,GH: iFrame Loading Phase
    
    SDK->>SDK: 9. Build secure URL<br/>gameUrl?token=JWT&session=abc123
    SDK->>MA: 10. Create iFrame element
    MA->>GI: 11. Load game in iFrame
    
    Note over GI,GH: Game Initialization Phase
    
    GI->>GH: 12. Request game assets
    GH-->>GI: 13. Load game + SDK
    
    GI->>GS: 14. Initialize Game SDK
    GS->>GS: 15. Extract token from URL
    
    Note over GS,LB: Session Validation Phase
    
    GS->>LB: 16. POST /api/session/validate<br/>{token, sessionId}
    
    LB->>Redis: 17. Get session:abc123
    Redis-->>LB: 18. Return session data
    
    LB->>LB: 19. Verify:<br/>- JWT signature<br/>- Session exists<br/>- Not expired<br/>- Game matches
    
    alt Session Valid
        LB-->>GS: 20a. Success response<br/>{valid: true, userId, limits}
        GS->>GI: 21a. Enable game features
        GS->>SDK: 22a. PostMessage: 'ready'
        SDK->>MA: 23a. Game ready callback
        Note over User: Game starts!
    else Session Invalid
        LB-->>GS: 20b. Error response<br/>{valid: false, error}
        GS->>SDK: 21b. PostMessage: 'error'
        SDK->>MA: 22b. Handle error
        Note over User: Show error message
    end

    Note over User,GH: During Gameplay (Reward Flow)
    
    User->>GI: 24. Complete achievement
    GI->>GS: 25. triggerReward()
    GS->>GS: 26. Sign request with HMAC
    GS->>LB: 27. POST /api/reward/trigger<br/>{sessionId, amount, idempotencyKey, signature}
    
    LB->>Redis: 28. Check idempotency
    LB->>LB: 29. Validate signature & limits
    
    alt Reward Valid
        LB-->>GS: 30a. Reward confirmed
        GS->>SDK: 31a. PostMessage: 'reward'
        SDK->>MA: 32a. Update UI/balance
    else Reward Invalid
        LB-->>GS: 30b. Reward rejected
        GS->>SDK: 31b. PostMessage: 'error'
    end

    Note over Redis: Session expires after 30 min
Loading

Session Token Flow Key Points

  1. Session Creation:

    • Mini app requests session from Looti backend with user's Farcaster FID
    • Backend generates JWT token with embedded session data
    • Session stored in Redis with 30-minute TTL
  2. Token Transmission:

    • Token passed to game via URL parameters (secure HTTPS)
    • Game SDK extracts token from URL on initialization
  3. Validation Process:

    • Game SDK validates session with backend before enabling features
    • Backend verifies JWT signature, session existence, and expiration
    • Only validated sessions can trigger rewards
  4. Security Measures:

    • Sessions expire after 30 minutes
    • Each session tied to specific user, game, and mini app
    • All reward requests require valid session + HMAC signature
    • Idempotency keys prevent duplicate rewards

Implementation Plan

Phase 1: Core Infrastructure

  1. Looti Backend Setup

    • API Gateway with rate limiting
    • Farcaster authentication integration (FID validation)
    • Database schema for users, games, transactions
    • Redis for session management and idempotency
    • Basic reward engine with token management
  2. Security Layer

    • Session-based authentication with JWT tokens
    • API key generation for game developers
    • HMAC signature validation for all reward requests
    • CORS configuration for iframe communication
    • Domain whitelisting for approved games
    • Idempotency key storage with Redis TTL

Phase 2: Admin Dashboard & Game Registration

  1. Developer Portal

    // Admin dashboard for game developers
    interface AdminDashboard {
      // Game registration
      registerGame(): { apiKey: string, apiSecret: string };
      
      // Configuration management
      configureLimits(gameId: string, limits: GameLimits): void;
      updateWhitelistedDomains(gameId: string, domains: string[]): void;
      
      // Analytics & monitoring
      viewAnalytics(gameId: string): GameAnalytics;
      viewRewardHistory(gameId: string): RewardTransaction[];
      
      // Budget management
      setDailyBudget(gameId: string, amount: number): void;
      setMonthlyBudget(gameId: string, amount: number): void;
    }
  2. Game Configuration Storage

    -- Database schema for game configurations
    CREATE TABLE games (
      game_id UUID PRIMARY KEY,
      game_name VARCHAR(255),
      api_key VARCHAR(64) UNIQUE,
      api_secret_hash VARCHAR(255),
      
      -- Admin-configured limits
      max_reward_per_action INT DEFAULT 100,
      max_reward_per_user_daily INT DEFAULT 1000,
      max_reward_per_user_hourly INT DEFAULT 200,
      max_game_budget_daily INT DEFAULT 50000,
      max_game_budget_monthly INT DEFAULT 1000000,
      cooldown_seconds INT DEFAULT 60,
      max_actions_per_minute INT DEFAULT 10,
      
      -- Settings
      whitelisted_domains TEXT[],
      status VARCHAR(20) DEFAULT 'testing',
      
      created_at TIMESTAMP,
      updated_at TIMESTAMP
    );
    
    -- Track usage against limits
    CREATE TABLE game_usage (
      game_id UUID REFERENCES games(game_id),
      date DATE,
      daily_spent INT DEFAULT 0,
      monthly_spent INT DEFAULT 0,
      PRIMARY KEY (game_id, date)
    );

Phase 3: Game Developer SDK (@looti/game-sdk)

  1. SDK Core Features

    import { LootiGameSDK } from '@looti/game-sdk';
    
    // Initialize with authentication
    const gameSDK = new LootiGameSDK({
      apiKey: process.env.LOOTI_API_KEY,
      gameId: 'puzzle-game-v1',
      secret: process.env.LOOTI_SECRET, // For HMAC signing
      debug: process.env.NODE_ENV === 'development'
    });
    
    // Validate session from URL params (passed by parent frame)
    await gameSDK.validateSession(urlParams.token, urlParams.sessionId);
    
    // Track player actions with automatic rate limiting
    await gameSDK.trackAction('level_complete', {
      level: 5,
      score: 1000,
      time: 120
    });
    
    // Trigger reward with idempotency
    const reward = await gameSDK.triggerReward('achievement_unlocked', {
      achievementId: 'first_win',
      amount: 100,
      idempotencyKey: gameSDK.generateIdempotencyKey('first_win')
    });
    
    // Communicate with parent frame
    gameSDK.sendToParent('game_completed', { 
      finalScore: 1000,
      rewardEarned: reward.amount 
    });
  2. IPC Integration for Game → SDK Communication

    // Built on existing @looti/ipc patterns
    class GameClient extends IpcClient {
      constructor(config: GameConfig) {
        super({
          debug: config.debug,
          allowedOrigins: ['https://looti.club', ...config.allowedOrigins]
        });
      }
      
      // Send game events to parent
      sendGameEvent(event: string, data: any) {
        this.postMessage({
          type: 'data',
          payload: { event, data }
        });
      }
      
      // Send reward trigger to parent
      sendRewardTrigger(reward: RewardData) {
        this.postMessage({
          type: 'reward',
          payload: reward
        });
      }
    }
  3. Developer Portal Features

    • Game registration with webhook for approval
    • API key and secret generation
    • Analytics dashboard (plays, rewards, earnings)
    • Testing sandbox with mock rewards
    • Documentation and code examples

Phase 4: Enhanced Rewards SDK (@looti/sdk)

  1. Extend Existing SDK with Rewards Features

    import { LootiSDK } from '@looti/sdk';
    
    class LootiRewardsSDK extends LootiSDK {
      private sessionId?: string;
      private userBalance: number = 0;
      
      constructor(config: LootiRewardsConfig) {
        super({
          appId: config.appId,
          debug: config.debug,
          timeout: config.timeout || 15000 // Longer for game loading
        });
        
        // Initialize Farcaster context
        this.farcasterFid = config.farcasterFid;
      }
      
      // Enhanced show method with session creation
      async showGame(options: ShowGameOptions): Promise<HTMLIFrameElement> {
        // Create session with backend
        const session = await this.createGameSession(options.gameId);
        this.sessionId = session.sessionId;
        
        // Build secure iframe URL with session token
        const iframeUrl = this.buildSecureGameUrl(options.gameId, session);
        
        // Use existing modal infrastructure
        const iframe = await this.show({
          gameId: options.gameId,
          url: iframeUrl, // Override with secure URL
          modal: {
            style: options.style || 'pixelated',
            onClose: () => this.handleGameClose(options.onClose)
          }
        });
        
        // Set up reward listeners
        this.setupRewardListeners();
        
        return iframe;
      }
      
      private setupRewardListeners() {
        // Listen for reward events from game
        this.iframe.onIpcMessage('reward', async (message) => {
          const validation = await this.validateReward(message.payload);
          if (validation.success) {
            this.userBalance = validation.newBalance;
            this.showRewardNotification(message.payload);
          }
        });
        
        // Listen for game completion
        this.iframe.onIpcMessage('game_completed', (message) => {
          this.handleGameCompletion(message.payload);
        });
      }
      
      private async createGameSession(gameId: string): Promise<GameSession> {
        const response = await fetch(`${LOOTI_API}/session/create`, {
          method: 'POST',
          headers: {
            'X-App-Id': this.config.appId,
            'X-Farcaster-FID': this.farcasterFid
          },
          body: JSON.stringify({ gameId })
        });
        
        return response.json();
      }
      
      private buildSecureGameUrl(gameId: string, session: GameSession): string {
        const params = new URLSearchParams({
          token: session.token,
          sessionId: session.sessionId,
          userId: this.farcasterFid,
          timestamp: Date.now().toString()
        });
        
        return `${session.gameUrl}?${params}`;
      }
    }
  2. UI Components Library

    // Reward notification component
    class RewardNotification {
      show(reward: RewardData) {
        const notification = this.createElement({
          type: 'success',
          title: 'Reward Earned!',
          message: `You earned ${reward.amount} tokens!`,
          duration: 3000
        });
        
        this.animate(notification, 'slide-in');
      }
    }
    
    // Game selection modal enhancements
    class GameSelectionModal {
      async render(games: Game[]) {
        return games.map(game => ({
          ...game,
          expectedReward: game.averageReward,
          difficulty: game.difficulty,
          playTime: game.estimatedMinutes,
          thumbnail: game.thumbnailUrl
        }));
      }
    }
    
    // Balance display widget
    class BalanceWidget {
      constructor(private sdk: LootiRewardsSDK) {}
      
      async refresh() {
        const balance = await this.sdk.getBalance();
        this.updateDisplay(balance);
      }
    }

Phase 5: Testing & Security Hardening

  1. Comprehensive Testing Strategy

    // Unit tests for reward validation
    describe('RewardValidation', () => {
      it('should reject duplicate idempotency keys', async () => {
        const key = 'test-key-123';
        await triggerReward({ idempotencyKey: key });
        
        // Second attempt should fail
        await expect(triggerReward({ idempotencyKey: key }))
          .rejects.toThrow('Duplicate request');
      });
      
      it('should enforce rate limits', async () => {
        // Trigger 10 actions quickly
        for (let i = 0; i < 10; i++) {
          await triggerAction();
        }
        
        // 11th should be rate limited
        await expect(triggerAction())
          .rejects.toThrow('Rate limit exceeded');
      });
    });
    
    // Integration tests with mock games
    describe('GameIntegration', () => {
      it('should handle complete game flow', async () => {
        const sdk = new LootiRewardsSDK({ appId: 'test' });
        const iframe = await sdk.showGame({ gameId: 'test-game' });
        
        // Simulate game events
        await simulateGameEvent(iframe, 'level_complete');
        await simulateGameEvent(iframe, 'achievement_unlocked');
        
        // Verify rewards issued
        const balance = await sdk.getBalance();
        expect(balance).toBeGreaterThan(0);
      });
    });
  2. Security Audit Checklist

    • HMAC signature validation on all reward endpoints
    • Session token expiration (30 min TTL)
    • Rate limiting per user/game/IP
    • Idempotency key uniqueness validation
    • Domain whitelisting for iframe origins
    • Budget enforcement (daily/hourly limits)
    • Fraud detection rules active
    • Error logging without exposing internals
    • CSP headers properly configured
    • PostMessage origin validation
  3. Load Testing

    // Simulate high traffic scenarios
    const loadTest = {
      scenarios: [
        {
          name: 'Normal Load',
          usersPerSecond: 100,
          duration: 300 // 5 minutes
        },
        {
          name: 'Peak Load',
          usersPerSecond: 1000,
          duration: 60 // 1 minute spike
        },
        {
          name: 'Sustained Gaming',
          usersPerSecond: 500,
          duration: 1800 // 30 minutes
        }
      ]
    };

Phase 6: Developer Experience & Documentation

  1. SDK Package Structure

    @looti/sdk          # Core rewards SDK for mini apps
    @looti/game-sdk     # SDK for game developers
    @looti/ipc          # Shared IPC client (already exists)
    @looti/types        # Shared TypeScript types
    @looti/test-utils   # Testing utilities for developers
    
  2. Developer Documentation

    • Quick start guides for both SDKs
    • Security best practices guide
    • Reward economy design guidelines
    • API reference with TypeScript examples
    • Video tutorials for common integrations
    • Sample game templates (puzzle, arcade, quiz)
  3. CLI Tool for Game Developers

    # Looti CLI for game developers
    npx @looti/cli init         # Initialize game project
    npx @looti/cli test         # Test with mock rewards
    npx @looti/cli validate     # Validate integration
    npx @looti/cli deploy       # Register game with platform

Technical Architecture Details

Security & Anti-Fraud System

1. Idempotency Keys for Reward Prevention

// Game Dev SDK - Idempotent reward triggers
const lootiGameSDK = new LootiGameSDK({
  apiKey: 'game_api_key',
  gameId: 'unique_game_id'
});

// Each reward request requires a unique idempotency key
lootiGameSDK.triggerReward('achievement_unlocked', {
  achievementId: 'first_win',
  amount: 100,
  idempotencyKey: `${userId}-${achievementId}-${timestamp}`, // Prevents duplicate rewards
  userSessionId: 'session_xyz' // Must match server session
});

2. Multi-Layer Authentication System

// Security flow for reward validation
{
  // Layer 1: Origin Validation
  origin: 'https://approved-game.com', // Must be whitelisted
  
  // Layer 2: Session Token
  sessionToken: 'jwt_token_from_looti_backend', // Generated on game load
  
  // Layer 3: Game Signature
  signature: 'hmac_sha256_signature', // Game secret + request data
  
  // Layer 4: Rate Limiting Key
  rateLimitKey: `${gameId}-${userId}-${actionType}`,
  
  // Layer 5: Idempotency
  idempotencyKey: 'unique_request_id'
}

3. Admin-Configured Reward Limits

// Game developer configures limits in admin dashboard
interface GameConfiguration {
  gameId: string;
  apiKey: string;           // Public key for identification
  apiSecret: string;        // Secret for HMAC signing
  
  // Developer-defined limits (set in admin panel)
  limits: {
    maxRewardPerAction: number;      // e.g., 100 tokens
    maxRewardPerUserDaily: number;   // e.g., 1000 tokens/day
    maxRewardPerUserHourly: number;  // e.g., 200 tokens/hour
    maxGameBudgetDaily: number;      // e.g., 50000 tokens/day
    maxGameBudgetMonthly: number;    // e.g., 1000000 tokens/month
    cooldownSeconds: number;         // e.g., 60s between rewards
    maxActionsPerMinute: number;     // e.g., 10 actions/min
  };
  
  // Admin settings
  settings: {
    whitelistedDomains: string[];    // Where game can be hosted
    enableAnalytics: boolean;
    enableFraudDetection: boolean;
    customWebhook?: string;          // For game events
  };
  
  // Status
  status: 'active' | 'suspended' | 'testing';
  createdAt: Date;
  updatedAt: Date;
}

// Admin dashboard flow
const adminDashboard = {
  // 1. Game developer registers game
  registerGame: async (gameDetails) => {
    const { apiKey, apiSecret } = generateCredentials();
    return await saveGameConfig({
      ...gameDetails,
      apiKey,
      apiSecret,
      limits: DEFAULT_LIMITS // Start with platform defaults
    });
  },
  
  // 2. Developer customizes limits
  updateLimits: async (gameId, newLimits) => {
    // Validate against platform maximums
    const validated = validateLimits(newLimits);
    return await updateGameConfig(gameId, { limits: validated });
  },
  
  // 3. Platform retrieves limits for validation
  getGameConfig: async (apiKey) => {
    return await db.games.findOne({ apiKey });
  }
};

4. Session-Based Security with Admin Config

graph TD
    subgraph "Admin Dashboard"
        AD[Admin Panel] -->|Configure| GC[Game Config DB]
        GC -->|Store| LIMITS[Reward Limits]
        GC -->|Store| KEYS[API Keys/Secrets]
        GC -->|Store| DOMAINS[Whitelisted Domains]
    end
    
    subgraph "Runtime Validation"
        MA[Mini App] -->|1. Request game| LB[Looti Backend]
        LB -->|2. Fetch config| GC
        GC -->|3. Return limits| LB
        LB -->|4. Generate session<br/>with limits| ST[Session Token]
        ST -->|5. Embed in URL| GI[Game iFrame]
        GI -->|6. Validate action| VAL[Validation Layer]
        VAL -->|7. Check against<br/>stored limits| GC
    end
    
    subgraph "Validation Checks"
        VAL --> C1[Verify API Key]
        VAL --> C2[Check Secret/HMAC]
        VAL --> C3[Apply Stored Limits]
        VAL --> C4[Check Budget]
        VAL --> C5[Rate Limiting]
        VAL --> C6[Idempotency]
    end
    
    C6 -->|All Pass| RW[Issue Reward]
    C6 -->|Any Fail| REJ[Reject & Log]

    style AD fill:#e3f2fd
    style GC fill:#fff9c4
    style LB fill:#e8f5e9
    style VAL fill:#ffccbc
    style RW fill:#c8e6c9
    style REJ fill:#ef5350
Loading

5. Implementation Details

Admin Dashboard - Game Registration:

// Admin API endpoint for game registration
app.post('/api/admin/games/register', authenticateAdmin, async (req, res) => {
  const { 
    gameName, 
    gameUrl, 
    developerEmail,
    initialLimits // Optional, uses defaults if not provided
  } = req.body;
  
  // Generate unique credentials
  const apiKey = `pk_${crypto.randomBytes(16).toString('hex')}`;
  const apiSecret = `sk_${crypto.randomBytes(32).toString('hex')}`;
  
  // Create game configuration
  const gameConfig = {
    gameId: crypto.randomUUID(),
    gameName,
    gameUrl,
    developerEmail,
    apiKey,
    apiSecret,
    
    // Use developer-provided limits or platform defaults
    limits: {
      maxRewardPerAction: initialLimits?.maxRewardPerAction || 100,
      maxRewardPerUserDaily: initialLimits?.maxRewardPerUserDaily || 1000,
      maxRewardPerUserHourly: initialLimits?.maxRewardPerUserHourly || 200,
      maxGameBudgetDaily: initialLimits?.maxGameBudgetDaily || 50000,
      maxGameBudgetMonthly: initialLimits?.maxGameBudgetMonthly || 1000000,
      cooldownSeconds: initialLimits?.cooldownSeconds || 60,
      maxActionsPerMinute: initialLimits?.maxActionsPerMinute || 10
    },
    
    settings: {
      whitelistedDomains: [new URL(gameUrl).origin],
      enableAnalytics: true,
      enableFraudDetection: true
    },
    
    status: 'testing', // Start in testing mode
    createdAt: new Date(),
    updatedAt: new Date()
  };
  
  // Store in database
  await db.games.insert(gameConfig);
  
  // Return credentials to developer
  res.json({
    gameId: gameConfig.gameId,
    apiKey,
    apiSecret,
    message: 'Save your API secret securely - it cannot be retrieved later'
  });
});

Runtime Validation Using Stored Config:

// When validating rewards, fetch limits from DB
app.post('/api/reward/trigger', async (req, res) => {
  const { 
    apiKey,
    sessionId, 
    action, 
    amount, 
    idempotencyKey,
    signature 
  } = req.body;
  
  // 1. Fetch game configuration from database
  const gameConfig = await db.games.findOne({ apiKey });
  if (!gameConfig || gameConfig.status !== 'active') {
    return res.status(401).json({ error: 'Invalid or inactive game' });
  }
  
  // 2. Verify signature using stored secret
  const expectedSig = createHmac('sha256', gameConfig.apiSecret)
    .update(JSON.stringify({ sessionId, action, amount }))
    .digest('hex');
    
  if (signature !== expectedSig) {
    await logSecurityEvent('INVALID_SIGNATURE', { apiKey, sessionId });
    return res.status(403).json({ error: 'Invalid signature' });
  }
  
  // 3. Check against admin-configured limits
  const { limits } = gameConfig;
  
  // Check per-action limit
  if (amount > limits.maxRewardPerAction) {
    return res.status(403).json({ 
      error: 'Exceeds maximum reward per action',
      limit: limits.maxRewardPerAction 
    });
  }
  
  // Check user daily limit
  const userDailyTotal = await getUserDailyRewards(sessionId, gameConfig.gameId);
  if (userDailyTotal + amount > limits.maxRewardPerUserDaily) {
    return res.status(403).json({ 
      error: 'User daily limit exceeded',
      limit: limits.maxRewardPerUserDaily 
    });
  }
  
  // Check game budget
  const gameDailySpent = await getGameDailySpend(gameConfig.gameId);
  if (gameDailySpent + amount > limits.maxGameBudgetDaily) {
    return res.status(403).json({ 
      error: 'Game daily budget exceeded',
      limit: limits.maxGameBudgetDaily 
    });
  }
  
  // 4. Check rate limiting
  const rateLimitKey = `rate:${sessionId}:${gameConfig.gameId}`;
  const recentActions = await redis.incr(rateLimitKey);
  if (recentActions === 1) {
    await redis.expire(rateLimitKey, 60); // 1 minute window
  }
  if (recentActions > limits.maxActionsPerMinute) {
    return res.status(429).json({ 
      error: 'Rate limit exceeded',
      limit: limits.maxActionsPerMinute 
    });
  }
  
  // 5. Check idempotency
  const existing = await redis.get(`idemp:${idempotencyKey}`);
  if (existing) {
    return res.status(409).json({ error: 'Duplicate request' });
  }
  
  // 6. All checks passed - issue reward
  await issueReward(sessionId, amount, gameConfig.gameId);
  
  // Store idempotency key
  await redis.setex(`idemp:${idempotencyKey}`, 86400, '1');
  
  res.json({ 
    success: true, 
    rewardId: crypto.randomUUID(),
    amount,
    userDailyRemaining: limits.maxRewardPerUserDaily - userDailyTotal - amount,
    gameDailyRemaining: limits.maxGameBudgetDaily - gameDailySpent - amount
  });
});

Admin Dashboard UI for Limit Configuration:

// Admin panel component for setting limits
const GameLimitsConfig = ({ gameId }) => {
  const [limits, setLimits] = useState({
    maxRewardPerAction: 100,
    maxRewardPerUserDaily: 1000,
    maxRewardPerUserHourly: 200,
    maxGameBudgetDaily: 50000,
    maxGameBudgetMonthly: 1000000,
    cooldownSeconds: 60,
    maxActionsPerMinute: 10
  });
  
  const updateLimits = async () => {
    const response = await fetch(`/api/admin/games/${gameId}/limits`, {
      method: 'PUT',
      headers: { 
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${adminToken}`
      },
      body: JSON.stringify(limits)
    });
    
    if (response.ok) {
      toast.success('Limits updated successfully');
    }
  };
  
  return (
    <div className="limits-config">
      <h3>Configure Reward Limits</h3>
      
      <label>Max Reward Per Action</label>
      <input 
        type="number" 
        value={limits.maxRewardPerAction}
        onChange={(e) => setLimits({...limits, maxRewardPerAction: e.target.value})}
      />
      
      <label>Max Per User (Daily)</label>
      <input 
        type="number" 
        value={limits.maxRewardPerUserDaily}
        onChange={(e) => setLimits({...limits, maxRewardPerUserDaily: e.target.value})}
      />
      
      <label>Game Budget (Daily)</label>
      <input 
        type="number" 
        value={limits.maxGameBudgetDaily}
        onChange={(e) => setLimits({...limits, maxGameBudgetDaily: e.target.value})}
      />
      
      {/* More limit inputs... */}
      
      <button onClick={updateLimits}>Save Limits</button>
    </div>
  );
};

6. Additional Security Measures

Domain Whitelisting:

const WHITELISTED_DOMAINS = [
  'https://bobs-game.com',
  'https://carols-racing.io'
];

// CSP Headers for iframe
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    `frame-ancestors ${WHITELISTED_DOMAINS.join(' ')};`
  );
  next();
});

Fraud Detection Rules:

  • Sudden spike in rewards from single IP
  • Multiple accounts from same device fingerprint
  • Rewards triggered outside normal game flow
  • Abnormal play patterns (too fast completion)
  • Geographic anomalies

Monitoring & Alerts:

// Real-time monitoring
const monitoringRules = {
  alertThresholds: {
    rewardsPerMinute: 1000,      // Alert if > 1000 rewards/min
    budgetUsagePercent: 80,      // Alert at 80% budget used
    failedValidations: 100,      // Alert on suspicious activity
    duplicateAttempts: 50        // Alert on duplicate attempts
  }
};

Communication Protocol

// Based on existing @looti/ipc patterns
interface LootiMessage {
  type: 'ready' | 'data' | 'status' | 'error' | 'reward';
  payload?: any;
}

// Game → Parent Messages
{
  type: 'data',
  payload: {
    event: 'game_action' | 'level_complete' | 'achievement_unlocked',
    data: {
      score?: number;
      level?: number;
      achievementId?: string;
      timestamp: number;
    }
  }
}

// Reward Request Message
{
  type: 'reward',
  payload: {
    action: 'trigger_reward',
    amount: number,
    reason: string,
    idempotencyKey: string,
    signature: string  // HMAC(secret, payload)
  }
}

// Parent → Game Messages
{
  type: 'data',
  payload: {
    event: 'session_validated' | 'reward_confirmed' | 'reward_rejected',
    data: {
      success: boolean;
      balance?: number;
      error?: string;
    }
  }
}

Security Considerations

  1. iframe Sandboxing

    • Restrict permissions for embedded games
    • Content Security Policy headers
    • Domain whitelist for approved games
  2. Rate Limiting

    • Per-user action limits
    • Per-game reward caps
    • Cooldown periods for rewards
  3. Validation Pipeline

    • Client-side event → SDK validation → Server validation → Fraud check → Reward issue

Monetization Model (Future)

  • For Game Developers:

    • Pay per unique player
    • Revenue share on in-game purchases
    • Bonus for high-engagement games
  • For Mini App Developers:

    • Free SDK integration
    • Optional premium features (custom themes, priority support)

Success Metrics

  • Developer Adoption: Number of games integrated
  • User Engagement: Average session time, repeat players
  • Reward Distribution: Tokens distributed vs. fraud prevented
  • Platform Growth: MAU, DAU, retention rates

Next Steps

  1. Finalize API specifications
  2. Set up development environment
  3. Create SDK boilerplates
  4. Build MVP with 1-2 test games
  5. Alpha test with select developers
  6. Iterate based on feedback
  7. Public beta launch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment