MineSeeker
A real-time 1v1 multiplayer minesweeper game played in the browser. Two players race on the same hidden minefield — uncover safe cells to score points, but hit a mine and you hand the advantage to your opponent. Games are live and synchronised instantly via a Mercure hub; no page reloads, no polling.
Created by SplendidBear.
Features
- Real-time 1v1 gameplay — moves broadcast instantly over Mercure (server-sent events)
- Guest & registered play — jump in anonymously or create an account for stats and history
- Full authentication stack — email/password, passkeys (WebAuthn), TOTP 2FA with backup codes
- Player profiles — win/loss/draw stats, per-month charts, recent battle history with shareable replay links
- Profile pictures — uploaded to MinIO object storage, thumbnails generated on-the-fly by LiipImagine
- Battle replay sharing — share a direct link to any finished game
- Docker-ready — single
make start-buildbrings up the full production-like stack
Tech stack
| Layer | Technology |
|---|---|
| Backend | PHP 8.5, Symfony 7.4, Doctrine ORM |
| Frontend | React 19, Vite, MUI, SCSS |
| Database | PostgreSQL 18 |
| Real-time | Mercure (built into FrankenPHP / Caddy) |
| File storage | MinIO (S3-compatible) |
| Image processing | LiipImagine + Flysystem |
| Server | FrankenPHP (Caddy + PHP in one binary) |
| Auth | Symfony Security, Scheb 2FA, web-auth/webauthn-framework |
Requirements
Bare-metal development
- PHP >= 8.5 with extensions:
pdo_pgsql,gd,intl,zip,sodium - Composer 2
- Node.js 22 + Bun
- PostgreSQL 18
- Caddy with FrankenPHP and the Mercure module
- MailHog (or any SMTP server on port 1025)
- MinIO (listening on port 9000)
Docker
- Docker Engine 24+
- Docker Compose v2
- Bun (for building frontend assets before
make start-build) - Composer (for
make start-build)
Installation
1. Clone the repository
git clone https://github.com/splendidbear/mineseeker.git
cd mineseeker
2. Configure environment
cp .env.dist .env
Edit .env and fill in every value. Key ones:
| Variable | What to set |
|---|---|
APP_SECRET |
Random 32-byte hex: openssl rand -hex 32 |
POSTGRES_USER/PASSWORD/DB |
Your PostgreSQL credentials |
MINIO_ROOT_USER/PASSWORD |
MinIO admin credentials |
MINIO_ENDPOINT |
http://localhost:9000 (bare-metal) |
MINIO_PUBLIC_URL |
Public URL browsers use to reach MinIO |
RECAPTCHA_SITE_KEY/SECRET_KEY |
Google reCAPTCHA v3 keys for your domain |
MERCURE_JWT_SECRET |
Random secret (generated in step 3) |
MERCURE_JWT_TOKEN |
Signed publisher JWT (generated in step 3) |
MERCURE_SUBSCRIBER_JWT |
Signed subscriber JWT (generated in step 3) |
MAILER_DSN |
smtp://localhost:1025 for MailHog in dev |
3. Generate Mercure JWT tokens
composer install
make mercure-jwt
Copy the printed values into .env and into the publisher_jwt / subscriber_jwt lines of your Caddy Mercure block, then reload Caddy:
sudo systemctl reload caddy
4a. Run with Docker
make start-build
This installs PHP dependencies, builds the frontend assets with Bun, builds the Docker image, and starts all services (app, db, mail, minio, minio_init).
The app is available at http://localhost:10080 (or the domain set in APP_PUBLIC_HOSTNAME).
To apply any code changes later, run the same command again.
4b. Run bare-metal (development)
composer install
bun install
bun run dev # Vite dev server with hot-reload
php bin/console doctrine:migrations:migrate --no-interaction
Start MinIO and MailHog, then open the URL configured in your Caddy vhost.
5. MinIO bucket setup
Docker — handled automatically on first start by the minio_init service. No action needed.
Bare-metal — run once after MinIO is up:
mc alias set local http://localhost:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD
mc mb local/mineseeker
echo '' | mc pipe local/mineseeker/media/.keep
echo '' | mc pipe local/mineseeker/cache/.keep
# Apply public-read policy for media/ and cache/ — see docker/minio-init.sh for the JSON
License
LGPL-3.0 — see LICENSE for details.
© 2026 SplendidBear