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:
| Layer | Path | Responsibility |
|---|---|---|
| Domain | src/lib/domain/ | Pure business logic, types, validation. No React, no side effects. |
| Hooks | src/lib/hooks/ | React state + effects. Calls domain layer for logic. |
| API Client | src/lib/api/ | HTTP client functions for API calls. |
| Data | src/lib/data/ | Supabase queries, server-side data fetching. |
| API Routes | src/app/api/ | Thin handlers that delegate to domain layer. |
| Components | src/components/ | Reusable UI components, both server and client. |
| Pages | src/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
| Channel | Pattern | Purpose |
|---|---|---|
| Event State | event:{code} | Phase changes, event-wide updates |
| Team Progress | team:{id} | Completion updates, member status |
| Help Requests | help:{eventId} | Attendee help requests |
| Organizer | organizer:{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_idagainstprofiles.platform_role - Company member - Join through
company_memberstable with role check - Event access - Validate attendee belongs to event via
attendees.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 clientsKey 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
- Explore the Audio System in detail
- Review the API Reference
- Understand Core Concepts