A modern, real-time multiplayer Pong game with tournament support, AI opponents, and comprehensive monitoring.
- Description
- Team Information
- Project Management
- Technical Stack
- API Documentation
- Features
- Module Points (21 Total)
- Database Schema
- Individual Contributions
- Installation & Setup
- Usage
- Architecture
ft_transcendence is a comprehensive web-based implementation of the classic Pong game, built as the final project of the 42 School common core curriculum. This project demonstrates full-stack development capabilities, real-time multiplayer gaming, microservices architecture, and modern DevOps practices.
The application features:
- Real-time multiplayer Pong gameplay using WebSockets
- AI opponent for single-player mode
- Tournament system with bracket generation
- User authentication with 2FA support
- Friend system and online status tracking
- Comprehensive monitoring with Prometheus and Grafana
- Microservices backend architecture
- Modern React frontend with TypeScript
Our team followed an Agile/Scrum methodology with clearly defined roles:
| Member | Role | Responsibilities |
|---|---|---|
| sudaniel | Product Owner | Vision, requirements prioritization, stakeholder communication, 2FA implementation, AI opponent, game logic |
| dhasan | Project Manager / Scrum Master | Sprint planning, standups, impediment removal, user management, remote players |
| dkremer | Technical Lead / Architect | Architecture decisions, code reviews, technical standards, WebSockets, monitoring system, microservices |
| ycheroua | Developer | Game implementation, browser compatibility, frontend features |
| All Members | Developers | Frontend & backend framework implementation, collaborative development |
We adopted Agile/Scrum with 1-2 week(s) sprints:
- Daily Standups: Quick sync meetings every few days
- Sprint Planning: Clear task breakdown with time estimates
- Code Reviews: Mandatory peer review before merging
- Pair Programming: Complex features tackled collaboratively
- Retrospectives: Quick feedback sessions after each sprint
- GitHub: Version control, issues, and project boards
- Discord/Slack: Real-time team communication
- Issues created with clear acceptance criteria
- Branches created from
mainfor each feature - Pull requests with detailed descriptions
- Code review by at least one team member
- Merge after approval and passing checks
| Technology | Version | Justification |
|---|---|---|
| React | 18+ | Modern component-based UI library with excellent ecosystem and TypeScript support |
| TypeScript | 5+ | Type safety, better developer experience, reduced runtime errors |
| Vite | 5+ | Lightning-fast HMR, optimized builds, better developer experience than CRA |
| Tailwind CSS | v4 | Utility-first CSS for rapid UI development with consistent design |
| Socket.IO Client | 4+ | Real-time bidirectional communication for game state synchronization |
| React Router | 6+ | Client-side routing for SPA navigation |
| Technology | Version | Justification |
|---|---|---|
| Fastify | 4+ | High-performance Node.js framework, faster than Express, excellent TypeScript support |
| TypeScript | 5+ | Type safety across the entire backend, shared types between services |
| better-sqlite3 | 11+ | Fast, embedded database, perfect for development and moderate loads, ACID compliance |
| Socket.IO | 4+ | WebSocket implementation with fallback support and room management |
| TypeBox | Latest | JSON schema validation with TypeScript inference |
| JWT | - | Stateless authentication, secure token-based auth between microservices |
| Technology | Version | Justification |
|---|---|---|
| Docker | 20+ | Containerization for consistent environments across dev/prod |
| Docker Compose | 2+ | Multi-container orchestration, simplified local development |
| Caddy | 2+ | Modern reverse proxy with automatic HTTPS via Let's Encrypt |
| Prometheus | Latest | Time-series database for collecting metrics from all services every 15 seconds |
| Grafana | Latest | Visualization dashboard for Prometheus metrics with pre-configured alerts |
| prom-client | 15+ | Node.js library for exposing Prometheus metrics from services |
We chose a microservices architecture to:
- Separate concerns: Auth, User, and Pong services handle distinct domains
- Independent scaling: Services can scale based on load patterns
- Fault isolation: Failures in one service don't crash the entire system
- Technology flexibility: Each service can be optimized independently
- Team productivity: Parallel development without conflicts
All microservices expose auto-generated API documentation using OpenAPI 3.0 specification with interactive Swagger UI.
| Component | Package | Purpose |
|---|---|---|
| TypeBox | @sinclair/typebox |
JSON schema definitions with TypeScript type inference |
| Swagger | @fastify/swagger |
OpenAPI 3.0 specification generation |
| Swagger UI | @fastify/swagger-ui |
Interactive API explorer |
TypeBox provides compile-time TypeScript types and runtime JSON schema validation:
import { Type, Static } from '@sinclair/typebox'
const UserSchema = Type.Object({
id: Type.Number(),
email: Type.String({ format: 'email' }),
display_name: Type.String({ minLength: 1 })
})
type User = Static<typeof UserSchema> // TypeScript type inferenceFastify integration via TypeBoxTypeProvider ensures request/response validation matches TypeScript types.
All protected endpoints include JWT Bearer authentication configured in the OpenAPI security schemes. The Swagger UI allows you to authenticate and test protected endpoints directly.
| Service | Swagger UI URL | Description |
|---|---|---|
| Auth | http://localhost/api/auth/docs |
Authentication, registration, 2FA |
| User | http://localhost/api/user/docs |
Profiles, friends, stats |
| Pong | http://localhost/api/pong/docs |
Game logic, matches, tournaments |
- Real-time Multiplayer Pong: Smooth 60 FPS gameplay with WebSocket synchronization
- AI Opponent: Single-player mode with adjustable difficulty
- Responsive Controls: Keyboard controls with smooth paddle movement
- Game Statistics: Track wins, losses, and match history
- Registration & Login: Secure authentication with JWT tokens
- Two-Factor Authentication (2FA): TOTP-based 2FA for enhanced security
- User Profiles: Customizable display names, avatars, and bios
- Online Status: Real-time online/offline status tracking
- Friend System: Send/accept/decline/remove friend requests, view friends list
- Bracket Generation: Automatic single-elimination tournament brackets
- Public Tournaments: Host open tournaments
- Real-time Updates: Live bracket updates as matches complete
- Tournament History: View past tournament results and standings
- Prometheus Metrics: HTTP requests, WebSocket connections, database queries
- Grafana Dashboards: Pre-configured dashboards for system health
- Service Health Checks: Monitor microservice availability
- Performance Tracking: Response times, error rates, throughput
Implementation: Full Pong game playable in the browser
- Canvas-based rendering with smooth animations
- Physics engine for ball movement and collision detection
- Score tracking and win conditions
- Contributors: sudaniel, ycheroua
Implementation: Real-time multiplayer via WebSockets
- Socket.IO integration for bidirectional communication
- Game state synchronization between clients
- Room management for matchmaking
- Latency compensation techniques
- Contributors: dhasan
Implementation: Comprehensive WebSocket integration
- Pong game synchronization
- Online status updates
- Friend request notifications
- Tournament bracket updates
- Contributors: dkremer, dhasan, sudaniel
Implementation: Complete authentication and user system
- Registration with email and password
- Secure login with JWT tokens
- Password hashing with bcrypt
- Session management
- Profile customization (avatar, bio, display name)
- Contributors: dhasan, dkremer, ycheroua
Implementation: Full observability stack
- Prometheus metrics collection from all services
- Custom metrics for game-specific events
- Grafana dashboards for visualization
- Service health monitoring
- Performance and error tracking
- Contributors: dkremer
Implementation: Modern React SPA with TypeScript
- Component-based architecture
- React Hooks for state management
- React Router for navigation
- Type-safe props and state
- Contributors: ycheroua
Implementation: High-performance API server
- RESTful API endpoints
- Request validation with TypeBox
- Authentication middleware
- Error handling
- Contributors: All team members
Implementation: Complete tournament management
- Create tournaments
- Automatic bracket generation
- Single-elimination logic
- Real-time match progression
- Tournament history and results
- Contributors: dkremer, sudaniel, dhasan
Implementation: Comprehensive stats tracking
- Win/loss records per user
- Tournament participation history
- Leaderboards
- Contributors: dkremer, sudaniel, ycheroua
Implementation: TOTP-based 2FA
- QR code generation for authenticator apps
- Time-based one-time password verification
- Optional 2FA during registration
- Secure secret storage
- Contributors: sudaniel, dkremer
Implementation: Three separate microservices
- Auth Service: Authentication, registration, 2FA
- User Service: Profiles, friends, stats
- Pong Service: Game logic, matches, tournaments
- Inter-service communication with JWT
- Independent databases per service
- Contributors: sudaniel, dkremer
Implementation: Single-player AI bot
- Difficulty levels (easy, medium, hard)
- Predictive ball tracking
- Realistic paddle movement
- Contributors: sudaniel
Implementation: Cross-browser support
- Tested on Chrome, Firefox, Brave, Edge
- Responsive design for various screen sizes
- Polyfills for older browsers
- Contributors: ycheroua
Implementation: Auto-generated API documentation with interactive testing
- TypeBox schemas (
@sinclair/typebox) for JSON schema validation with TypeScript inference @fastify/swaggerfor OpenAPI 3.0 specification generation@fastify/swagger-uifor interactive API explorer at/docs- JWT Bearer authentication configured in security schemes
- Available for all microservices (Auth, User, Pong)
- Contributors: dkremer
We use SQLite databases with a microservices approachβeach service has its own database for data isolation and independence.
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
display_name TEXT NOT NULL,
twofa_enabled INTEGER DEFAULT 0,
twofa_secret TEXT,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);Purpose: Stores authentication credentials and 2FA secrets. 5. Play your bracket matches when scheduled
CREATE TABLE users (
id INTEGER PRIMARY KEY, -- Synchronized from Auth service
email TEXT UNIQUE NOT NULL,
display_name TEXT NOT NULL,
avatar_url TEXT,
bio TEXT,
wins INTEGER DEFAULT 0,
losses INTEGER DEFAULT 0,
online INTEGER DEFAULT 0,
last_seen TEXT DEFAULT CURRENT_TIMESTAMP,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE match_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
winner_id INTEGER NOT NULL,
loser_id INTEGER NOT NULL,
left_score INTEGER NOT NULL,
right_score INTEGER NOT NULL,
played_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (winner_id) REFERENCES users(id),
FOREIGN KEY (loser_id) REFERENCES users(id)
);
CREATE TABLE friendships (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL, --sender
friend_id INTEGER NOT NULL, --recipient
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','accepted')),
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (friend_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE(user_id, friend_id),
CHECK(user_id != friend_id)
);
CREATE TRIGGER IF NOT EXISTS last_seen_update
AFTER UPDATE OF online ON users
FOR EACH ROW
WHEN NEW.online = 0 AND OLD.online = 1
BEGIN
UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;Purpose: User profiles, stats, match history, and friend relationships.
CREATE TABLE matches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
winner_id INTEGER NOT NULL,
loser_id INTEGER NOT NULL,
left_score INTEGER NOT NULL,
right_score INTEGER NOT NULL,
duration INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_matches_winner ON matches(winner_id);
CREATE INDEX IF NOT EXISTS idx_matches_loser ON matches(loser_id);
CREATE INDEX IF NOT EXISTS idx_matches_created ON matches(created_at);
CREATE TABLE tournaments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
created_by INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
max_players INTEGER NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
started_at TEXT,
finished_at TEXT,
winner_id INTEGER
);
CREATE INDEX IF NOT EXISTS idx_tournament_status ON tournaments(status);
CREATE INDEX IF NOT EXISTS idx_tournament_created_by ON tournaments(created_by);
CREATE TABLE tournament_players (
tournament_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
seed INTEGER,
joined_at TEXT DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (tournament_id, user_id),
FOREIGN KEY (tournament_id) REFERENCES tournaments(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_tournament_players_tournament ON tournament_players(tournament_id);
CREATE INDEX IF NOT EXISTS idx_tournament_players_user ON tournament_players(user_id);
CREATE TABLE tournament_matches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tournament_id INTEGER NOT NULL,
round INTEGER NOT NULL,
match_index INTEGER NOT NULL,
left_player_id INTEGER,
right_player_id INTEGER,
winner_id INTEGER,
left_score INTEGER,
right_score INTEGER,
pong_match_id TEXT,
status TEXT NOT NULL DEFAULT 'pending',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
started_at TEXT,
finished_at TEXT,
FOREIGN KEY (tournament_id) REFERENCES tournaments(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_tournament_matches_tournament ON tournament_matches(tournament_id);
CREATE INDEX IF NOT EXISTS idx_tournament_matches_status ON tournament_matches(status);
CREATE INDEX IF NOT EXISTS idx_tournament_matches_pong_match ON tournament_matches(pong_match_id);
CREATE INDEX IF NOT EXISTS idx_tournament_matches_lookup ON tournament_matches(tournament_id, round, match_index);Purpose: Pong matches, tournament brackets, and game statistics.
- SQLite: Chosen for simplicity, ACID compliance, and zero configuration
- Microservice Isolation: Each service owns its dataβno shared database
- WAL Mode: Enabled for better concurrent read/write performance
- Indexes: Created on frequently queried columns for performance
- Foreign Keys: Enforced for referential integrity
Total Contribution:
- 2FA Implementation: Designed and implemented TOTP-based two-factor authentication with QR code generation
- AI Opponent: Created single-player AI with difficulty levels and predictive algorithms
- Game Logic: Core Pong game mechanics, physics, collision detection
- Tournament System: Bracket logic and match progression (with dkremer, dhasan)
- Microservices Architecture: Backend service design and inter-service communication (with dkremer)
- Product Vision: Defined feature priorities and user stories
- Code Reviews: Reviewed and approved pull requests
Total Contribution:
- Project Management: Sprint planning, daily standups, task tracking via GitHub issues
- User Management System: Registration, login, JWT authentication (with dkremer)
- Remote Players: WebSocket integration for multiplayer Pong
- Friend System: Friend requests, acceptance, and status management
- Tournament Features: Tournament joining, player management (with dkremer, sudaniel)
- Team Coordination: Resolved blockers, facilitated communication
- Testing: Manual and integration testing across features
Total Contribution:
- Architecture Design: Microservices structure, service boundaries, API contracts
- WebSocket Infrastructure: Socket.IO plugin, room management, event handling (with dhasan, sudaniel)
- Monitoring System: Full Prometheus and Grafana setup with custom metrics
- User Management: Authentication middleware, password hashing, session handling (with dhasan)
- Tournament System: Bracket generation algorithm, seeding logic (with sudaniel, dhasan)
- Microservices Backend: Service-to-service authentication, shared plugins (with sudaniel)
- Code Reviews: Technical review of all PRs, enforced coding standards
- DevOps: Docker configuration, Caddy reverse proxy, production deployment
Total Contribution:
- Game UI: Pong game canvas rendering and animations (with sudaniel)
- Browser Compatibility: Cross-browser testing and fixes for Chrome, Firefox, Safari, Edge
- Frontend Features: UI components, styling with Tailwind CSS
- Responsive Design: Mobile-friendly layouts (if applicable)
- Bug Fixes: Frontend bug identification and resolution
Total Contribution:
- Framework Setup: React frontend and Fastify backend scaffolding
- Code Reviews: Peer review of all pull requests
- Documentation: Inline code comments, API documentation
- Testing: Manual testing, bug reporting
- Meetings: Daily standups, sprint planning, retrospectives
Ensure you have the following installed:
- Docker: v20.0+ (Install Docker)
- Docker Compose: v2.0+ (comes with Docker Desktop)
- Make (optional, for convenience commands)
git clone https://github.com/ygalsk/ft_transcendence.git
cd ft_transcendenceCopy the example environment file and configure it:
cp .env.example .envEdit .env with your preferred values:
# JWT Secrets (CHANGE THESE IN PRODUCTION)
JWT_SECRET=your-super-secret-jwt-key-change-in-production
SERVICE_JWT_SECRET=your-service-secret-key-change-in-production
SERVICE_SECRET=your-internal-service-secret
# Database Paths
AUTH_DB_PATH=/usr/src/app/data/auth.sqlite
USER_DB_PATH=/usr/src/app/data/user.sqlite
PONG_DB_PATH=/usr/src/app/data/pong.sqlite
# Grafana Admin Credentials
GF_SECURITY_ADMIN_USER=admin
GF_SECURITY_ADMIN_PASSWORD=adminUsing Make (recommended):
make upOr using Docker Compose directly:
docker-compose up --buildThis will:
- Build all Docker images (frontend, backend services, Caddy)
- Start all containers (auth, user, pong, prometheus, grafana, caddy)
- Initialize databases with schemas
- Start the frontend dev server
- Frontend: http://localhost
- Production: https://transcendence.keystone-gateway.dev
- Grafana Dashboard: http://localhost/dashboard (credentials: admin/admin - change in .env)
- Prometheus (direct): Not exposed externally, access via Grafana or
docker-compose exec prometheus curl localhost:9090
- Navigate to http://localhost
- Click "Register"
- Fill in email, display name, and password
- (Optional) Enable 2FA for enhanced security
- Start playing!
- Log in to your account or play as guest
- Click "Play Now"
- Wait for matchmaking or invite a friend
- Use W/S or Arrow Keys to move your paddle
- First to 11 points wins!
- Select "Play vs AI"
- Choose difficulty (Easy / Medium / Hard)
- Play against the AI bot
- Navigate to "Tournaments"
- Click "Create Tournament"
- Set tournament name and max players
- Start tournament once minimum players join
- Click your avatar in the top-right
- Select "Profile"
- Update display name, bio, or avatar
- View your stats (wins, losses, match history)
- Enable/disable 2FA in settings
- Search for users by display name
- Click "Add Friend"
- Wait for them to accept your request
- See their online status in your friends list
graph TB
Internet["π Internet<br/>HTTPS 443"]
Internet --> Caddy["<b>Caddy</b><br/>Reverse Proxy & Static Server<br/>Port: 80/443"]
Caddy -->|"/"| Frontend["Frontend<br/>Static Files<br/>(Built React SPA)"]
Caddy -->|"/api/auth/*"| AuthSvc["<b>Auth Service</b><br/>Port: 4000<br/>(Internal)"]
Caddy -->|"/api/user/*"| UserSvc["<b>User Service</b><br/>Port: 5000<br/>(Internal)"]
Caddy -->|"/api/pong/*<br/>/socket.io/*"| PongSvc["<b>Pong Service</b><br/>Port: 6061<br/>(Internal)"]
Caddy -->|"/dashboard/*"| Grafana["<b>Grafana</b><br/>Port: 3000<br/>(Internal)"]
AuthSvc --> AuthDB["SQLite<br/>auth.sqlite"]
UserSvc --> UserDB["SQLite<br/>user.sqlite"]
PongSvc --> PongDB["SQLite<br/>pong.sqlite"]
UserSvc -.->|"HTTP + JWT"| AuthSvc
PongSvc -.->|"HTTP + JWT"| UserSvc
AuthSvc -->|"/metrics"| Prometheus["<b>Prometheus</b><br/>Port: 9090<br/>(Internal)<br/>Scrapes every 15s"]
UserSvc -->|"/metrics"| Prometheus
PongSvc -->|"/metrics"| Prometheus
Prometheus --> Grafana
style Caddy fill:#4CAF50,color:#fff
style Frontend fill:#61dafb,color:#000
style AuthSvc fill:#FF9800,color:#fff
style UserSvc fill:#FF9800,color:#fff
style PongSvc fill:#FF9800,color:#fff
style Prometheus fill:#E34234,color:#fff
style Grafana fill:#F05A28,color:#fff
style Internet fill:#2196F3,color:#fff
Responsibilities:
- User registration
- Login and JWT token generation
- Password hashing and verification
- 2FA setup and verification
- Internal service authentication
Tech: Fastify, TypeScript, SQLite, JWT, TOTP
Responsibilities:
- User profile management
- Avatar uploads
- Friend system (add, accept, list)
- Online status tracking
- Match history
- Public user data API
Tech: Fastify, TypeScript, SQLite, Multipart file upload
Responsibilities:
- Real-time Pong game logic via WebSockets
- Match creation and state management
- Tournament creation and bracket generation
- Game statistics and leaderboards
- AI opponent
Tech: Fastify, TypeScript, Socket.IO, SQLite
- Frontend β Backend: REST APIs (JSON over HTTP) + WebSockets (Socket.IO)
- Service β Service: Internal HTTP APIs with JWT authentication
- Database: Each service owns its SQLite database (no shared DB)
- Metrics Collection: All services expose
/metricsendpoint for Prometheus scraping- Scrape Interval: Every 15 seconds
- Metrics Path:
GET /metricson each service - Services Monitored: Auth Service (:4000), User Service (:5000), Pong Service (:6061)
- Purpose: Time-series metrics database for the system
- Scrape Targets: Pulls metrics from all three microservices every 15 seconds
- Configuration: IaC/prometheus.yml defines jobs for each service
- Alert Rules: IaC/prometheus/alert_rules.yml defines alerting conditions
- Data Retention: Default 15 days of historical metrics
- Access: Internal only (accessible via Grafana or direct API)
- Purpose: Visualization and dashboarding for Prometheus metrics
- Dashboard: Pre-configured dashboard at IaC/grafana/provisioning/dashboards/transcendence.json
- Data Source: Automatically configured to scrape from Prometheus
- Access URL:
https://transcendence.keystone-gateway.dev/dashboard/(production) orhttp://localhost/dashboard/(local) - Default Credentials: Admin user configured via environment variables
- Alerts: Visual notifications based on Prometheus alert rules
Each service exposes standard metrics via prom-client:
- HTTP Metrics: Request count, latency, status codes
- WebSocket Metrics (Pong Service): Active connections, message throughput
- Database Metrics: Query duration, connection pool status
- System Metrics: Memory usage, CPU, Node.js event loop lag
- 42 Subject PDF: en.subject.pdf - Official project requirements
- Socket.IO Documentation: https://socket.io/docs/
- Fastify Documentation: https://www.fastify.io/docs/
- React Documentation: https://react.dev/
- Prometheus Documentation: https://prometheus.io/docs/
- Grafana Tutorials: https://grafana.com/tutorials/
Tested on:
- β Chrome
- β Firefox
make clean # or docker-compose down -v
make up # or docker-compose up --buildCheck if ports 80, 443, or 5173 are occupied:
# Linux/Mac
lsof -i :80
lsof -i :443
# Windows
netstat -ano | findstr :80Stop conflicting services or change ports in docker-compose.yaml.
SQLite databases use WAL mode for concurrency. If you encounter "database is locked":
- Ensure
busy_timeoutis set in DB plugin (already configured) - Restart affected service:
docker-compose restart <service-name>
- Ensure Caddy is running and proxying
/api/pongcorrectly - Check browser console for Socket.IO connection errors
- Verify
CORSsettings in pong service allow your frontend origin
This project is part of the 42 School curriculum and is intended for educational purposes.
Built with by dkremer, sudaniel, dhasan, ycheroua
Last updated: January 2026