Initial project documentation and architecture

- Complete project requirements and game mechanics specification
- Comprehensive technical architecture with microservices design
- Security-first approach with authentication and anti-cheating measures
- Cross-platform UI strategy using Gluestack UI framework
- Development guidelines with Fiber framework and Ent ORM
- Observability strategy with Prometheus, Grafana, and Jaeger
- Self-hosted Zitadel authentication with repository pattern
- Implementation plan with 10 phases covering all aspects

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
master
oabrivard 1 year ago
commit 4143ceaf25

@ -0,0 +1,360 @@
# Know Foolery - Claude Code Project Guide
## Project Overview
Know Foolery is a cross-platform quiz game with a Go microservices backend and React/React Native frontend using Gluestack UI. This document provides guidance for future Claude Code sessions.
## Project Structure
```
knowfoolery/
├── docs/ # Project documentation
│ ├── architecture.md # System architecture
│ ├── api-specs.md # API specifications
│ └── deployment.md # Deployment guides
├── backend/ # Go microservices
│ ├── services/ # Individual microservices
│ │ ├── game-service/ # Game logic and session management
│ │ ├── question-service/ # Question CRUD operations
│ │ ├── user-service/ # User management
│ │ ├── leaderboard-service/ # Score and ranking logic
│ │ ├── session-service/ # Session and timer management
│ │ ├── admin-service/ # Admin panel backend
│ │ └── gateway-service/ # API gateway
│ ├── shared/ # Shared Go packages
│ │ ├── auth/ # JWT middleware and Zitadel integration
│ │ ├── database/ # Ent database client
│ │ ├── observability/ # Metrics and tracing
│ │ └── security/ # Security utilities
│ └── scripts/ # Build and deployment scripts
├── frontend/ # Cross-platform frontend
│ ├── packages/ # Shared packages
│ │ ├── ui-components/ # Gluestack UI components
│ │ ├── shared-logic/ # Business logic
│ │ └── shared-types/ # TypeScript types
│ ├── web/ # React web application
│ ├── mobile/ # React Native mobile app
│ └── desktop/ # Electron desktop app (future)
├── infrastructure/ # Infrastructure as code
│ ├── docker/ # Docker configurations
│ ├── kubernetes/ # K8s manifests
│ └── monitoring/ # Observability configs
├── tests/ # Testing utilities
│ ├── e2e/ # End-to-end tests
│ ├── load/ # Performance tests
│ └── security/ # Security tests
├── requirements.md # Project requirements
└── README.md # Setup and development guide
```
## Technology Stack
### Backend (Go)
- **Framework**: Fiber for HTTP routing
- **Database ORM**: Ent with PostgreSQL
- **Authentication**: Zitadel OAuth 2.0/OIDC
- **Observability**: Prometheus metrics, OpenTelemetry tracing
- **Communication**: gRPC for inter-service communication
- **Testing**: Go testing + testcontainers
- **Security**: Comprehensive input validation, JWT middleware
### Frontend (TypeScript)
- **Web**: React 18 with TypeScript
- **Mobile**: React Native with TypeScript
- **UI Framework**: Gluestack UI with NativeWind/Tailwind CSS
- **State Management**: React Context + useReducer
- **Testing**: Jest + React Testing Library + Playwright
### Database & Infrastructure
- **Primary Database**: PostgreSQL with Ent ORM
- **Cache**: Redis for sessions and performance
- **Authentication**: Self-hosted Zitadel
- **Deployment**: Docker + Kubernetes
- **Monitoring**: Prometheus + Grafana + Jaeger
## Key Architectural Decisions
### 1. Database Strategy
- **Development**: SQLite for fast local development
- **Testing**: PostgreSQL with testcontainers
- **Production**: PostgreSQL with high availability
- **ORM**: Ent (Facebook) for type-safe database operations
- **No Repository Pattern**: Direct Ent usage except for external APIs
### 2. Authentication Strategy
- **Local Development**: Mock JWT service
- **Production**: Self-hosted Zitadel
- **Repository Pattern**: Only for external integrations (Zitadel)
- **Security**: MFA required for admin accounts
### 3. Cross-Platform UI Strategy
- **Design System**: Gluestack UI for consistent components
- **Styling**: NativeWind/Tailwind CSS for utility-first styling
- **Code Sharing**: Same components work on web and mobile
- **Performance**: Native performance on mobile, optimized for web
## Development Guidelines
### Code Organization
```go
// Backend service structure
services/
├── game-service/
│ ├── cmd/main.go # Service entry point
│ ├── internal/
│ │ ├── handlers/ # HTTP handlers
│ │ ├── services/ # Business logic
│ │ └── models/ # Ent models
│ ├── api/ # API definitions
│ └── tests/ # Service tests
```
```typescript
// Frontend component structure
packages/ui-components/
├── src/
│ ├── GameCard/
│ │ ├── GameCard.tsx # Main component
│ │ ├── GameCard.test.tsx # Tests
│ │ └── index.ts # Exports
│ └── index.ts # Package exports
```
### Naming Conventions
- **Go**: PascalCase for exported, camelCase for unexported
- **TypeScript**: PascalCase for components, camelCase for functions
- **Files**: kebab-case for directories, PascalCase for components
- **Database**: snake_case for tables and columns
### Testing Standards
- **Unit Tests**: Required for all business logic
- **Integration Tests**: Required for database operations
- **E2E Tests**: Required for critical user journeys
- **Security Tests**: Required for all input validation
- **Performance Tests**: Required for high-load scenarios
## Security Requirements
### Critical Security Measures (Implemented Early)
1. **Input Validation**: All user inputs sanitized and validated
2. **SQL Injection Prevention**: Ent parameterized queries only
3. **XSS Protection**: HTML escaping and CSP headers
4. **Authentication**: JWT validation with secure middleware
5. **Rate Limiting**: Per-user and global rate limits
6. **HTTPS**: TLS encryption for all communications
7. **CORS**: Strict cross-origin policies
### Game Integrity
- **Server-Side Validation**: All game logic on backend
- **Score Verification**: Server calculates all scores
- **Session Security**: Tamper-proof session management
- **Anti-Cheating**: Multiple validation layers
## Common Commands
### Backend Development
```bash
# Start all services locally
docker-compose up -d
# Run specific service
cd backend/services/game-service
go run cmd/main.go
# Run tests with coverage
go test -v -cover ./...
# Generate Ent code
go generate ./...
```
### Frontend Development
```bash
# Install dependencies
npm install
# Start web development
cd frontend/web
npm run dev
# Start mobile development
cd frontend/mobile
npm run ios # or npm run android
# Run tests
npm test
# Build for production
npm run build
```
### Database Operations
```bash
# Run migrations
go run backend/scripts/migrate.go
# Reset database (development only)
go run backend/scripts/reset-db.go
# Seed test data
go run backend/scripts/seed.go
```
## Environment Configuration
### Development (.env.dev)
```bash
# Database
DATABASE_URL=sqlite://./dev.db
REDIS_URL=redis://localhost:6379
# Authentication
JWT_SECRET=dev-secret-key
ZITADEL_URL=http://localhost:8080
# Observability
PROMETHEUS_PORT=9090
JAEGER_ENDPOINT=http://localhost:14268
```
### Production (.env.prod)
```bash
# Database
DATABASE_URL=postgres://user:pass@db:5432/knowfoolery
REDIS_URL=redis://redis:6379
# Authentication
JWT_SECRET=${JWT_SECRET}
ZITADEL_URL=${ZITADEL_URL}
# Observability
PROMETHEUS_PORT=9090
JAEGER_ENDPOINT=${JAEGER_ENDPOINT}
```
## Development Workflow
### 1. Feature Development
1. Create feature branch from main
2. Implement backend service changes
3. Add comprehensive tests
4. Implement frontend components
5. Test cross-platform compatibility
6. Update documentation
7. Create pull request
### 2. Testing Checklist
- [ ] Unit tests pass
- [ ] Integration tests pass
- [ ] Security tests pass
- [ ] E2E tests pass
- [ ] Cross-platform UI tests pass
- [ ] Performance tests pass
### 3. Deployment Process
1. Merge to main branch
2. Automated CI/CD pipeline
3. Deploy to staging environment
4. Run smoke tests
5. Deploy to production
6. Monitor metrics and alerts
## Troubleshooting
### Common Issues
#### Database Connection Errors
```bash
# Check database status
docker-compose ps
# Reset database connection
docker-compose restart postgres
# Check logs
docker-compose logs postgres
```
#### Authentication Issues
```bash
# Verify JWT token
curl -H "Authorization: Bearer $TOKEN" localhost:8080/api/health
# Check Zitadel status
curl localhost:8080/.well-known/openid_configuration
```
#### Frontend Build Issues
```bash
# Clear node modules
rm -rf node_modules package-lock.json
npm install
# Clear build cache
npm run clean
npm run build
```
## Performance Optimization
### Backend Optimization
- Use Ent query optimization
- Implement Redis caching
- Profile with pprof
- Monitor with Prometheus
### Frontend Optimization
- Bundle size analysis
- React Native performance profiling
- Gluestack UI optimization
- Image and asset optimization
## Security Checklist
### Before Each Release
- [ ] Security dependencies updated
- [ ] Vulnerability scan passed
- [ ] Penetration testing completed
- [ ] Security headers configured
- [ ] Input validation tested
- [ ] Authentication flows verified
- [ ] Rate limiting tested
- [ ] Audit logs reviewed
## Monitoring and Alerts
### Key Metrics to Watch
- **Response Times**: API endpoint performance
- **Error Rates**: Application stability
- **Authentication**: Success/failure rates
- **Game Metrics**: Session duration, completion rates
- **Security Events**: Failed logins, rate limit hits
### Alert Conditions
- API response time > 500ms
- Error rate > 1%
- Authentication failure rate > 5%
- Database connection issues
- Security events detected
## Future Development Notes
### Mobile App Considerations
- Platform-specific optimizations needed
- App store submission requirements
- Push notification integration
- Offline capability planning
### Desktop App Considerations
- Electron performance optimization
- Native OS integration
- Auto-update mechanism
- Platform-specific packaging
### Scalability Planning
- Horizontal scaling strategies
- Database sharding considerations
- CDN integration for global reach
- Microservice boundaries refinement
This guide should be updated as the project evolves and new decisions are made.

@ -0,0 +1,157 @@
# Know Foolery - Claude Implementation Plan
## Project Overview
**Know Foolery** is a comprehensive quiz game application based on the French game "Déconnaissance". The project implements a cross-platform solution with web, mobile (iOS/Android), and desktop applications, featuring secure authentication, microservices architecture, and comprehensive observability.
## Technology Stack
- **Backend**: Go with Fiber framework, microservices architecture
- **Database**: PostgreSQL (production), SQLite (development)
- **ORM**: Ent for type-safe database operations
- **Cache**: Redis for session management and caching
- **Authentication**: Self-hosted Zitadel (OAuth 2.0/OIDC)
- **Frontend**: React (web), React Native (mobile), Wails (desktop)
- **UI Framework**: Gluestack UI for cross-platform consistency
- **Styling**: NativeWind/Tailwind CSS
- **Monitoring**: Prometheus, Grafana, Jaeger
- **Containerization**: Docker with Docker Compose
## Implementation Phases
### Phase 0: Documentation & Planning ✅ COMPLETED
- [x] Create requirements.md with complete application requirements
- [x] Create CLAUDE.md project guidance file
- [x] Document game mechanics and scoring system
- [x] Document technical requirements and architecture decisions
- [x] Create architecture documentation with Zitadel integration
- [x] Document development guidelines and coding standards
- [x] Document observability strategy
- [x] Document security requirements
- [x] Document cross-platform UI strategy with Gluestack UI
**Completion Date**: Phase 0 completed successfully with comprehensive documentation covering all aspects of the project.
### Phase 1: Project Setup & Core Security Infrastructure
- [ ] Initialize Go project structure with microservices architecture
- [ ] Set up Ent schema definitions for core entities (User, Quiz, Question, etc.)
- [ ] Create ZitadelRepository interface for external auth integration
- [ ] Set up development environment with Docker Compose
- [ ] Initialize Gluestack UI setup with theme configuration
- [ ] Implement core security middleware and input validation
- [ ] Set up Prometheus metrics collection foundation
- [ ] Create basic CI/CD pipeline structure
### Phase 2: Authentication & User Management
- [ ] Implement Zitadel integration with repository pattern
- [ ] Create JWT validation middleware with circuit breaker
- [ ] Build user registration and profile management
- [ ] Implement role-based access control (player/admin)
- [ ] Add MFA requirements for admin access
- [ ] Create secure token storage for each platform
- [ ] Implement comprehensive authentication testing
### Phase 3: Core Game Engine
- [ ] Implement quiz engine with theme and difficulty support
- [ ] Create question management system with validation
- [ ] Build scoring system with anti-cheating measures
- [ ] Implement session management (30-minute time limits)
- [ ] Add hint system with progressive revelation
- [ ] Create leaderboard functionality
- [ ] Implement comprehensive game testing
### Phase 4: Cross-Platform Frontend
- [ ] Build React web application with Gluestack UI
- [ ] Create React Native mobile applications
- [ ] Develop Wails desktop application
- [ ] Implement shared authentication flows
- [ ] Create responsive game interface components
- [ ] Add platform-specific optimizations
- [ ] Implement comprehensive frontend testing
### Phase 5: Admin Panel & Content Management
- [ ] Build secure admin dashboard
- [ ] Create question management interface
- [ ] Implement user administration tools
- [ ] Add analytics and reporting features
- [ ] Create content import/export functionality
- [ ] Implement admin-specific security measures
- [ ] Add comprehensive admin testing
### Phase 6: Performance & Optimization
- [ ] Implement Redis caching strategy
- [ ] Add database query optimization
- [ ] Create CDN integration for assets
- [ ] Implement API rate limiting
- [ ] Add frontend performance optimization
- [ ] Create load testing and benchmarks
### Phase 7: Monitoring & Observability
- [ ] Complete Prometheus metrics implementation
- [ ] Set up Grafana dashboards
- [ ] Implement distributed tracing with Jaeger
- [ ] Add comprehensive logging strategy
- [ ] Create alerting and notification system
- [ ] Implement health checks and monitoring
### Phase 8: Security Hardening
- [ ] Conduct comprehensive security audit
- [ ] Implement advanced anti-cheating measures
- [ ] Add security monitoring and incident response
- [ ] Create backup and disaster recovery procedures
- [ ] Implement data encryption at rest
- [ ] Add comprehensive security testing
### Phase 9: Production Deployment
- [ ] Set up production infrastructure
- [ ] Configure CI/CD for production deployment
- [ ] Implement monitoring and alerting in production
- [ ] Create deployment documentation
- [ ] Conduct production readiness review
- [ ] Implement production security measures
### Phase 10: Launch & Post-Launch
- [ ] Conduct final testing and quality assurance
- [ ] Deploy to production environment
- [ ] Monitor launch metrics and performance
- [ ] Address any post-launch issues
- [ ] Gather user feedback and analytics
- [ ] Plan future feature development
## Key Architectural Decisions
1. **Repository Pattern**: Used only for external API integrations (Zitadel), direct Ent client usage for database operations
2. **Security First**: Critical vulnerabilities addressed in early phases (1-3), enhancements in Phase 8
3. **Cross-Platform UI**: Gluestack UI for consistent design across web, mobile, and desktop
4. **Microservices**: Go with Fiber framework for high-performance, maintainable services
5. **Self-Hosted Auth**: Zitadel for complete control over authentication and user data
6. **Comprehensive Testing**: Unit and integration testing after each implementation phase
## Current Status
**Phase 0 Complete**: All documentation has been created and is ready for implementation. The project has comprehensive documentation covering:
- Complete requirements and specifications
- Technical architecture with microservices design
- Security requirements and implementation strategy
- Cross-platform UI strategy with Gluestack UI
- Development guidelines and coding standards
- Observability and monitoring strategy
- Game mechanics and anti-cheating measures
- Zitadel authentication integration
**Next Steps**: Ready to begin Phase 1 (Project Setup & Core Security Infrastructure) upon approval to proceed with implementation.
## Files Created
1. `/requirements.md` - Complete project requirements and specifications
2. `/CLAUDE.md` - Project guidance for future Claude Code sessions
3. `/docs/game-mechanics.md` - Game flow, scoring, and anti-cheating measures
4. `/docs/technical-architecture.md` - System architecture and service design
5. `/docs/zitadel-integration.md` - Authentication architecture and implementation
6. `/docs/development-guidelines.md` - Coding standards and patterns
7. `/docs/observability-strategy.md` - Monitoring and metrics strategy
8. `/docs/security-requirements.md` - Security strategy and requirements
9. `/docs/gluestack-ui-strategy.md` - Cross-platform UI implementation strategy
All documentation includes comprehensive code examples, implementation details, and best practices for building a secure, scalable quiz game application.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,476 @@
# Know Foolery - Game Mechanics Documentation
## Overview
Know Foolery is a quiz game where players answer questions to earn points within a time-limited session. The game emphasizes strategic thinking through its hint system and attempt limitations.
## Core Game Flow
### 1. Game Session Initialization
```
Player Input: Name → Session Creation → Question Selection → Game Start
```
**Process:**
1. Player enters their name (required)
2. System creates new game session
3. System randomly selects first question
4. 30-minute timer starts
5. Game begins with question display
**Validation:**
- Player name: 2-50 characters, alphanumeric + spaces
- Session uniqueness: One active session per player
- Timer initialization: Server-side timestamp
### 2. Question Presentation
**Question Structure:**
```typescript
interface Question {
id: string
theme: string // e.g., "Geography", "History", "Science"
text: string // The actual question
answer: string // Correct answer (case-insensitive)
hint: string // Optional hint text
difficulty?: number // Future enhancement
}
```
**Display Format:**
```
┌─────────────────────────────────────┐
│ Theme: Geography ⏱️ 28:45 │
├─────────────────────────────────────┤
│ │
│ What is the capital of France? │
│ │
│ [Your answer here... ] 💡 │
│ │
│ Attempts remaining: 3/3 │
│ Current score: 4 points │
└─────────────────────────────────────┘
```
### 3. Answer Submission Process
**Player Actions:**
1. **Direct Answer**: Submit answer without hint
2. **Request Hint**: View hint before answering
3. **Skip Question**: Move to next question (future enhancement)
**Answer Validation:**
```go
// Server-side validation logic
func ValidateAnswer(userAnswer, correctAnswer string) bool {
// Normalize both answers
user := strings.ToLower(strings.TrimSpace(userAnswer))
correct := strings.ToLower(strings.TrimSpace(correctAnswer))
// Exact match
if user == correct {
return true
}
// Fuzzy matching (future enhancement)
similarity := calculateSimilarity(user, correct)
return similarity > 0.85
}
```
## Scoring System
### Point Values
- **Correct without hint**: 2 points
- **Correct with hint**: 1 point
- **Incorrect answer**: 0 points
- **Session timeout**: 0 points for remaining questions
### Scoring Logic
```go
type AttemptResult struct {
IsCorrect bool
UsedHint bool
AttemptNum int // 1, 2, or 3
}
func CalculateScore(result AttemptResult) int {
if !result.IsCorrect {
return 0
}
if result.UsedHint {
return 1 // Reduced score for using hint
}
return 2 // Full score for direct correct answer
}
```
### Score Tracking
```go
type GameSession struct {
PlayerName string
TotalScore int
QuestionsAsked int
QuestionsCorrect int
HintsUsed int
StartTime time.Time
EndTime *time.Time
IsActive bool
}
```
## Attempt System
### Attempt Rules
- **Maximum Attempts**: 3 per question
- **Attempt Tracking**: Server-side validation
- **Attempt Consequences**:
- Attempt 1-3: Normal scoring if correct
- After 3 failed attempts: Move to next question automatically
### Attempt Flow
```go
type QuestionAttempt struct {
SessionID string
QuestionID string
AttemptNum int // 1, 2, or 3
Answer string
IsCorrect bool
UsedHint bool
Timestamp time.Time
}
func ProcessAttempt(sessionID, questionID, answer string) AttemptResult {
// 1. Validate session is active
// 2. Check attempts remaining
// 3. Validate answer
// 4. Update score if correct
// 5. Move to next question if needed
}
```
## Hint System
### Hint Mechanics
- **Availability**: One hint per question
- **Timing**: Can be requested before any attempt
- **Impact**: Reduces maximum score from 2 to 1 points
- **Content**: Provides helpful clue without giving away answer
### Hint Implementation
```typescript
interface HintRequest {
sessionId: string
questionId: string
}
interface HintResponse {
hint: string
scoreImpact: string // "Using this hint will reduce your score to 1 point"
attemptsRemaining: number
}
```
### Hint Examples
```javascript
const hintExamples = {
question: "What is the capital of France?",
hint: "This city is famous for the Eiffel Tower and is located on the Seine River.",
question: "Who painted the Mona Lisa?",
hint: "This Italian Renaissance artist was also an inventor and scientist.",
question: "What is the largest planet in our solar system?",
hint: "This gas giant is named after the king of the Roman gods."
}
```
## Time Management
### Session Timer
- **Duration**: 30 minutes maximum per session
- **Display**: Real-time countdown timer
- **Warnings**: Visual alerts at 5 minutes and 1 minute remaining
- **Timeout**: Automatic session termination when time expires
### Timer Implementation
```go
type SessionTimer struct {
SessionID string
StartTime time.Time
Duration time.Duration // 30 minutes
WarningsSent map[int]bool // Track warnings sent
}
func (t *SessionTimer) TimeRemaining() time.Duration {
elapsed := time.Since(t.StartTime)
remaining := t.Duration - elapsed
if remaining < 0 {
return 0
}
return remaining
}
func (t *SessionTimer) IsExpired() bool {
return t.TimeRemaining() == 0
}
```
### Timer Events
```typescript
interface TimerEvent {
type: 'warning' | 'timeout'
timeRemaining: number // seconds
message: string
}
// Timer warnings
const timerWarnings = [
{ at: 300, message: "5 minutes remaining!" }, // 5 minutes
{ at: 60, message: "1 minute remaining!" }, // 1 minute
{ at: 10, message: "10 seconds left!" } // 10 seconds
]
```
## Question Selection
### Random Selection Algorithm
```go
func SelectRandomQuestion(sessionID string, excludeAnswered bool) (*Question, error) {
var questions []Question
query := db.Question.Query()
if excludeAnswered {
// Exclude questions already answered in this session
answeredIDs := getAnsweredQuestions(sessionID)
query = query.Where(question.IDNotIn(answeredIDs...))
}
questions, err := query.All(ctx)
if err != nil {
return nil, err
}
if len(questions) == 0 {
return nil, errors.New("no more questions available")
}
// Random selection
randomIndex := rand.Intn(len(questions))
return &questions[randomIndex], nil
}
```
### Question Difficulty (Future Enhancement)
```go
type DifficultyLevel int
const (
Easy DifficultyLevel = iota + 1
Medium
Hard
)
// Adaptive difficulty based on player performance
func SelectQuestionByDifficulty(sessionID string, targetDifficulty DifficultyLevel) (*Question, error) {
// Implementation for adaptive difficulty
// Consider player's success rate, current score, etc.
}
```
## Leaderboard System
### Leaderboard Calculation
```go
type LeaderboardEntry struct {
PlayerName string
Score int
QuestionsAnswered int
SuccessRate float64 // Percentage of correct answers
SessionDuration time.Duration
CompletedAt time.Time
}
func CalculateLeaderboard() ([]LeaderboardEntry, error) {
sessions := getCompletedSessions()
entries := make([]LeaderboardEntry, 0, len(sessions))
for _, session := range sessions {
entry := LeaderboardEntry{
PlayerName: session.PlayerName,
Score: session.TotalScore,
QuestionsAnswered: session.QuestionsAsked,
SuccessRate: float64(session.QuestionsCorrect) / float64(session.QuestionsAsked) * 100,
SessionDuration: session.EndTime.Sub(session.StartTime),
CompletedAt: *session.EndTime,
}
entries = append(entries, entry)
}
// Sort by score (descending), then by completion time (ascending)
sort.Slice(entries, func(i, j int) bool {
if entries[i].Score == entries[j].Score {
return entries[i].CompletedAt.Before(entries[j].CompletedAt)
}
return entries[i].Score > entries[j].Score
})
// Return top 10
if len(entries) > 10 {
entries = entries[:10]
}
return entries, nil
}
```
### Leaderboard Display
```
🏆 LEADERBOARD
Rank | Player | Score | Questions | Success Rate | Duration
-----|-----------|-------|-----------|--------------|----------
1 | Alice | 24 | 14 | 86% | 28m
2 | Bob | 22 | 13 | 85% | 25m
3 | Charlie | 20 | 12 | 83% | 30m
4 | Diana | 18 | 11 | 82% | 22m
5 | Eve | 16 | 10 | 80% | 27m
```
## Game State Management
### Session States
```go
type SessionState int
const (
SessionCreated SessionState = iota
SessionActive
SessionPaused // Future enhancement
SessionCompleted
SessionTimedOut
SessionAbandoned
)
type GameSession struct {
ID string
PlayerName string
State SessionState
CurrentQuestion *Question
Score int
// ... other fields
}
```
### State Transitions
```
Created → Active → {Completed, TimedOut, Abandoned}
↓
Paused → Active (future enhancement)
```
## Anti-Cheating Measures
### Server-Side Validation
```go
type SecurityCheck struct {
SessionID string
QuestionID string
ExpectedAnswer string
SubmittedAnswer string
TimestampSubmit time.Time
TimestampShow time.Time
IsValid bool
Reason string
}
func ValidateSubmission(check SecurityCheck) bool {
// 1. Check minimum time between question show and answer submit
minTime := 2 * time.Second
if check.TimestampSubmit.Sub(check.TimestampShow) < minTime {
check.Reason = "Answer submitted too quickly"
return false
}
// 2. Validate session state
session := getSession(check.SessionID)
if session.State != SessionActive {
check.Reason = "Invalid session state"
return false
}
// 3. Check if question belongs to session
if session.CurrentQuestion.ID != check.QuestionID {
check.Reason = "Question mismatch"
return false
}
return true
}
```
### Client-Side Integrity
```typescript
// Prevent common cheating attempts
class GameIntegrity {
private questionStartTime: number = 0
onQuestionDisplayed() {
this.questionStartTime = Date.now()
// Disable browser dev tools (basic deterrent)
this.disableDevTools()
// Prevent copy-paste in answer input
this.disableCopyPaste()
}
onAnswerSubmit(answer: string) {
const timeTaken = Date.now() - this.questionStartTime
// Include timing in submission for server validation
return {
answer,
timeTaken,
timestamp: Date.now()
}
}
private disableDevTools() {
// Basic dev tools detection (not foolproof)
const devtools = {
open: false,
orientation: null
}
const threshold = 160
setInterval(() => {
if (window.outerHeight - window.innerHeight > threshold ||
window.outerWidth - window.innerWidth > threshold) {
console.warn('Developer tools detected')
// Log security event
}
}, 500)
}
}
```
## Future Enhancements
### Planned Features
1. **Question Categories**: Filter questions by subject
2. **Difficulty Progression**: Adaptive difficulty based on performance
3. **Multiplayer Mode**: Real-time competition between players
4. **Daily Challenges**: Special themed question sets
5. **Achievement System**: Badges and milestones
6. **Question Contributions**: Player-submitted questions
### Scoring Enhancements
1. **Time Bonus**: Extra points for quick correct answers
2. **Streak Bonus**: Consecutive correct answers bonus
3. **Difficulty Multiplier**: Higher points for harder questions
4. **Perfect Game Bonus**: Bonus for 100% correct rate
This game mechanics documentation ensures consistent implementation across all platforms and provides clear guidelines for future enhancements.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,837 @@
# Know Foolery - Technical Architecture Documentation
## Architecture Overview
Know Foolery follows a microservices architecture with clear separation between frontend presentation, backend services, and data persistence layers. The system is designed for cross-platform compatibility, scalability, and maintainability.
## System Architecture Diagram
```
┌────────────────────────────────────────────────────────────────────────────┐
│ Client Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Web App │ │ Mobile iOS │ │ Mobile Androi│ │ Desktop ctr│ │
│ │ (React) │ │(React Native)│ │(React Native)│ │ (Wails) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘
│
HTTPS/WSS
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ API Gateway Layer │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ NGINX + API Gateway Service (Go) │ │
│ │ • Authentication & Authorization │ │
│ │ • Rate Limiting & CORS │ │
│ │ • Request Routing & Load Balancing │ │
│ │ • Security Headers & Input Validation │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
gRPC/HTTP
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ Microservices Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Game │ │ Question │ │ User │ │ Leaderboard │ │
│ │ Service │ │ Service │ │ Service │ │ Service │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Session │ │ Admin │ │
│ │ Service │ │ Service │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
Database Connections
│
┌─────────────────────────────────────────────────────────────────────────────┐
│ Data & External Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Redis │ │ Zitadel │ │ Observabi- │ │
│ │ Primary │ │ Cache & │ │ OAuth │ │ lity │ │
│ │ Database │ │ Sessions │ │ Provider │ │ Stack │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## Technology Stack
### Frontend Technologies
```yaml
Web Application:
Framework: React 18.2+
Language: TypeScript 5.0+
Build Tool: Vite 4.0+
UI Library: Gluestack UI
Styling: NativeWind/Tailwind CSS 3.0+
State Management: React Context + useReducer
Testing: Jest + React Testing Library + Playwright
Mobile Applications:
Framework: React Native 0.81+
Language: TypeScript 5.9+
UI Library: Gluestack UI (Native)
Navigation: React Navigation 6+
State Management: React Context + useReducer
Testing: Jest + React Native Testing Library
Desktop Application:
Framework: Wails 2.10+
Template: wails-vite-react-ts-tailwind-template
```
### Backend Technologies
```yaml
Microservices:
Language: Go 1.25+
HTTP Framework: Fiber 3.0+
gRPC: Protocol Buffers + gRPC-Go
Database ORM: Ent (Facebook)
Authentication: JWT + Zitadel Integration
Testing: Go testing + testcontainers
API Gateway:
Reverse Proxy: NGINX
Gateway Service: Go + Fiber
Load Balancing: Round-robin + Health checks
Rate Limiting: Redis-based sliding window
```
### Data Technologies
```yaml
Primary Database:
Engine: PostgreSQL 15+
Connection Pooling: pgxpool
Migrations: Ent migrations
Backup: pg_dump + Object Storage
Cache Layer:
Engine: Redis 7+
Use Cases: Sessions, Rate limiting, Cache
Clustering: Redis Cluster (Production)
Persistence: RDB + AOF
Authentication:
Provider: Zitadel (Self-hosted)
Protocol: OAuth 2.0 + OpenID Connect
Tokens: JWT with RS256 signing
MFA: TOTP + WebAuthn
```
### Infrastructure Technologies
```yaml
Containerization:
Runtime: Docker 24+
Orchestration: Kubernetes 1.28+
Registry: Private Docker Registry
Observability:
Metrics: Prometheus + Node Exporter
Visualization: Grafana + Custom Dashboards
Tracing: Jaeger + OpenTelemetry
Logging: Structured logging + Loki
CI/CD:
Platform: GitHub Actions
Testing: Automated test suites
Security: SAST/DAST scanning
Deployment: GitOps with ArgoCD
```
## Microservices Design
### Service Boundaries
#### 1. Game Service
```go
// Responsibilities
type GameService struct {
// Core game logic and session management
SessionManager *SessionManager
ScoreCalculator *ScoreCalculator
AttemptTracker *AttemptTracker
TimerManager *TimerManager
}
// API Endpoints
POST /api/v1/game/start // Start new game session
GET /api/v1/game/session/{id} // Get session details
POST /api/v1/game/answer // Submit answer
POST /api/v1/game/hint // Request hint
POST /api/v1/game/end // End session
GET /api/v1/game/status/{id} // Get session status
```
#### 2. Question Service
```go
// Responsibilities
type QuestionService struct {
QuestionRepo *QuestionRepository
ThemeManager *ThemeManager
Randomizer *QuestionRandomizer
Validator *AnswerValidator
}
// API Endpoints
GET /api/v1/questions/random // Get random question
GET /api/v1/questions/{id} // Get specific question
GET /api/v1/questions/themes // List available themes
POST /api/v1/questions // Create question (admin)
PUT /api/v1/questions/{id} // Update question (admin)
DELETE /api/v1/questions/{id} // Delete question (admin)
```
#### 3. User Service
```go
// Responsibilities
type UserService struct {
UserRepo *UserRepository
ProfileManager *ProfileManager
SessionTracker *SessionTracker
}
// API Endpoints
POST /api/v1/users/register // Register new player
GET /api/v1/users/profile // Get user profile
PUT /api/v1/users/profile // Update profile
GET /api/v1/users/sessions // Get session history
DELETE /api/v1/users/profile // Delete account (GDPR)
```
#### 4. Leaderboard Service
```go
// Responsibilities
type LeaderboardService struct {
ScoreAggregator *ScoreAggregator
RankingEngine *RankingEngine
StatsCalculator *StatsCalculator
}
// API Endpoints
GET /api/v1/leaderboard/top10 // Get top 10 scores
GET /api/v1/leaderboard/stats // Get game statistics
GET /api/v1/leaderboard/player/{id} // Get player ranking
POST /api/v1/leaderboard/update // Update scores (internal)
```
#### 5. Session Service
```go
// Responsibilities
type SessionService struct {
SessionStore *SessionStore
TimerManager *TimerManager
StateManager *StateManager
}
// API Endpoints
POST /api/v1/sessions // Create session
GET /api/v1/sessions/{id} // Get session
PUT /api/v1/sessions/{id} // Update session
DELETE /api/v1/sessions/{id} // End session
GET /api/v1/sessions/{id}/timer // Get timer status
```
#### 6. Admin Service
```go
// Responsibilities
type AdminService struct {
AuthManager *AuthManager
QuestionMgmt *QuestionManagement
UserMgmt *UserManagement
AuditLogger *AuditLogger
}
// API Endpoints
POST /api/v1/admin/auth // Admin authentication
GET /api/v1/admin/dashboard // Dashboard data
GET /api/v1/admin/questions // List all questions
POST /api/v1/admin/questions/bulk // Bulk question import
GET /api/v1/admin/users // User management
GET /api/v1/admin/audit // Audit logs
```
### Inter-Service Communication
#### Service Mesh Architecture
```yaml
Communication Patterns:
Synchronous: gRPC for real-time operations
Asynchronous: Event-driven via message queues (future)
Service Discovery:
Registry: Kubernetes DNS
Health Checks: HTTP /health endpoints
Load Balancing: Round-robin with health awareness
Circuit Breaker:
Pattern: Hystrix-style circuit breakers
Fallback: Graceful degradation
Timeout: Context-based timeouts
```
#### gRPC Service Definitions
```protobuf
// game_service.proto
service GameService {
rpc StartGame(StartGameRequest) returns (StartGameResponse);
rpc SubmitAnswer(SubmitAnswerRequest) returns (SubmitAnswerResponse);
rpc GetSession(GetSessionRequest) returns (GetSessionResponse);
rpc EndGame(EndGameRequest) returns (EndGameResponse);
}
// question_service.proto
service QuestionService {
rpc GetRandomQuestion(RandomQuestionRequest) returns (Question);
rpc ValidateAnswer(ValidateAnswerRequest) returns (ValidationResponse);
rpc GetQuestionHint(HintRequest) returns (HintResponse);
}
// leaderboard_service.proto
service LeaderboardService {
rpc UpdateScore(UpdateScoreRequest) returns (UpdateScoreResponse);
rpc GetTopScores(TopScoresRequest) returns (TopScoresResponse);
rpc GetPlayerRank(PlayerRankRequest) returns (PlayerRankResponse);
}
```
## Database Architecture
### Data Model Design
#### Core Entities (Ent Schemas)
```go
// Question Entity
type Question struct {
ent.Schema
}
func (Question) Fields() []ent.Field {
return []ent.Field{
field.String("theme").NotEmpty(),
field.Text("text").NotEmpty(),
field.String("answer").NotEmpty(),
field.Text("hint").Optional(),
field.Enum("difficulty").Values("easy", "medium", "hard").Default("medium"),
field.Bool("is_active").Default(true),
field.Time("created_at").Default(time.Now),
field.Time("updated_at").Default(time.Now).UpdateDefault(time.Now),
}
}
func (Question) Indexes() []ent.Index {
return []ent.Index{
index.Fields("theme"),
index.Fields("difficulty"),
index.Fields("is_active"),
index.Fields("created_at"),
}
}
// GameSession Entity
type GameSession struct {
ent.Schema
}
func (GameSession) Fields() []ent.Field {
return []ent.Field{
field.String("player_name").NotEmpty(),
field.Int("total_score").Default(0),
field.Int("questions_asked").Default(0),
field.Int("questions_correct").Default(0),
field.Int("hints_used").Default(0),
field.Time("start_time").Default(time.Now),
field.Time("end_time").Optional().Nillable(),
field.Enum("status").Values("active", "completed", "timeout", "abandoned").Default("active"),
field.String("current_question_id").Optional(),
field.Int("current_attempts").Default(0),
}
}
func (GameSession) Edges() []ent.Edge {
return []ent.Edge{
edge.To("attempts", QuestionAttempt.Type),
edge.To("current_question", Question.Type).Unique().Field("current_question_id"),
}
}
// QuestionAttempt Entity
type QuestionAttempt struct {
ent.Schema
}
func (QuestionAttempt) Fields() []ent.Field {
return []ent.Field{
field.String("session_id"),
field.String("question_id"),
field.Int("attempt_number"),
field.String("submitted_answer"),
field.Bool("is_correct"),
field.Bool("used_hint"),
field.Int("points_awarded"),
field.Time("submitted_at").Default(time.Now),
field.Duration("time_taken"),
}
}
func (QuestionAttempt) Edges() []ent.Edge {
return []ent.Edge{
edge.From("session", GameSession.Type).Ref("attempts").Unique().Field("session_id"),
edge.To("question", Question.Type).Unique().Field("question_id"),
}
}
```
#### Database Performance Optimization
```sql
-- Indexes for optimal query performance
CREATE INDEX idx_questions_theme_active ON questions(theme, is_active);
CREATE INDEX idx_sessions_status_start_time ON game_sessions(status, start_time);
CREATE INDEX idx_attempts_session_question ON question_attempts(session_id, question_id);
CREATE INDEX idx_leaderboard_score_time ON game_sessions(total_score DESC, end_time ASC) WHERE status = 'completed';
-- Partitioning for large datasets (future scaling)
CREATE TABLE game_sessions_y2024m01 PARTITION OF game_sessions
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
```
### Database Connection Management
```go
// Database configuration
type DatabaseConfig struct {
Primary DatabaseInstance
Replicas []DatabaseInstance
Pool PoolConfig
}
type DatabaseInstance struct {
Host string
Port int
Database string
Username string
Password string
SSLMode string
}
type PoolConfig struct {
MaxOpenConns int // 25
MaxIdleConns int // 5
ConnMaxLifetime time.Duration // 30 minutes
ConnMaxIdleTime time.Duration // 15 minutes
}
// Connection pooling with read/write splitting
func NewDatabaseClient(config DatabaseConfig) (*ent.Client, error) {
primaryDSN := formatDSN(config.Primary)
// Primary database for writes
primaryDB, err := sql.Open("postgres", primaryDSN)
if err != nil {
return nil, err
}
configurePools(primaryDB, config.Pool)
// Create Ent client with instrumented driver
drv := entsql.OpenDB("postgres", primaryDB)
return ent.NewClient(ent.Driver(drv)), nil
}
```
## Security Architecture
### Authentication Flow
```mermaid
sequenceDiagram
participant C as Client
participant G as API Gateway
participant Z as Zitadel
participant S as Service
C->>G: Request with credentials
G->>Z: Validate credentials
Z->>G: Return JWT token
G->>C: Return token + user info
C->>G: API request with JWT
G->>G: Validate JWT signature
G->>S: Forward request with user context
S->>G: Return response
G->>C: Return response
```
### Authorization Strategy
```go
// Role-based access control
type Role string
const (
RolePlayer Role = "player"
RoleAdmin Role = "admin"
)
type Permissions struct {
CanPlayGame bool
CanViewScores bool
CanManageQuestions bool
CanViewAuditLogs bool
CanManageUsers bool
}
var RolePermissions = map[Role]Permissions{
RolePlayer: {
CanPlayGame: true,
CanViewScores: true,
},
RoleAdmin: {
CanPlayGame: true,
CanViewScores: true,
CanManageQuestions: true,
CanViewAuditLogs: true,
CanManageUsers: true,
},
}
// JWT middleware with role checking
func AuthMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
token := extractToken(c.Get("Authorization"))
claims, err := validateJWT(token)
if err != nil {
return c.Status(401).JSON(fiber.Map{"error": "Invalid token"})
}
c.Locals("user_id", claims.Subject)
c.Locals("user_role", claims.Role)
c.Locals("permissions", RolePermissions[Role(claims.Role)])
return c.Next()
}
}
func RequirePermission(permission func(Permissions) bool) fiber.Handler {
return func(c *fiber.Ctx) error {
perms := c.Locals("permissions").(Permissions)
if !permission(perms) {
return c.Status(403).JSON(fiber.Map{"error": "Insufficient permissions"})
}
return c.Next()
}
}
```
### Input Validation & Security
```go
// Comprehensive input validation
type InputValidator struct {
maxAnswerLength int
maxNameLength int
allowedChars *regexp.Regexp
}
func NewInputValidator() *InputValidator {
return &InputValidator{
maxAnswerLength: 500,
maxNameLength: 50,
allowedChars: regexp.MustCompile(`^[a-zA-Z0-9\s\-_.]+$`),
}
}
func (v *InputValidator) ValidateAnswer(answer string) error {
if len(answer) == 0 {
return errors.New("answer cannot be empty")
}
if len(answer) > v.maxAnswerLength {
return errors.New("answer too long")
}
// Sanitize HTML and potential XSS
clean := html.EscapeString(strings.TrimSpace(answer))
if clean != answer {
return errors.New("invalid characters in answer")
}
return nil
}
func (v *InputValidator) ValidatePlayerName(name string) error {
if len(name) < 2 || len(name) > v.maxNameLength {
return errors.New("name must be 2-50 characters")
}
if !v.allowedChars.MatchString(name) {
return errors.New("name contains invalid characters")
}
return nil
}
// Rate limiting implementation
type RateLimiter struct {
redis *redis.Client
limits map[string]RateLimit
}
type RateLimit struct {
Requests int // Number of requests
Window time.Duration // Time window
}
func (rl *RateLimiter) Allow(key string, limit RateLimit) bool {
current, err := rl.redis.Incr(key).Result()
if err != nil {
return false // Fail closed
}
if current == 1 {
rl.redis.Expire(key, limit.Window)
}
return current <= int64(limit.Requests)
}
// Usage in middleware
func RateLimitMiddleware() fiber.Handler {
limiter := NewRateLimiter()
return func(c *fiber.Ctx) error {
userID := c.Locals("user_id").(string)
clientIP := c.IP()
// Per-user rate limiting
userKey := fmt.Sprintf("rate_limit:user:%s", userID)
if !limiter.Allow(userKey, RateLimit{Requests: 60, Window: time.Minute}) {
return c.Status(429).JSON(fiber.Map{"error": "Rate limit exceeded"})
}
// Per-IP rate limiting
ipKey := fmt.Sprintf("rate_limit:ip:%s", clientIP)
if !limiter.Allow(ipKey, RateLimit{Requests: 100, Window: time.Minute}) {
return c.Status(429).JSON(fiber.Map{"error": "Rate limit exceeded"})
}
return c.Next()
}
}
```
## Cross-Platform Frontend Architecture
### Gluestack UI Integration
```typescript
// Shared component library structure
// packages/ui-components/src/index.ts
export { GameCard } from './GameCard'
export { Leaderboard } from './Leaderboard'
export { Timer } from './Timer'
export { ScoreDisplay } from './ScoreDisplay'
export { AdminPanel } from './AdminPanel'
// Component example with Gluestack UI
// packages/ui-components/src/GameCard/GameCard.tsx
import {
Card,
VStack,
HStack,
Text,
Input,
Button,
Badge,
Progress,
Box
} from '@gluestack-ui/themed'
export interface GameCardProps {
question: string
theme: string
timeRemaining: number
attemptsLeft: number
currentScore: number
onSubmitAnswer: (answer: string) => void
onRequestHint: () => void
isLoading?: boolean
}
export const GameCard: React.FC<GameCardProps> = ({
question,
theme,
timeRemaining,
attemptsLeft,
currentScore,
onSubmitAnswer,
onRequestHint,
isLoading = false
}) => {
const [answer, setAnswer] = useState('')
const handleSubmit = () => {
if (answer.trim()) {
onSubmitAnswer(answer.trim())
setAnswer('')
}
}
return (
<Card size="lg" variant="elevated" m="$4">
<VStack space="md" p="$4">
{/* Header with theme and timer */}
<HStack justifyContent="space-between" alignItems="center">
<Badge size="sm" variant="solid" action="info">
<Text color="$white" fontSize="$sm" fontWeight="$semibold">
{theme}
</Text>
</Badge>
<HStack space="sm" alignItems="center">
<Text fontSize="$sm" color="$textLight600">
⏱️ {Math.floor(timeRemaining / 60)}:{(timeRemaining % 60).toString().padStart(2, '0')}
</Text>
</HStack>
</HStack>
{/* Progress indicator */}
<Progress value={((30 * 60 - timeRemaining) / (30 * 60)) * 100} size="sm">
<ProgressFilledTrack />
</Progress>
{/* Question */}
<Box>
<Text fontSize="$lg" fontWeight="$semibold" lineHeight="$xl">
{question}
</Text>
</Box>
{/* Answer input */}
<Input size="lg" isDisabled={isLoading}>
<InputField
placeholder="Enter your answer..."
value={answer}
onChangeText={setAnswer}
autoCapitalize="none"
autoCorrect={false}
/>
</Input>
{/* Game stats */}
<HStack justifyContent="space-between" alignItems="center">
<Text fontSize="$sm" color="$textLight600">
Attempts left: {attemptsLeft}/3
</Text>
<Text fontSize="$sm" color="$textLight600">
Score: {currentScore} points
</Text>
</HStack>
{/* Action buttons */}
<HStack space="sm">
<Button
size="lg"
variant="solid"
action="primary"
flex={1}
onPress={handleSubmit}
isDisabled={!answer.trim() || isLoading}
>
<ButtonText>Submit Answer</ButtonText>
</Button>
<Button
size="lg"
variant="outline"
action="secondary"
onPress={onRequestHint}
isDisabled={isLoading}
>
<ButtonText>💡 Hint</ButtonText>
</Button>
</HStack>
</VStack>
</Card>
)
}
```
### Platform-Specific Adaptations
```typescript
// Platform detection and adaptation
// packages/shared-logic/src/platform.ts
export interface PlatformCapabilities {
hasTouch: boolean
hasKeyboard: boolean
hasCamera: boolean
canVibrate: boolean
supportsNotifications: boolean
isOfflineCapable: boolean
}
export const getPlatformCapabilities = (): PlatformCapabilities => {
// Web platform
if (typeof window !== 'undefined') {
return {
hasTouch: 'ontouchstart' in window,
hasKeyboard: true,
hasCamera: !!navigator.mediaDevices?.getUserMedia,
canVibrate: !!navigator.vibrate,
supportsNotifications: 'Notification' in window,
isOfflineCapable: 'serviceWorker' in navigator
}
}
// React Native platform
if (typeof require !== 'undefined') {
try {
const { Platform } = require('react-native')
return {
hasTouch: true,
hasKeyboard: Platform.OS === 'ios' || Platform.OS === 'android',
hasCamera: true,
canVibrate: true,
supportsNotifications: true,
isOfflineCapable: true
}
} catch {
// Fallback for other environments
}
}
// Default capabilities
return {
hasTouch: false,
hasKeyboard: true,
hasCamera: false,
canVibrate: false,
supportsNotifications: false,
isOfflineCapable: false
}
}
// Responsive design utilities
export const useResponsiveValue = <T>(values: {
mobile: T
tablet: T
desktop: T
}) => {
const [screenSize, setScreenSize] = useState<'mobile' | 'tablet' | 'desktop'>('desktop')
useEffect(() => {
const updateScreenSize = () => {
const width = window.innerWidth
if (width < 768) {
setScreenSize('mobile')
} else if (width < 1024) {
setScreenSize('tablet')
} else {
setScreenSize('desktop')
}
}
updateScreenSize()
window.addEventListener('resize', updateScreenSize)
return () => window.removeEventListener('resize', updateScreenSize)
}, [])
return values[screenSize]
}
```
This technical architecture provides a solid foundation for building a scalable, secure, and maintainable quiz game platform with cross-platform capabilities.

@ -0,0 +1,882 @@
# Know Foolery - Zitadel Integration Architecture
## Overview
Zitadel serves as the self-hosted OAuth 2.0/OpenID Connect authentication provider for Know Foolery, providing secure authentication for both players and administrators while maintaining complete control over user data and authentication flows.
## Architecture Integration
### Zitadel Deployment Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ Client Applications │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Web App │ │ Mobile iOS │ │Mobile Android│ │Desktop Wails│ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
OAuth 2.0/OIDC Flows
│
┌─────────────────────────────────────────────────────────────────────┐
│ API Gateway │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ JWT Validation Middleware │ │
│ │ • Token signature verification │ │
│ │ • Claims validation & role extraction │ │
│ │ • Token refresh handling │ │
│ └────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
Validated Requests
│
┌─────────────────────────────────────────────────────────────────────┐
│ Microservices Layer │
│ (Receives user context from JWT claims) │
└─────────────────────────────────────────────────────────────────────┘
│
Zitadel Admin API
│
┌─────────────────────────────────────────────────────────────────────┐
│ Zitadel Instance │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ OAuth │ │ Admin API │ │ PostgreSQL │ │ OIDC │ │
│ │ Provider │ │ │ │ Database │ │ Discovery │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
## Zitadel Configuration
### Project and Application Setup
```yaml
# Zitadel Project Configuration
project:
name: "Know Foolery Quiz Game"
description: "Cross-platform quiz game authentication"
applications:
web_app:
name: "Know Foolery Web"
type: "WEB"
auth_method: "PKCE"
redirect_uris:
- "http://localhost:3000/auth/callback" # Development
- "https://app.knowfoolery.com/auth/callback" # Production
post_logout_redirect_uris:
- "http://localhost:3000/"
- "https://app.knowfoolery.com/"
mobile_app:
name: "Know Foolery Mobile"
type: "NATIVE"
auth_method: "PKCE"
redirect_uris:
- "knowfoolery://auth/callback"
admin_app:
name: "Know Foolery Admin"
type: "WEB"
auth_method: "PKCE"
redirect_uris:
- "http://localhost:3001/admin/auth/callback"
- "https://admin.knowfoolery.com/auth/callback"
roles:
- name: "player"
display_name: "Quiz Player"
description: "Can play quiz games and view leaderboards"
- name: "admin"
display_name: "Game Administrator"
description: "Can manage questions, users, and system settings"
policies:
password_policy:
min_length: 8
has_uppercase: true
has_lowercase: true
has_number: true
has_symbol: false
lockout_policy:
max_password_attempts: 5
show_lockout_failure: true
mfa_policy:
force_mfa: true # Required for admin role
force_mfa_local_only: false
```
### Zitadel Repository Pattern Implementation
```go
// ZitadelRepository interface for external integration
package auth
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)
type ZitadelRepository interface {
ValidateToken(ctx context.Context, token string) (*AuthClaims, error)
RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error)
GetUserInfo(ctx context.Context, accessToken string) (*UserInfo, error)
RevokeToken(ctx context.Context, token string) error
CreateUser(ctx context.Context, user *CreateUserRequest) (*UserResponse, error)
GetUserByID(ctx context.Context, userID string) (*UserInfo, error)
UpdateUserRoles(ctx context.Context, userID string, roles []string) error
}
type zitadelRepository struct {
client *http.Client
baseURL string
projectID string
adminToken string
publicKeys map[string]*PublicKey
publicKeysExpiry time.Time
circuitBreaker *CircuitBreaker
}
type AuthClaims struct {
Subject string `json:"sub"`
Email string `json:"email"`
Name string `json:"name"`
Roles []string `json:"urn:zitadel:iam:org:project:roles"`
Audience []string `json:"aud"`
Issuer string `json:"iss"`
IssuedAt int64 `json:"iat"`
ExpiresAt int64 `json:"exp"`
MFAVerified bool `json:"amr"`
}
type TokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
type UserInfo struct {
ID string `json:"sub"`
Email string `json:"email"`
Name string `json:"name"`
Verified bool `json:"email_verified"`
Roles []string
}
func NewZitadelRepository(config ZitadelConfig) ZitadelRepository {
return &zitadelRepository{
client: &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
},
baseURL: config.BaseURL,
projectID: config.ProjectID,
adminToken: config.AdminToken,
publicKeys: make(map[string]*PublicKey),
circuitBreaker: NewCircuitBreaker(),
}
}
func (r *zitadelRepository) ValidateToken(ctx context.Context, token string) (*AuthClaims, error) {
// Circuit breaker pattern for resilience
result, err := r.circuitBreaker.Execute(func() (interface{}, error) {
return r.validateTokenInternal(ctx, token)
})
if err == ErrCircuitOpen {
// Fallback: validate token locally if Zitadel is unavailable
return r.validateTokenLocally(token)
}
if err != nil {
return nil, fmt.Errorf("token validation failed: %w", err)
}
return result.(*AuthClaims), nil
}
func (r *zitadelRepository) validateTokenInternal(ctx context.Context, token string) (*AuthClaims, error) {
// Parse JWT token
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
// Verify signing algorithm
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
// Get public key from Zitadel JWKS endpoint
keyID := token.Header["kid"].(string)
return r.getPublicKey(ctx, keyID)
})
if err != nil {
return nil, fmt.Errorf("failed to parse token: %w", err)
}
if !parsedToken.Valid {
return nil, fmt.Errorf("invalid token")
}
// Extract claims
claims, ok := parsedToken.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("invalid token claims")
}
return r.extractAuthClaims(claims)
}
func (r *zitadelRepository) getPublicKey(ctx context.Context, keyID string) (*rsa.PublicKey, error) {
// Check cache first
if key, exists := r.publicKeys[keyID]; exists && time.Now().Before(r.publicKeysExpiry) {
return key.RSAPublicKey, nil
}
// Fetch from Zitadel JWKS endpoint
jwksURL := fmt.Sprintf("%s/.well-known/openid_configuration", r.baseURL)
req, err := http.NewRequestWithContext(ctx, "GET", jwksURL, nil)
if err != nil {
return nil, err
}
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var jwks JWKS
if err := json.NewDecoder(resp.Body).Decode(&jwks); err != nil {
return nil, err
}
// Update cache
r.updatePublicKeysCache(jwks)
key, exists := r.publicKeys[keyID]
if !exists {
return nil, fmt.Errorf("key ID %s not found", keyID)
}
return key.RSAPublicKey, nil
}
func (r *zitadelRepository) RefreshToken(ctx context.Context, refreshToken string) (*TokenResponse, error) {
data := url.Values{
"grant_type": {"refresh_token"},
"refresh_token": {refreshToken},
"client_id": {r.projectID},
}
req, err := http.NewRequestWithContext(ctx, "POST",
fmt.Sprintf("%s/oauth/v2/token", r.baseURL),
strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("token refresh failed with status: %d", resp.StatusCode)
}
var tokenResp TokenResponse
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
return nil, err
}
return &tokenResp, nil
}
func (r *zitadelRepository) CreateUser(ctx context.Context, user *CreateUserRequest) (*UserResponse, error) {
reqBody := map[string]interface{}{
"userName": user.Email,
"profile": map[string]interface{}{
"firstName": user.FirstName,
"lastName": user.LastName,
"displayName": user.DisplayName,
},
"email": map[string]interface{}{
"email": user.Email,
"isEmailVerified": false,
},
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, "POST",
fmt.Sprintf("%s/management/v1/users/human", r.baseURL),
bytes.NewBuffer(jsonBody))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.adminToken))
req.Header.Set("Content-Type", "application/json")
resp, err := r.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var userResp UserResponse
if err := json.NewDecoder(resp.Body).Decode(&userResp); err != nil {
return nil, err
}
return &userResp, nil
}
// Circuit breaker implementation for resilience
type CircuitBreaker struct {
state CircuitState
failureCount int
threshold int
timeout time.Duration
lastFailTime time.Time
mutex sync.RWMutex
}
type CircuitState int
const (
Closed CircuitState = iota
Open
HalfOpen
)
var ErrCircuitOpen = errors.New("circuit breaker is open")
func NewCircuitBreaker() *CircuitBreaker {
return &CircuitBreaker{
state: Closed,
threshold: 5,
timeout: 30 * time.Second,
}
}
func (cb *CircuitBreaker) Execute(fn func() (interface{}, error)) (interface{}, error) {
cb.mutex.RLock()
state := cb.state
cb.mutex.RUnlock()
switch state {
case Open:
if time.Since(cb.lastFailTime) > cb.timeout {
cb.setState(HalfOpen)
return cb.tryExecute(fn)
}
return nil, ErrCircuitOpen
case HalfOpen:
return cb.tryExecute(fn)
default: // Closed
return cb.tryExecute(fn)
}
}
func (cb *CircuitBreaker) tryExecute(fn func() (interface{}, error)) (interface{}, error) {
result, err := fn()
cb.mutex.Lock()
defer cb.mutex.Unlock()
if err != nil {
cb.failureCount++
cb.lastFailTime = time.Now()
if cb.failureCount >= cb.threshold {
cb.state = Open
}
return nil, err
}
// Success - reset circuit breaker
cb.failureCount = 0
cb.state = Closed
return result, nil
}
func (cb *CircuitBreaker) setState(state CircuitState) {
cb.mutex.Lock()
defer cb.mutex.Unlock()
cb.state = state
}
```
## Authentication Flows
### Player Authentication Flow (Web/Mobile)
```typescript
// Frontend authentication service
export class AuthService {
private zitadelConfig: ZitadelConfig
constructor(config: ZitadelConfig) {
this.zitadelConfig = config
}
async loginPlayer(): Promise<AuthResult> {
// PKCE flow for security
const codeVerifier = this.generateCodeVerifier()
const codeChallenge = await this.generateCodeChallenge(codeVerifier)
// Store code verifier securely
await this.secureStorage.store('code_verifier', codeVerifier)
// Redirect to Zitadel authorization endpoint
const authURL = new URL(`${this.zitadelConfig.baseURL}/oauth/v2/authorize`)
authURL.searchParams.set('client_id', this.zitadelConfig.clientId)
authURL.searchParams.set('response_type', 'code')
authURL.searchParams.set('scope', 'openid profile email urn:zitadel:iam:org:project:roles')
authURL.searchParams.set('redirect_uri', this.zitadelConfig.redirectUri)
authURL.searchParams.set('code_challenge', codeChallenge)
authURL.searchParams.set('code_challenge_method', 'S256')
authURL.searchParams.set('state', this.generateState())
// Platform-specific redirect
if (this.isPlatform('web')) {
window.location.href = authURL.toString()
} else if (this.isPlatform('mobile')) {
await this.openAuthSession(authURL.toString())
}
}
async handleAuthCallback(code: string, state: string): Promise<TokenResult> {
// Verify state parameter
const storedState = await this.secureStorage.get('auth_state')
if (state !== storedState) {
throw new Error('Invalid state parameter')
}
// Exchange code for tokens
const codeVerifier = await this.secureStorage.get('code_verifier')
const tokenResponse = await fetch(`${this.zitadelConfig.baseURL}/oauth/v2/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'authorization_code',
client_id: this.zitadelConfig.clientId,
code,
redirect_uri: this.zitadelConfig.redirectUri,
code_verifier: codeVerifier,
}),
})
if (!tokenResponse.ok) {
throw new Error('Token exchange failed')
}
const tokens = await tokenResponse.json()
// Store tokens securely
await this.storeTokens(tokens)
// Decode and validate JWT claims
const userInfo = await this.getUserInfo(tokens.access_token)
return {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
userInfo,
}
}
async refreshAccessToken(): Promise<string> {
const refreshToken = await this.secureStorage.get('refresh_token')
if (!refreshToken) {
throw new Error('No refresh token available')
}
const response = await fetch(`${this.zitadelConfig.baseURL}/oauth/v2/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: this.zitadelConfig.clientId,
}),
})
if (!response.ok) {
// Refresh token expired, need to re-authenticate
await this.logout()
throw new Error('Refresh token expired')
}
const tokens = await response.json()
await this.storeTokens(tokens)
return tokens.access_token
}
private async storeTokens(tokens: TokenResponse): Promise<void> {
// Platform-specific secure storage
if (this.isPlatform('web')) {
// Use secure HttpOnly cookies for web
await this.apiClient.post('/auth/store-tokens', { tokens })
} else if (this.isPlatform('mobile')) {
// Use Keychain/Keystore for mobile
await this.secureStorage.store('access_token', tokens.access_token)
await this.secureStorage.store('refresh_token', tokens.refresh_token)
}
}
}
```
### Admin Authentication Flow with MFA
```typescript
export class AdminAuthService extends AuthService {
async loginAdmin(): Promise<AuthResult> {
// Standard OAuth flow first
const authResult = await super.loginPlayer()
// Check if user has admin role
if (!authResult.userInfo.roles.includes('admin')) {
throw new Error('Insufficient permissions')
}
// Verify MFA is completed
if (!authResult.userInfo.mfaVerified) {
return this.redirectToMFA()
}
return authResult
}
private async redirectToMFA(): Promise<AuthResult> {
// Redirect to Zitadel MFA setup/verification
const mfaURL = new URL(`${this.zitadelConfig.baseURL}/ui/login/mfa/init`)
if (this.isPlatform('web')) {
window.location.href = mfaURL.toString()
}
// Handle MFA callback
return this.handleMFACallback()
}
private async handleMFACallback(): Promise<AuthResult> {
// After MFA completion, refresh token to get updated claims
const newAccessToken = await this.refreshAccessToken()
const userInfo = await this.getUserInfo(newAccessToken)
if (!userInfo.mfaVerified) {
throw new Error('MFA verification failed')
}
return {
accessToken: newAccessToken,
userInfo,
}
}
}
```
## Backend JWT Validation
### JWT Middleware Implementation
```go
// JWT validation middleware for API Gateway
package middleware
import (
"context"
"fmt"
"net/http"
"strings"
"time"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
)
type JWTMiddleware struct {
zitadelRepo auth.ZitadelRepository
config JWTConfig
}
type JWTConfig struct {
Issuer string
Audience string
RequiredClaims []string
AdminEndpoints []string
RateLimitByUserID bool
}
func NewJWTMiddleware(zitadelRepo auth.ZitadelRepository, config JWTConfig) *JWTMiddleware {
return &JWTMiddleware{
zitadelRepo: zitadelRepo,
config: config,
}
}
func (m *JWTMiddleware) ValidateToken() fiber.Handler {
return func(c *fiber.Ctx) error {
// Extract token from Authorization header
authHeader := c.Get("Authorization")
if authHeader == "" {
return c.Status(401).JSON(fiber.Map{"error": "Authorization header required"})
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
if tokenString == authHeader {
return c.Status(401).JSON(fiber.Map{"error": "Invalid authorization header format"})
}
// Validate token with Zitadel
ctx, cancel := context.WithTimeout(c.Context(), 5*time.Second)
defer cancel()
claims, err := m.zitadelRepo.ValidateToken(ctx, tokenString)
if err != nil {
return c.Status(401).JSON(fiber.Map{"error": "Invalid token", "details": err.Error()})
}
// Validate claims
if err := m.validateClaims(claims); err != nil {
return c.Status(401).JSON(fiber.Map{"error": "Invalid token claims", "details": err.Error()})
}
// Set user context
c.Locals("user_id", claims.Subject)
c.Locals("user_email", claims.Email)
c.Locals("user_name", claims.Name)
c.Locals("user_roles", claims.Roles)
c.Locals("mfa_verified", claims.MFAVerified)
// Check admin access for admin endpoints
if m.isAdminEndpoint(c.Path()) {
if !m.hasAdminRole(claims.Roles) {
return c.Status(403).JSON(fiber.Map{"error": "Admin access required"})
}
if !claims.MFAVerified {
return c.Status(403).JSON(fiber.Map{"error": "MFA verification required for admin access"})
}
}
return c.Next()
}
}
func (m *JWTMiddleware) validateClaims(claims *auth.AuthClaims) error {
// Validate issuer
if claims.Issuer != m.config.Issuer {
return fmt.Errorf("invalid issuer: expected %s, got %s", m.config.Issuer, claims.Issuer)
}
// Validate audience
validAudience := false
for _, aud := range claims.Audience {
if aud == m.config.Audience {
validAudience = true
break
}
}
if !validAudience {
return fmt.Errorf("invalid audience")
}
// Validate expiration
if time.Unix(claims.ExpiresAt, 0).Before(time.Now()) {
return fmt.Errorf("token expired")
}
// Validate required claims
for _, requiredClaim := range m.config.RequiredClaims {
switch requiredClaim {
case "email":
if claims.Email == "" {
return fmt.Errorf("email claim required")
}
case "name":
if claims.Name == "" {
return fmt.Errorf("name claim required")
}
case "roles":
if len(claims.Roles) == 0 {
return fmt.Errorf("roles claim required")
}
}
}
return nil
}
func (m *JWTMiddleware) isAdminEndpoint(path string) bool {
for _, adminPath := range m.config.AdminEndpoints {
if strings.HasPrefix(path, adminPath) {
return true
}
}
return false
}
func (m *JWTMiddleware) hasAdminRole(roles []string) bool {
for _, role := range roles {
if role == "admin" {
return true
}
}
return false
}
// Token refresh middleware
func (m *JWTMiddleware) RefreshTokenIfNeeded() fiber.Handler {
return func(c *fiber.Ctx) error {
userID := c.Locals("user_id")
if userID == nil {
return c.Next()
}
// Check if token is close to expiration (within 5 minutes)
tokenString := strings.TrimPrefix(c.Get("Authorization"), "Bearer ")
token, err := jwt.Parse(tokenString, nil)
if err != nil {
// Token parsing failed, but that's handled by ValidateToken middleware
return c.Next()
}
if claims, ok := token.Claims.(jwt.MapClaims); ok {
if exp, ok := claims["exp"].(float64); ok {
expirationTime := time.Unix(int64(exp), 0)
if time.Until(expirationTime) < 5*time.Minute {
// Signal frontend to refresh token
c.Set("X-Token-Refresh-Required", "true")
}
}
}
return c.Next()
}
}
```
## Monitoring and Metrics
### Zitadel Integration Metrics
```go
// Metrics for Zitadel integration monitoring
package metrics
import (
"context"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
zitadelRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "zitadel_requests_total",
Help: "Total number of requests to Zitadel",
},
[]string{"operation", "status"},
)
zitadelRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "zitadel_request_duration_seconds",
Help: "Duration of Zitadel requests",
Buckets: prometheus.DefBuckets,
},
[]string{"operation"},
)
authenticationAttempts = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "authentication_attempts_total",
Help: "Total number of authentication attempts",
},
[]string{"method", "result"},
)
circuitBreakerState = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "circuit_breaker_state",
Help: "Circuit breaker state (0=closed, 1=open, 2=half-open)",
},
[]string{"service"},
)
)
// Instrumented Zitadel repository
type InstrumentedZitadelRepository struct {
repo auth.ZitadelRepository
}
func NewInstrumentedZitadelRepository(repo auth.ZitadelRepository) auth.ZitadelRepository {
return &InstrumentedZitadelRepository{repo: repo}
}
func (r *InstrumentedZitadelRepository) ValidateToken(ctx context.Context, token string) (*auth.AuthClaims, error) {
start := time.Now()
claims, err := r.repo.ValidateToken(ctx, token)
duration := time.Since(start).Seconds()
status := "success"
if err != nil {
status = "error"
}
zitadelRequestsTotal.WithLabelValues("validate_token", status).Inc()
zitadelRequestDuration.WithLabelValues("validate_token").Observe(duration)
if err == nil {
authenticationAttempts.WithLabelValues("jwt", "success").Inc()
} else {
authenticationAttempts.WithLabelValues("jwt", "failure").Inc()
}
return claims, err
}
func (r *InstrumentedZitadelRepository) RefreshToken(ctx context.Context, refreshToken string) (*auth.TokenResponse, error) {
start := time.Now()
tokenResp, err := r.repo.RefreshToken(ctx, refreshToken)
duration := time.Since(start).Seconds()
status := "success"
if err != nil {
status = "error"
}
zitadelRequestsTotal.WithLabelValues("refresh_token", status).Inc()
zitadelRequestDuration.WithLabelValues("refresh_token").Observe(duration)
return tokenResp, err
}
```
This Zitadel integration architecture provides secure, scalable authentication with proper monitoring and resilience patterns for the Know Foolery quiz game.

@ -0,0 +1,248 @@
# Know Foolery - Project Requirements
## Overview
Know Foolery is a quiz game inspired by the French game "Déconnaissance" (https://escaleajeux.fr/jeu/decon.0.1). It's a web-based quiz platform with plans for cross-platform expansion to mobile and desktop applications.
## Game Concept
### Core Gameplay
- **Objective**: Players answer randomly selected questions to score points
- **Question Format**: Each question belongs to a theme, displayed above the question
- **Scoring System**:
- 2 points for correct answer without hint
- 1 point for correct answer with hint
- 0 points for incorrect answer or timeout
- **Attempts**: Players have exactly 3 attempts per question
- **Time Limit**: Each game session is limited to 30 minutes maximum
- **Hints**: Players can request one hint per question, but this reduces their score
### Player Experience
- **Registration**: Players must enter their name before starting a game
- **Session Management**: Game tracks progress, attempts, and time remaining
- **Leaderboard**: Display top 10 scores accessible to all players
- **Themes**: Questions are categorized by themes (Geography, History, Science, etc.)
## System Requirements
### Functional Requirements
#### Player Features
1. **Game Session Management**
- Start new game with player name registration
- Track current question, attempts remaining, and score
- Display timer with 30-minute session limit
- Automatic session termination on timeout
2. **Question Interaction**
- Display question with theme category
- Accept text-based answers
- Provide hint system (1 hint per question)
- Validate answers server-side to prevent cheating
3. **Scoring and Leaderboard**
- Real-time score calculation
- Display top 10 scores with player names
- Anonymous leaderboard (no personal data exposure)
- Score persistence across sessions
#### Administrative Features
1. **Question Management**
- CRUD operations for questions, themes, hints, and answers
- Secure admin panel with OAuth authentication
- Question categorization by themes
- Bulk import/export capabilities
2. **User Management**
- Admin role-based access control
- Multi-factor authentication for admin access
- Audit logging for all administrative actions
### Non-Functional Requirements
#### Performance
- **Response Time**: API responses under 200ms for 95% of requests
- **Concurrent Users**: Support for 1000+ simultaneous players
- **Database Performance**: Query response times under 100ms
- **Cross-Platform**: Consistent performance across web, mobile, and desktop
#### Security
- **Authentication**: OAuth 2.0/OIDC with Zitadel
- **Authorization**: Role-based access control (RBAC)
- **Data Protection**: Encryption at rest and in transit
- **Input Validation**: Comprehensive sanitization of all user inputs
- **Game Integrity**: Server-side validation to prevent cheating
#### Scalability
- **Microservices Architecture**: Independent scaling of components
- **Database**: PostgreSQL with read replicas for high availability
- **Caching**: Redis for session state and frequently accessed data
- **Auto-scaling**: Kubernetes-based horizontal scaling
#### Reliability
- **Uptime**: 99.9% availability target
- **Backup**: Automated daily backups with point-in-time recovery
- **Monitoring**: Comprehensive observability with alerting
- **Failover**: Automatic failover for critical components
## Technical Architecture
### Frontend Architecture
- **Web Application**: React with TypeScript
- **Mobile Applications**: React Native for iOS and Android
- **Desktop Applications**: Wails for macOS, Windows, and Linux
- **UI Framework**: Gluestack UI for consistent cross-platform design
- **Styling**: NativeWind/Tailwind CSS for utility-first styling
### Backend Architecture
- **Microservices**: Go-based services with clear domain boundaries
- **API Gateway**: Centralized routing, authentication, and rate limiting
- **Database ORM**: Ent for type-safe database operations
- **Communication**: gRPC for inter-service communication
- **External APIs**: Repository pattern for external integrations
### Microservices Design
#### Core Services
1. **Game Service**: Session management, scoring logic, attempts tracking
2. **Question Service**: CRUD operations for questions, themes, hints
3. **User Service**: Player registration and profile management
4. **Leaderboard Service**: Score calculations, rankings, statistics
5. **Session Service**: Timer management, session state persistence
6. **Admin Service**: Question management with OAuth protection
7. **Gateway Service**: API routing, authentication, rate limiting
#### External Services
1. **Zitadel**: Self-hosted OAuth 2.0/OIDC authentication
2. **PostgreSQL**: Primary database for application data
3. **Redis**: Session state and caching layer
### Database Design
#### Core Entities
- **Questions**: ID, theme, text, answer, hint, metadata
- **GameSessions**: ID, player_name, score, start_time, end_time, is_active
- **Users**: ID, name, created_at (minimal player data)
- **Leaderboard**: Aggregated scores and rankings
- **AuditLogs**: Admin actions and security events
#### Data Relationships
- Questions belong to themes
- GameSessions track multiple question attempts
- Leaderboard aggregates from GameSessions
- AuditLogs reference users and actions
## Security Requirements
### Authentication & Authorization
- **OAuth 2.0/OIDC**: Industry-standard authentication via Zitadel
- **JWT Tokens**: Secure token-based authentication with short expiration
- **MFA**: Multi-factor authentication required for admin accounts
- **Role-Based Access**: Granular permissions (player, admin roles)
### Data Security
- **Encryption**: AES-256 encryption for sensitive data at rest
- **TLS**: All communications encrypted in transit
- **Input Validation**: Comprehensive sanitization against XSS/injection
- **SQL Injection**: Prevention through parameterized queries (Ent)
### Application Security
- **Rate Limiting**: Per-user and global rate limits
- **CORS**: Strict cross-origin resource sharing policies
- **Security Headers**: CSP, HSTS, X-Frame-Options implementation
- **Audit Logging**: Comprehensive logging of security events
### Game Integrity
- **Server-Side Validation**: All game logic validated on backend
- **Anti-Cheating**: Session state verification and tampering detection
- **Score Validation**: Server-side score calculation and verification
## Compliance & Privacy
### Data Protection
- **GDPR Compliance**: Right to be forgotten, data portability
- **Data Minimization**: Collect only necessary player information
- **Anonymization**: Leaderboard data anonymized for privacy
- **Consent Management**: Clear privacy controls and consent
### Audit & Compliance
- **Audit Trails**: Comprehensive logging of all administrative actions
- **Compliance Reporting**: SOC 2, ISO 27001 compliance capabilities
- **Data Retention**: Defined policies for data lifecycle management
## Deployment & Infrastructure
### Development Environment
- **Local Development**: SQLite for rapid iteration
- **Docker Compose**: Containerized development environment
- **Testing**: Comprehensive unit, integration, and E2E testing
### Production Environment
- **Container Orchestration**: Kubernetes for production deployment
- **Database**: PostgreSQL with high availability configuration
- **Monitoring**: Prometheus, Grafana, Jaeger for observability
- **CI/CD**: GitHub Actions for automated testing and deployment
### Cross-Platform Deployment
- **Web**: Standard web application deployment
- **Mobile**: iOS App Store and Google Play Store distribution
- **Desktop**: Electron applications for major operating systems
## Quality Assurance
### Testing Strategy
- **Unit Testing**: Go testing framework + Jest for frontend
- **Integration Testing**: testcontainers for database testing
- **E2E Testing**: Playwright for complete user journeys
- **Performance Testing**: k6 for load and stress testing
- **Security Testing**: Automated security scanning and penetration testing
### Code Quality
- **Type Safety**: TypeScript for frontend, Go's type system for backend
- **Code Review**: Mandatory peer review for all changes
- **Static Analysis**: Automated code quality checks
- **Documentation**: Comprehensive API and component documentation
## Success Metrics
### User Engagement
- **Daily Active Users**: Track player engagement
- **Session Duration**: Average time spent playing
- **Question Accuracy**: Player success rates by theme
- **Hint Usage**: Frequency and effectiveness of hints
### Technical Performance
- **Response Times**: API and database performance metrics
- **Error Rates**: Application stability and reliability
- **Uptime**: System availability and reliability
- **Security Events**: Monitoring for security incidents
### Business Metrics
- **Player Retention**: User return rates and engagement
- **Content Quality**: Question difficulty and engagement
- **Platform Usage**: Cross-platform adoption rates
- **Admin Efficiency**: Content management effectiveness
## Future Roadmap
### Phase 1: Core Platform (Weeks 1-6)
- Basic web application with core game mechanics
- Essential microservices and database setup
- Admin panel for question management
### Phase 2: Enhanced Features (Weeks 7-12)
- Complete security implementation
- Comprehensive observability and monitoring
- Production-ready deployment
### Phase 3: Mobile Expansion (Weeks 13-18)
- React Native mobile applications
- Cross-platform component optimization
- Mobile app store deployment
### Phase 4: Advanced Features (Weeks 19-24)
- Desktop applications
- Advanced analytics and reporting
- Enhanced security and compliance features
This requirements document serves as the foundation for the Know Foolery quiz game development, ensuring all stakeholders understand the scope, technical approach, and quality standards for the project.
Loading…
Cancel
Save