2026-04-13 16:10:41 +02:00
# MineSeeker
2019-10-26 16:52:25 +02:00
2026-04-13 16:10:41 +02:00
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.
2019-10-26 16:52:25 +02:00
2026-04-13 16:10:41 +02:00
Created by [SplendidBear ](https://www.splendidbear.org ).
2019-10-26 16:52:25 +02:00
2026-04-13 16:10:41 +02:00
---
2016-09-22 13:56:57 +02:00
2026-04-13 16:10:41 +02:00
## Features
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
- **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-build` brings up the full production-like stack
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
---
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
## Tech stack
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
| 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 |
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
---
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
## Requirements
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
### 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)
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
### Docker
- Docker Engine 24+
- Docker Compose v2
- Bun (for building frontend assets before `make start-build` )
- Composer (for `make start-build` )
2016-10-01 21:49:15 +02:00
2026-04-13 16:10:41 +02:00
---
2016-11-30 20:48:17 +01:00
2026-04-13 16:10:41 +02:00
## Installation
2016-11-30 20:48:17 +01:00
2026-04-13 16:10:41 +02:00
### 1. Clone the repository
2016-10-25 11:19:50 +02:00
2026-04-13 16:10:41 +02:00
```bash
git clone https://github.com/splendidbear/mineseeker.git
cd mineseeker
```
2016-11-30 20:48:17 +01:00
2026-04-13 16:10:41 +02:00
### 2. Configure environment
```bash
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
```bash
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:
```bash
sudo systemctl reload caddy
```
### 4a. Run with Docker
```bash
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)
```bash
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:
```bash
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
```
---
2026-04-14 10:37:02 +02:00
## Development environment
When running the Docker stack locally you typically want to catch outgoing emails instead of relaying them through a real SMTP server.
The production `compose.yaml` uses Postfix for actual mail delivery and must not be edited for local overrides.
Use a `compose.override.yaml` file instead — Docker Compose merges it automatically on top of the base file whenever both are present.
### Email: replace Postfix with MailHog
Create `compose.override.yaml` in the project root (it is git-ignored and never reaches production):
```yaml
services:
app:
environment:
MAILER_DSN: smtp://mail:1025?verify_peer=0
2026-04-15 14:33:53 +02:00
TRUSTED_PROXIES: "0.0.0.0/0"
2026-04-14 10:37:02 +02:00
mail:
image: mailhog/mailhog:latest
ports:
2026-04-14 12:55:47 +02:00
- "1025:1025"
2026-04-14 10:37:02 +02:00
- "8025:8025"
```
This replaces the `mail` service image with MailHog and points the application's mailer at its SMTP port (`1025` ).
No other files need to change.
After adding the file, restart the stack:
```bash
make start-build
```
All emails sent by the application are now captured by MailHog.
Open the web UI at **http://localhost:8025 ** to inspect them.
> **Production note** — `compose.override.yaml` is listed in `.gitignore`.
> Never commit it; the production server must only see `compose.yaml` with Postfix.
---
2026-04-14 12:55:47 +02:00
## Deploying to production
Releases are automated via Gitea Actions. Pushing a tag that starts with `v` (e.g. `v2026.01` ) triggers the workflow at `.gitea/workflows/deploy.yml` .
The job runs on a **self-hosted runner ** installed on the production server — the server only needs an outbound connection to Gitea, no open SSH port required.
The `app` image is rebuilt with the new code; the database and storage containers are untouched so all data is preserved.
### Gitea repository variables and secrets
**Variable** (plaintext, editable — **Repository → Settings → Variables ** ):
| Variable | Value |
|---|---|
| `PROD_APP_DIR` | Absolute path on the server (e.g. `/var/www/mineseeker` ) |
**Secret** (encrypted, write-only — **Repository → Settings → Secrets ** ):
| Secret | Value |
|---|---|
| `PROD_ENV_FILE` | Full content of the production `.env` file (see below) |
The workflow writes `PROD_ENV_FILE` to `.env` on every deploy, so you never need to manage the file on the server manually. To update a credential, overwrite the secret in Gitea and push a new tag.
#### `PROD_ENV_FILE` contents
Paste the filled-in `.env` file as the secret value:
```dotenv
2026-04-14 15:54:00 +02:00
APP_NAME=mineseeker
2026-04-14 12:55:47 +02:00
APP_ENV=prod
APP_SECRET="<openssl rand -hex 32>"
DATABASE_URL=postgresql://POSTGRES_USER:POSTGRES_PASSWORD@db:5432/POSTGRES_DB ?serverVersion=18&charset=utf8
POSTGRES_USER=mineseeker
POSTGRES_PASSWORD="<strong password>"
POSTGRES_DB=mineseeker
POSTGRES_VERSION=18
MINIO_ROOT_USER=mineseeker
MINIO_ROOT_PASSWORD="<strong password>"
MINIO_ENDPOINT=http://minio:9000
2026-04-14 16:47:15 +02:00
MINIO_PUBLIC_URL=https://aws.mineseeker.hu
2026-04-14 12:55:47 +02:00
MAILER_DSN=smtp://mail:25?verify_peer=0
MAIL_DOMAIN=mineseeker.hu
RECAPTCHA_SITE_KEY="<your reCAPTCHA v3 site key>"
RECAPTCHA_SECRET_KEY="<your reCAPTCHA v3 secret key>"
MERCURE_URL=https://mineseeker.hu/.well-known/mercure
MERCURE_PUBLIC_URL=https://mineseeker.hu/.well-known/mercure
MERCURE_JWT_SECRET="<generated by make mercure-jwt>"
MERCURE_JWT_TOKEN="<generated by make mercure-jwt>"
MERCURE_SUBSCRIBER_JWT="<generated by make mercure-jwt>"
APP_PUBLIC_HOSTNAME=mineseeker.hu
WEBAUTHN_RP_ID=mineseeker.hu
WEBAUTHN_ORIGIN=https://mineseeker.hu
2026-04-15 14:33:53 +02:00
# OG Tags & Social Media Sharing (IMPORTANT for Docker deployments)
# TRUSTED_PROXIES: IP address (or range) of your reverse proxy (Caddy/Nginx)
# This ensures OG image tags use HTTPS URLs instead of HTTP
TRUSTED_PROXIES="172.18.0.0/16"
TRUSTED_HOSTS="mineseeker.hu,www.mineseeker.hu"
```
2026-04-14 12:55:47 +02:00
### Production server: one-time setup
The server needs Docker, Git, and a self-hosted `act_runner` registered against the Gitea repository. Bun and Composer run inside the multi-stage Dockerfile, so they are not needed on the server.
#### 1. Clone the repository
```bash
git clone https://gitea.mineseeker.hu/youruser/mineseeker.git /var/www/mineseeker
```
#### 2. Generate Mercure JWT tokens (run once locally)
```bash
composer install # only needed for this step
make mercure-jwt
```
Copy the three printed values into the `PROD_ENV_FILE` secret.
2026-04-15 14:33:53 +02:00
#### 3. First deploy
2026-04-14 12:55:47 +02:00
Trigger it by pushing the first tag:
```bash
git tag v2026.01
git push origin v2026.01
```
This writes `.env` , builds the Docker image, starts all services, runs migrations, and initialises the MinIO buckets automatically via `minio_init` .
2026-04-15 14:33:53 +02:00
#### 4. Verify
2026-04-14 12:55:47 +02:00
```bash
docker compose ps # all services should be healthy/running
docker compose logs app # look for "Starting FrankenPHP"
```
### Releasing
```bash
git tag v2026.01
git push origin v2026.01
```
---
2026-04-21 14:18:59 +02:00
## Documentation
2026-04-18 12:57:20 +02:00
2026-04-21 17:56:04 +02:00
For detailed information about game mechanics, bonus systems, fonts, testing, and other technical details, see the [docs ](./docs/ ) directory:
2026-04-18 12:57:20 +02:00
2026-04-21 14:30:38 +02:00
- **[AI Agent Guidelines ](./AGENTS.md )** — Comprehensive guide for AI coding agents working on this project
2026-04-18 12:57:20 +02:00
- **[Bonus Points System ](./docs/game-mechanics/BONUS_POINTS_SYSTEM.md )** — Complete reference for all bonus point types, calculation rules, and implementation details
2026-04-21 14:18:59 +02:00
- **[Fonts ](./docs/FONTS.md )** — TrueType fonts used for server-side image generation
2026-04-21 17:56:04 +02:00
- **[Testing Guide ](./docs/testing/TESTING.md )** — Complete testing setup with Foundry factories, database isolation, and best practices
- **[Factory Documentation ](./docs/testing/FACTORIES.md )** — Detailed API reference for all test data factories
2026-04-18 12:57:20 +02:00
---
2026-04-13 16:10:41 +02:00
## License
LGPL-3.0 — see [LICENSE ](LICENSE ) for details.
© 2026 [SplendidBear ](https://www.splendidbear.org )