Security Baseline¶
Security Model¶
This game needs three security layers at once:
- web application security
- game-state integrity
- economy and abuse prevention
Core Rule¶
The client is never authoritative. The browser can request actions, but the server must validate and resolve all meaningful outcomes.
The async, server-authoritative expedition model is the most important anti-exploit measure in the architecture. The server owns all state. There is no client-side game logic to spoof, no mid-combat timing window to abuse, and no authoritative number the client can alter. Most MMO exploits exist because the client was trusted for something. This design does not trust the client for anything.
Edge and Platform Controls¶
- Cloudflare WAF and TLS terminate all traffic at the edge
- Cloudflare Turnstile on registration, login, password reset, and any action that creates an account or session — invisible in most cases, free tier available
- Cloudflare Bot Management (paid) or Bot Fight Mode (free) — handles device fingerprinting at the edge before Workers run
- Rate limiting rules at the Cloudflare edge per IP and per endpoint — auth endpoints must be limited independently from game API endpoints
- DDoS protection is automatic via Cloudflare
Auth and Session Security¶
- Short-lived JWTs (15–30 min expiry) with refresh tokens stored in
HttpOnlycookies — never inlocalStorageor readable JS scope - Email verification required before any game action is permitted
- Rate-limit login attempts at the edge — lock accounts after repeated failures
- Support session rotation and explicit revocation (logout invalidates the refresh token server-side)
- Passkeys preferred over passwords if implementing modern auth; otherwise bcrypt with adequate work factor for password storage
- Optional MFA for accounts with high-value inventory or guild treasury access
Bot Prevention¶
Bots in an async game are not trying to react faster — they are automating repetitive optimal loops: perfect-timing market orders, continuous low-risk expedition grinding, or account farming. Catch them through:
- Action velocity limits server-side per account — a human cannot submit 50 expedition commands per minute; enforce a hard ceiling with exponential backoff penalties
- Market order velocity limits — flag accounts posting or canceling orders faster than humanly possible
- Statistical pattern detection — accounts that always buy or sell at statistically perfect timing across sessions are bot candidates; log and flag for review
- Position caps — one account cannot own more than a defined percentage of any single commodity on the market; prevents both bot accumulation and coordinated manipulation
- Turnstile on registration — stops the account farm layer before bot accounts are created
Economy Exploit Prevention¶
Economy exploits are the highest-risk threat for this game type. The priority mitigations:
Atomic writes Every command that creates, moves, or destroys items must execute as a single D1 transaction. No partial writes that generate items from nothing or consume them without granting the outcome.
Idempotent commands with unique request keys Every mutating command carries a client-generated unique key. The server stores settled keys and rejects duplicates. This eliminates the entire class of "submit → network glitch → retry → receive item twice" exploits. This must be implemented from the start, not retrofitted.
Idempotency Implementation¶
Client: generate crypto.randomUUID() before each mutating request. Send as Idempotency-Key: {uuid} HTTP header. Generate a new key for each distinct logical action — do not reuse across different commands.
Worker middleware (runs before business logic on all mutating routes):
1. Read Idempotency-Key header; reject 400 if missing on applicable endpoints
2. SELECT response_json FROM idempotency_key WHERE key = ? AND character_id = ?
3. If found and expires_at > now() → return stored response_json immediately
4. Proceed with business logic
5. In same D1 transaction as the mutation:
INSERT INTO idempotency_key (key, endpoint, character_id, response_json, settled_at, expires_at)
VALUES (?, ?, ?, ?, unixepoch(), unixepoch() + 86400)
TTL: 24 hours. Client may retry any mutating request within 24 hours with the same key and receive the original response without double-execution.
Applicable endpoints: all POST/DELETE routes that mutate game state — board accept, market order post, market fill, item transfer, expedition cancel, guild storage deposit/withdraw, message send.
Cleanup: cron job (daily) runs DELETE FROM idempotency_key WHERE expires_at < unixepoch().
See d1-schema.md for the idempotency_key table definition.
Supply commitment at acceptance, not at settlement Supplies consumed by an expedition must be deducted from inventory when the expedition is accepted, not when it settles. There must be no window where both the expedition record and the inventory row believe they hold the same resource.
Append-only item audit log Every item creation, destruction, and transfer writes an immutable log row in D1. Any duplication exploit becomes immediately visible and fully traceable. All affected accounts can be identified and rolled back.
Reject client-asserted quantities and IDs Never trust item quantities, item IDs, or target IDs sent by the client as proof of ownership or availability. Re-read the authoritative server row on every command and reject if they do not match.
Escrow for high-value settlement Direct trade and contract payouts hold funds in an escrow row until both sides confirm. Settlement writes both sides atomically or not at all.
Input Validation at the Worker Boundary¶
- Schema-validate every incoming command — reject unexpected fields, wrong types, negative quantities, and out-of-range values before any business logic runs
- Validate that referenced IDs (item IDs, area IDs, target IDs) exist and that the requesting account has permission to act on them
- Use D1 parameterized queries everywhere — no string-concatenated SQL
- Sanitize all player-authored text (character names, any future chat or item descriptions) — XSS is a real risk if any text is rendered without escaping
- Do not allow raw HTML in player-facing content
- Do not allow user-uploaded SVG at launch; if custom emblems are added later, sanitize and rasterize server-side before storage
Durable Objects Concurrency¶
Durable Objects execute single-threaded per instance. Race conditions within one expedition coordinator, market coordinator, or town coordinator are naturally eliminated.
Cross-DO operations (for example, settling a market trade that touches two players' inventories) must use compensating transactions or a coordinator pattern. Do not use optimistic concurrent writes across DO boundaries — one side can succeed and the other fail, leaving state inconsistent.
Secrets and Build Hygiene¶
- No secrets in the client bundle — API keys, signing secrets, and service tokens go in Workers Secrets, not environment variables that get inlined at build time
- Sign R2 asset URLs rather than making the bucket public — prevents asset hotlinking and leaking unreleased content
- Separate staging and production D1 databases from the first day of development — never test against production data
- Staff and admin tools connect through a separate Worker with explicit role checks, not through the same API surface as players
Monitoring and Incident Response¶
- Flag accounts accumulating items or currency at rates outside observed player distributions
- Alert on large single-transaction inventory changes above a defined threshold
- Log every expedition settlement outcome with enough detail to fully replay it — if an exploit is found, you need to identify every affected account
- Tools to freeze trading, auction posting, or treasury flows per account or globally during an active incident
- Immutable admin action logs — every staff action that touches player inventory or economy state is recorded with actor, timestamp, and reason
Phase 1 Security Minimum¶
Do not launch without all of the following in place:
- server-authoritative expedition resolution with immutable acceptance snapshots
- Turnstile on registration and login
- edge-level rate limiting on auth endpoints
HttpOnlycookie session handling with refresh token revocation- idempotent command keys on all mutating commands
- D1 parameterized queries with no raw SQL concatenation
- append-only item and currency audit log
- sanitized player text with no raw HTML rendering
- atomic item transactions with no partial writes
- supply deduction at expedition acceptance