Architecture

Technical overview of the LokkoLabs platform design.

Tech Stack

Frontend

  • Next.js 14 - App Router, Server Components
  • React 18 - Concurrent features, Suspense
  • TailwindCSS - Utility-first styling
  • TypeScript - End-to-end type safety

Backend

  • Next.js API Routes - Serverless functions
  • Supabase - Auth, Database, Realtime, Storage
  • PostgreSQL - Primary data store with RLS
  • ElevenLabs - Text-to-speech generation

Layer Architecture

The codebase follows Domain-Driven Design principles with clear layer separation:

LayerPathResponsibility
Domainsrc/lib/domain/Pure business logic, types, validation. No React, no side effects.
Hookssrc/lib/hooks/React state + effects. Calls domain layer for logic.
API Clientsrc/lib/api/HTTP client functions for API calls.
Datasrc/lib/data/Supabase queries, server-side data fetching.
API Routessrc/app/api/Thin handlers that delegate to domain layer.
Componentssrc/components/Reusable UI components, both server and client.
Pagessrc/app/Next.js App Router pages and layouts.

Domain Layer Rules

  • Pure functions only - No React, no side effects
  • Types first - Define types before implementation
  • Result pattern - Use Result<T> for fallible operations
  • No Supabase imports - Pass client as parameter

Data Model

Core Entities

Companies

Organizations that run events. Have members with roles. Own events, content, and assets.

Events

Instances of lock-picking experiences. Have phases, teams, and attendees. Belong to a company. Have a unique access code.

Attendees

Event participants. Have generated identity (codename, number, cover job). Assigned to teams. Track progress through phases.

Teams

Groups within events. Identified by color. Map to physical tables. Compete in mission phase. Have aggregate stats.

Content Entities

Audio Clips

Pre-generated TTS audio. Stored in Supabase Storage. Referenced by segments. Have duration and pause metadata.

Segments

Atomic content units. Combine audio, media, and activities. Have type (intro, instruction, action, etc.). Ordered within phases.

Templates

Complete event content packages. Define all phases and segments. Can be cloned and customized per company.

Real-Time Architecture

LokkoLabs uses Supabase Realtime for live event synchronization:

Channels

ChannelPatternPurpose
Event Stateevent:{code}Phase changes, event-wide updates
Team Progressteam:{id}Completion updates, member status
Help Requestshelp:{eventId}Attendee help requests
Organizerorganizer:{eventId}Private organizer commands

Subscription Pattern

Components subscribe in useEffect with proper cleanup:

useEffect(() => {
  const channel = supabase
    .channel(`event:${eventCode}`)
    .on('broadcast', { event: 'phase_change' }, handler)
    .subscribe();

  return () => {
    supabase.removeChannel(channel);
  };
}, [eventCode]);

Authentication

Two authentication modes based on context:

Authenticated Users

Organizers, admins, company members. Full Supabase Auth with email/password. Session-based with JWT tokens.

  • • Sign in via /auth/login
  • • Session stored in cookies
  • • RLS policies enforce access

Code-Based Access

Event attendees. No account required. Access via event code, session stored locally.

  • • Join via /join/{code}
  • • Attendee ID in sessionStorage
  • • API validates event code

Database Security

PostgreSQL Row Level Security (RLS) enforces access control:

Policy Patterns

  • Platform admin - Check user_id againstprofiles.platform_role
  • Company member - Join through company_memberstable with role check
  • Event access - Validate attendee belongs to event viaattendees.event_id

Security Definer Functions

For complex access checks, use SECURITY DEFINER functions to avoid RLS recursion:

CREATE FUNCTION is_company_owner(company_id UUID)
RETURNS BOOLEAN AS $$
  SELECT EXISTS (
    SELECT 1 FROM company_members
    WHERE company_id = $1
      AND user_id = auth.uid()
      AND role = 'owner'
  );
$$ LANGUAGE sql SECURITY DEFINER;

File Structure

src/
├── app/                    # Next.js App Router
│   ├── (auth)/             # Auth pages (login, register)
│   ├── (marketing)/        # Public pages
│   ├── admin/              # Platform admin pages
│   ├── api/                # API routes
│   ├── app/                # Main app (events, attendee views)
│   ├── docs/               # Documentation site
│   └── organizer/          # Organizer dashboard
├── components/
│   ├── admin/              # Admin-specific components
│   ├── event/              # Event/attendee components
│   ├── organizer/          # Organizer dashboard components
│   └── ui/                 # Shared UI primitives
└── lib/
    ├── api/                # API client functions
    ├── data/               # Server-side queries
    ├── domain/             # Business logic + types
    ├── hooks/              # React hooks
    └── services/           # External service clients

Key Design Decisions

Segment-Based Audio

Audio is composed from atomic segments at runtime, reducing ElevenLabs API costs by ~80%. Team-specific announcements combine base segments with team names.

Code-Based Event Access

Attendees join via event codes without creating accounts. Simplifies onboarding and works well for one-time participants.

Phase-Based Progression

Events are structured as phases with defined content. Enables consistent experiences and progress tracking.

Shared Organizer Components

Test simulator and live control center share the same UI components to prevent drift and ensure consistency.

Next Steps