Harden gameplay timing and map loading

This commit is contained in:
2026-10-05 15:15:35 +05:00
parent 8b31891776
commit dcc3779262
36 changed files with 1050 additions and 205 deletions

View File

@@ -36,6 +36,9 @@ MINEGEO_ALLOW_ANY_ORIGIN=true
This bypasses MineGeo's browser origin checks, including their CSRF protection.
The default is `false`; only the exact value `true` enables the bypass.
Use `npm run dev` for this testing mode. Production ignores the bypass, and
`npm start` refuses to launch when it is enabled. Public production URLs must use
HTTPS; loopback HTTP remains available for testing a production build locally.
Authentication, permissions, and JSON request requirements still apply. This
setting controls MineGeo's API checks; Supabase email sign-in still needs allowed
redirect URLs, and `MINEGEO_URL` still controls capture-tool pairing links.

57
docs/production-audit.md Normal file
View File

@@ -0,0 +1,57 @@
# Map and production audit — October 5, 2026
Tracked in [Linear DUN-30](https://linear.app/dungeons-and-projects/issue/DUN-30/fix-map-and-gameplay-bugs-and-prepare-minegeo-for-production).
## Fixes
- Panorama viewers own and release their textures on unmount. Failed and stalled
loads release completed and late-arriving faces. There is no unbounded global
cache of every panorama and rotating signed URL.
- Map-image failures and stalled loads show a retry control, including results.
Map cleanup is installed before image initialization. Resizing keeps the map
fitted or preserves the player's view. Keyboard users can pan with arrow keys
and press Enter to place a pin at the center.
- Countdown synchronization accounts for request latency and uses a monotonic
clock. Submission has an immediate lock, failed timeout requests stop retrying
behind an error dialog, current-round points update immediately, and timeout
results do not retain an unsubmitted pin.
- Session media signing uses a single batch for the map and 30 faces. Catalog
signing deduplicates images. Existing revision/media authorization is retained.
- Missing/invalid map IDs get a 404; backend failures reach the retryable error
boundary. The error page now uses Next.js 16.3's `retry()` to fetch fresh data.
Database transport/schema errors return 503 without exposing their internals.
- All API responses prevent caching, guest HTTPS cookies respect the configured
proxy origin, production disables the origin bypass, and baseline browser
security headers are configured. Startup validates environment, heap and
panorama size before launching either process.
- Client requests explain offline, timeout and non-JSON gateway errors. Catalog
searches trim whitespace, source/result selection exposes pressed state,
successful dashboard refreshes clear stale errors, and sign-out failures stay
visible. Results now label the dashboard link accurately as My maps.
## Validation
- 63 Node regression tests passed, including new client-error, texture-disposal,
clock, startup-config, backend-error and production-origin tests.
- TypeScript checking and the optimized Next.js 16.3.8 production build passed.
- The HTTP/API/Storage smoke passed against both development and compiled
production servers with the configured backend. It exercised pages/JARs,
headers, privacy, signed map/cubemap downloads, five rounds, duplicate requests,
out-of-bounds rejection and foreign-origin denial, then deleted its test games.
- Codex's built-in browser verified the live homepage/catalog, trimmed search,
hard-mode panorama and timeout, game reload/results and result-round selection.
A separate local fixture verified failed map images, successful retry, keyboard
guessing and rectangular-map resizing. Browser checks found no uncaught errors.
- The production npm dependency audit reported zero known vulnerabilities.
- The Supabase security advisor and a permissions query confirmed public workflow
RPCs use invoker security and deny anon/authenticated execution. Informational
no-policy notices concern deliberately server-only RLS tables.
## Launch requirements and limits
Leaked-password protection remains disabled in the configured Supabase project;
it must be enabled in provider settings where the plan supports it. Production
SMTP, emailed login/recovery delivery, public HTTPS/proxy configuration and
restoration from backups were not exercised in this local audit. No deployment,
new Minecraft client capture, or cross-platform worker benchmark was performed.
Follow [the production guide](production.md) before public launch.

55
docs/production.md Normal file
View File

@@ -0,0 +1,55 @@
# Production launch
The website requires a Node.js host and a persistent rendering worker. Deploy
this checkout with Node.js 22 or later, run `npm ci`, `npm run setup`, and
`npm run check`, then start it with `npm start` under your host's process manager.
The supervisor runs the website and worker together and stops the companion if
either exits. A host that runs only Next.js must run `npm run worker` separately.
Configure these before starting:
- `MINEGEO_URL`: the public HTTPS origin, without a path, query or credentials.
Loopback HTTP is supported for local production checks.
- `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`, and the
server-only `SUPABASE_SECRET_KEY`. The supervisor validates missing keys and
rejects a server secret in the publishable-key field before starting children.
- Keep `MINEGEO_ALLOW_ANY_ORIGIN=false`. Production ignores the bypass; the
supervisor also rejects it at startup. Use `MINEGEO_ALLOWED_ORIGINS` for exact
aliases and configure your reverse proxy to retain the public Host header.
- Keep the default loopback binding behind a local HTTPS reverse proxy. Set
`MINEGEO_HOST=0.0.0.0` only when your hosting/container network requires it.
- Apply the existing `supabase/migrations` in filename order. This audit adds no
database migration. Keep the media and raw buckets private.
Configure Supabase Auth's Site URL and allowed `/auth/callback` redirects for the
public origin, email confirmation, working production SMTP, and a minimum
password length of 12. Enable leaked-password protection when the plan supports
it; the October 5 audit found it disabled. See the [account guide](account-settings.md)
and [Supabase password security](https://supabase.com/docs/guides/auth/password-security).
The site sends no-store headers for all application API responses, including
drafts, pairing credentials, tokens and errors. Guest cookies are HttpOnly,
SameSite=Lax, and Secure for the configured HTTPS public host behind a proxy.
The response headers prevent framing and MIME sniffing, limit referrers, and
disable unused camera, microphone and location permissions. Configure HTTPS
certificates and HSTS at the public reverse proxy.
Monitor `/api/v1/health`: HTTP 200 means its database query succeeded; 503 means
configuration or the data service is unavailable. Also retain worker logs and
monitor failed/queued jobs; website health does not assert worker liveness.
Keep Supabase backups enabled and test restoration before storing irreplaceable
community uploads.
After deployment, run:
```sh
npm run test:production -- https://your-public-origin.example
```
Run this from the configured checkout for the same Supabase project. The test
checks pages/downloads, security headers, invalid map IDs, guest access denial,
CSRF denial, malformed JSON, session isolation, real signed WebP downloads, and
all five rounds with idempotent activation/submission and out-of-bounds rejection.
It removes only its generated guest session. A published map is required.
See [the audit record](production-audit.md) for evidence and remaining limits.