A multiplayer web reimagination of the board game Khet
- Overview
- Architecture
- Services
- Prerequisites
- Getting Started
- Backups
- Game Modes
- Project Structure
- Testing
- Contributors
- License
Snell is a full-stack, microservice-based online game platform built around a reimagination of Khet, the laser board game. Players move and rotate pieces on a grid, fire laser beams, and try to eliminate the opponent's king.
The platform supports local play, multiplayer matchmaking, AI opponents, game review/replay, an in-game shop (with Stripe integration), achievements, chat, and an Android mobile client via Capacitor.
All services communicate through a central API Gateway over an internal Docker network. The frontend is served as static files and connects to the gateway via HTTPS.
┌─────────────────┐
│ API Gateway │ :8000 (HTTPS)
└────────┬────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │ │ │
┌────▼───┐ ┌─────▼──┐ ┌─────▼──┐ ┌─────▼──┐ ┌──────▼────┐
│ User │ │ Game │ │ Chat │ │ Shop │ │Achievement│
│ :8010 │ │ :8002 │ │ :8003 │ │ :8005 │ │ :8004 │
└────────┘ └────────┘ └────────┘ └────────┘ └───────────┘
│ │ │ │ │
└────────────┴───────────┴───────────┴─────────────┘
│
┌──────▼──────┐ ┌─────────┐
│ MongoDB │ │ AI │
└─────────────┘ └─────────┘
┌──────────────┐ ┌──────────────┐
│ Files (CDN) │ │ Mail │ Called by User only, never routed
└──────────────┘ │ :8006 │ through the gateway
Static frontend + └──────┬───────┘
Android (Capacitor) │
SMTP (Gmail)
| Service | Technology | Port | Description |
|---|---|---|---|
gateway |
Node.js / Express | 8000 |
API Gateway, auth middleware, TLS termination (off by default) |
game |
Node.js / Socket.IO | 8002 |
Game engine, matchmaking, AI, real-time state |
user |
Node.js / Express | 8010 |
Auth, profiles, friends, Stripe payments |
files |
Node.js / Express | 8001 |
Static frontend serving + Capacitor Android app |
chat |
Node.js / Socket.IO | 8003 |
Global, game, and friend chat |
achievement |
Node.js / Express | 8004 |
In-game achievement tracking |
shop |
Node.js / Express | 8005 |
Themes, emotes, profile pictures |
ai |
Node.js | — | Heuristic AI opponent engine |
mail |
Node.js / Nodemailer | 8006 |
Transactional emails (welcome, password reset) |
mongodb |
MongoDB | 27017 |
Shared NoSQL database |
backup |
mongo-tools / cron | — | Nightly AES-256 encrypted MongoDB backups (snell-backup) |
- Docker ≥ 24
- Docker Compose ≥ 2.20
- (For Stripe) A valid Stripe secret key
The stack runs from a single docker-compose.yml, shared between dev and prod.
What changes between environments is only the .env file loaded — see below.
git clone <repository-url>
cd ps8-26-snell/servicesCopy .env.example to .env and adjust the values:
ENV="dev" # "dev" or "prod"
STRIPE_SECRET_KEY="sk_test_..."
PUBLIC_URL="http://localhost:8000"
BACKUP_ENCRYPTION_KEY="..." # generated by ./backup.sh --genkey| Variable | Description |
|---|---|
ENV |
dev or prod. In prod, the gateway mounts and reads HTTPS certificates from ../../secrets/https. In dev, that volume is mounted but ignored. |
PUBLIC_URL |
The URL written into the frontend and used by clients (including emails sent by user). This URL runs in the visitor's browser — if it points at localhost on a real deployment, every API call goes to the visitor's own machine and nothing works. |
STRIPE_SECRET_KEY |
Stripe secret key, forwarded to the user service. |
BACKUP_ENCRYPTION_KEY |
AES-256 key for the nightly database backups — generate it with ./backup.sh --genkey. See Backups. |
./launch.shThis script:
- Reads
PUBLIC_URLfrom your.env(or from an already-exportedPUBLIC_URL, which takes priority — a warning is printed if so). - Regenerates
files/front/env.jsanduser/env.jswith that URL (these are plain JS files read directly by the browser, not part of any bundler build). - Exports
ENVandPUBLIC_URLso Docker Compose can substitute them intodocker-compose.yml. - Creates the external
proxyDocker network if it doesn't already exist. - Runs
docker compose up.
Flags:
| Flag | Effect |
|---|---|
| (none) | Prod mode, .env next to the script, no rebuild (docker compose up -d) |
--dev |
Dev mode |
--prod |
Prod mode (default, explicit) |
--build |
Rebuilds images (docker compose up --build), runs attached |
--env="path" |
Loads a custom .env file instead of the default one |
Examples:
./launch.sh # prod, no rebuild, detached
./launch.sh --build # prod, rebuild, attached (see logs)
./launch.sh --dev --build # dev, rebuild, attached
./launch.sh --dev --env=".env.dev" # dev, using a specific .env file
⚠️ Without--build, the stack starts detached (-d) from whatever images already exist locally. Use--buildafter any code change.
./stop-dockers.sh| Flags | Effect |
|---|---|
| (none) | docker compose down — stops and removes containers, keeps volumes (DB data preserved) |
--restart |
Restarts existing containers in place (docker compose restart) |
--restart --build |
docker compose down then docker compose up --build -d |
--reset |
docker compose down -v — removes containers AND volumes (database wiped), does not restart anything |
--reset --restart |
docker compose down -v then docker compose up -d |
--reset --restart --build |
docker compose down -v then docker compose up --build -d |
--reset on its own only tears everything down — nothing comes back up until
--restart is also passed. This lets you wipe the database without immediately
relaunching the stack.
The MongoDB database is backed up every night at 2 AM by the snell-backup
container: encrypted with AES-256, stored in backup/ at the repository root,
kept for a rolling 14 days.
Three scripts, all in services/:
./backup.sh --genkey # once: generate the AES-256 key, paste it into services/.env
./backup.sh # back up now
./backup.sh --list # list backups (name, size, date, integrity)
./test-backup.sh # test a backup without touching production
./restore-backup.sh # restore the database (asks for confirmation)The key lives in services/.env as BACKUP_ENCRYPTION_KEY.
⚠️ services/.envis not versioned. Keep a copy of that key somewhere safe: without it, no backup can ever be read again.
Full documentation: services/backup/README.md
| Mode | Description |
|---|---|
| Local | Two players on the same browser |
| Multiplayer | Real-time online matchmaking |
| vs AI | Play against the heuristic AI engine |
| Tutorial | Step-by-step introduction to the game rules |
| Review | Replay and analyze a past game move by move |
Snell is based on Khet — a laser chess variant. Each player controls a set of pieces on a grid:
- King — must be protected at all costs
- Shooter — deflects lasers in one direction
- Triangle — deflects the laser 90°
- Protector — absorbs lasers on one side
- Full Mirror — deflects on both sides
On your turn, move or rotate a piece, then fire your laser. If the laser hits an unprotected side of a piece, it is eliminated. The player who eliminates the opponent's king wins.
services/
├── gateway/ # API Gateway & auth middleware
├── game/ # Game engine, matchmaking, AI, tutorial
├── user/ # User accounts, friends, Stripe shop
├── files/ # Static frontend (HTML/CSS/JS) + Android
├── chat/ # Chat rooms (global, game, friend)
├── achievement/ # Achievement system
├── shop/ # In-game store (themes, emotes, avatars)
├── ai/ # Heuristic AI (snell_heuristique.js)
├── mail/ # Transactional emails (welcome, password reset)
├── helpers/ # Shared utilities (CORS, env, express helpers)
├── backup.sh # Create an encrypted backup (--list, --genkey)
├── test-backup.sh # Test a backup without touching production
├── restore-backup.sh # Restore the database from a backup
├── backup/ # Backup container: Dockerfile, cron, lib.sh, docs
├── docker-compose.yml # dev by default: `docker compose up`
├── docker-compose-dev.yml
├── docker-compose-prod.yml
└── compose-prod.sh
The frontend is a vanilla HTML/CSS/JS SPA organized by pages and components:
pages/— auth, home, game modes, profile, shopcomponents/— board renderer, chat, player info, notifications, social barassets/— themes (default, red&blue, bizot&deyann), sounds, emotes, piece sprites
service/— BoardService, LaserService, PieceService, GameService, AImodel/— Board, Game, Playermanager/— GameManager (real-time), MatchmakingManagerinitializer/— Board and player setup
Unit tests are available in the game service:
cd services/game
node src/test/TestRunner.jsTests cover:
- Triangle piece movement and reflections
- Action serialization / deserialization
- Board service logic
| Name | GitHub |
|---|---|
| Driss | @MkDriss |
| Cyril | @Musdevl |
This project is licensed under the terms of the LICENSE file included in this repository.