---
description: Server-authoritative architecture blueprint — authority boundaries, WS protocol contract, and gap priorities. Reference docs/blueprint_server_authoritative.md for full details.
alwaysApply: true
---

# Server-Authoritative Architecture

Full blueprint: `docs/blueprint_server_authoritative.md`

## Authority Boundary

**Server owns:** combat simulation, damage/crit rolls, HP changes, XP/gold awards, level-ups, loot generation, buff/debuff logic, enemy spawning, death detection.

**Client owns:** rendering (PixiJS), camera, animations, walking visuals, UI overlays (HUD, HP bars, buff bars, popups), input (touch/click), sound/haptics triggered by server events.

**Client must NOT:** compute damage, roll crits, award XP/gold, generate loot, run level-up calculations, or send hero stats to server.

## WS Message Envelope (mandatory)

All messages (both directions) use `{"type": string, "payload": object}`. Text `"ping"`/`"pong"` for heartbeat (not inside envelope).

### Server → Client Events

| Type | Key Payload Fields |
|------|-------------------|
| `hero_state` | Full hero JSON (sent on connect/revive) |
| `combat_start` | `enemy`, `heroHp`, `heroMaxHp` |
| `attack` | `source` (hero\|enemy), `damage`, `isCrit`, `heroHp`, `enemyHp`, `debuffApplied` |
| `hero_died` | `heroHp`, `enemyHp`, `killedBy` |
| `combat_end` | `xpGained`, `goldGained`, `newXp`, `newGold`, `newLevel`, `leveledUp`, `loot[]` |
| `level_up` | `newLevel`, all stat fields |
| `buff_applied` | `buffType`, `magnitude`, `durationMs`, `expiresAt` |
| `debuff_applied` | `debuffType`, `magnitude`, `durationMs`, `expiresAt` |
| `town_tour_phase` | `phase`, `townId`, optional NPC fields, `exitPending` |
| `town_tour_service_end` | `reason` (e.g. timeout) |
| `town_npc_visit` | NPC stand snapshot (log/engine; UI follows `town_tour_phase`) |

### Client → Server Commands

| Type | Payload |
|------|---------|
| `request_encounter` | `{}` |
| `request_revive` | `{}` |
| `activate_buff` | `{"buffType": "rage"}` |
| `town_tour_npc_interaction_opened` | `{}` |
| `town_tour_npc_dialog_closed` | `{}` |
| `town_tour_npc_interaction_closed` | `{}` |

## Critical Gaps (P0 — must fix)

1. **GAP-1:** `SaveHero` accepts arbitrary stats — **delete it**, replace with preferences-only endpoint
2. **GAP-2:** Combat is client-only — wire `engine.StartCombat()` into encounter handlers
3. **GAP-3:** XP/gold/level client-dictated — server awards on `handleEnemyDeath`, persists to DB
4. **GAP-4:** Auth middleware disabled — uncomment and enforce Telegram HMAC validation

## Backend Rules

- Engine emits `WSMessage` (not raw `CombatEvent`) through `eventCh`
- `handleEnemyDeath` must persist hero to DB after awarding rewards
- WS handler extracts heroID from Telegram auth `initData`, not hardcoded
- On client disconnect during combat: `StopCombat(heroID)` + persist state
- Engine is the single writer for heroes in active combat (REST reads from engine, not DB)

## Frontend Rules

- Remove `_simulateFighting`, `_onEnemyDefeated`, `_spawnEnemy`, `_requestEncounter` (client combat sim)
- Remove `_serverAuthoritative` flag — always server-authoritative
- Remove auto-save (`_triggerSave`, `sendBeacon`, `heroStateToSaveRequest`)
- Walking requests encounters via WS every ~3s; server enforces cooldown
- Revive and buff activation go through WS commands (REST as fallback)
- On WS disconnect: show "Reconnecting..." overlay; server sends `hero_state` on reconnect

## Implementation Order

1. Enable auth middleware (blocks everything)
2. WS envelope + ping/pong + heroID from auth
3. Wire `StartCombat` into encounter handler
4. Engine events with rewards + DB persist on death
5. Delete `SaveHero`
6. Frontend: remove client combat sim, add server event handlers
7. Frontend: switch encounter/revive/buff to WS commands
8. Buff/debuff DB persistence
9. Server-side loot generation
10. CORS + WS `CheckOrigin` hardening

Steps 1–5 (backend) and 6–7 (frontend) parallelize once WS contract is agreed.
