# REBNO — Rebuild BN Online

## Current Milestone: v1.1 Map Groundwork

**Goal:** Stand up the LDtk authoring + fast-deploy + in-game minimap pipeline that unblocks porting all legacy BNO rooms and floor-tile assets.

**Target features:**
- Convert legacy BNO rooms + relevant assets into a single comprehensive LDtk project (decomposed by content category: non-interactive static floor tiles, animated floor tiles, tiles with metadata, interactive tiles, entities)
- Confirm conversions import to LDtk in an acceptable, manageable format
- Map publish + fast-deploy workflow (edit → push → see live)
- Smoke-test the workflow with a minimal layout (basic BORDERED floor tiles only)
- In-game minimap UI

**Editor decision (pre-locked):** LDtk — see `.planning/notes/map-editor-decision.md` for full rationale, alternatives weighed, accepted tradeoffs (~1-task custom Phaser loader, BNCentral split mitigation).

**Phase numbering:** starts at **Phase 7** (v1.0 ended at 6.8; original Phase 7 Full Parity was relegated to backlog on 2026-05-17 so the number is free).

**Carry-overs from v1.0 explicitly NOT in v1.1 unless pulled in by requirements:** Phase 06.8 anti-cheat fall trigger, SRV-08 kill -9 verification, Phase 5 HUMAN-UAT Test 5 + 6, CI restoration, D-63 audit script, trace:check placeholder sweep.

## Previous State (v1.0 MVP shipped 2026-05-17)

**v1.0 MVP is live on Fly.io staging.** Two real players (`dunsen_uat` + `rebbie_uat`) can log in over WSS, walk around a single MVP room, exchange chat messages, and disconnect/reconnect within the grace window — demonstrated end-to-end in HUMAN-UAT round 2 at commit `1f6073e`. CLI-08 (the entire-rebuild hard milestone) is **cleared**.

Stack landed as planned: monorepo with `apps/client` (Vite + Phaser 3.90 + TypeScript), `apps/server` (Node 22 + Colyseus 0.17 + Drizzle + Better-Auth + argon2id), shared `packages/protocol` + `packages/game-logic`, SQLite + Litestream → Tigris persistence, deployed on a single Fly.io machine. The original 13-phase plan compressed into 13 phases (11 executed + 2 superseded by a late trust-model pivot in 06.7).

**Significant mid-flight pivot (Phase 06.7):** player-movement authority flipped from server-authoritative to client-owns-position, eliminating the reconciler-drift bug class that had blocked Phases 06.1 → 06.3 → 06.4. CLAUDE.md Hard Rule 1 carries a narrow movement carve-out. Anti-cheat backstop (`isPositionLegal` + fall-reset) was cut from 06.7 to ship CLI-08 and is now Phase 06.8 in the next milestone.

## What This Is

REBNO is a ground-up modernization of *BN Online*, a late-1990s/early-2000s multiplayer game shipped as a GameMaker 5.3a `.gmd` project. v1.0 delivered a working two-player MVP slice (movement + chat) over a deployed authoritative server. Full feature parity with the original (asset pipeline, multi-room, full chat, character migration, modernized admin) is staged for follow-up milestones.

## Core Value

**Original BN Online players can open Chrome, log in with their migrated account, walk around, and chat with each other in real time — bug-free.** Validated end-to-end via HUMAN-UAT round 2 at `1f6073e`.

## Requirements

### Validated

#### Extraction (Phase 1, 2026-05-03)
- ✓ EXT-01..08 — `tools/extract-gmd` ports LateralGM `GmFileReader` semantics; 157 tests; both `BN Online Client 5-8.gmd` + `BN Online Master 5-4.gmd` parsed end-to-end; byte-identical re-extraction; 13 wire-format bugs documented against LateralGM source; opaque BMP carried forward (Option A pivot).

#### Client Engine Documentation (Phase 2, 2026-05-04)
- ✓ CDOC-01..04 — 11 subsystem MDs + asset catalogue + feature-vs-engine matrix (Phaser 3.90 = 286, Phaser 4.1 = 286, PixiJS 8.18 = 201); ADR 0001 locks Phaser 3.90.0.

#### Server Documentation & Schemas (Phase 3, 2026-05-05)
- ✓ SDOC-01..06 — 11 server subsystem MDs + 39dll opcode table + `.bno`/`.bnb`/`.bnu` field schemas (Drizzle 8-table baseline) + ADR 0002 (SQLite + Litestream) + ADR 0003 (canonical-snapshot) + locked parity checklist.

#### Server Rebuild MVP (Phase 4, 2026-05-07)
- ✓ SRV-01..14 — Node 22 + Colyseus 0.17, 30 Hz fixed-tick, token-bucket rate limiting, Better-Auth + argon2id, legacy-account transparent rehash on first login, signed `RoomRegistry` hot-reload, `lint-no-clipboard-rce` forcing function (SRV-12 anti-port).
- ~ SRV-08 partial — SIGTERM grace ✓; kill -9 mid-tick verify deferred to Phase 5 staging (Linux host).

#### Deploy (Phase 5, 2026-05-08)
- ✓ DEP-01..08 — Multi-stage Dockerfile, fly.staging.toml + fly.prod.toml, Litestream + Tigris (RPO sub-second), `/health`, pino + OpenObserve, RESTORE.md drilled.
- ⚠ DEP-04 deploy path swapped 2026-05-17 — GH Actions storage exhausted → operator-local `flyctl deploy` per `docs/deploy/LOCAL-DEPLOY.md`.

#### Client Rebuild — MVP Gate (Phase 6 + 06.1/06.2/06.5/06.6/06.7, 2026-05-17)
- ✓ CLI-01..09 + AST-01 — Vite + Phaser 3, login + game scenes, client-prediction + interp, chat HUD, HiDPI integer scale, content-hashed asset manifest.
- ✓ **CLI-08 CLEARED** at `1f6073e` (HUMAN-UAT round 2 PASS, two real players, two browser pairs).

### Active (next milestone candidates — not yet ratified)

#### Phase 06.8 — anti-cheat illegal-position fall trigger
- [ ] Cut from 06.7 to ship CLI-08. Server-side tick-by-tick `isPositionLegal(player.x, player.y, room.walkable_grid)` + grace-window (~250–500 ms) fall-reset to last-known-legal/respawn.

#### Stage 7 candidates (re-decompose from Full Parity backlog)
- [ ] AST-02 vintage-soundfont MIDI→OGG; AST-03 BMP fonts → WOFF2; AST-04 content-hashed cache-bust manifest
- [ ] PAR-01 full client surface; PAR-02 full server surface; PAR-03 all rooms imported + pixel-diff verification; PAR-04 full chat (ignore/block/report/profanity/channels/history); PAR-05 transactional `.bnu` character migration; PAR-06 multi-room + presence + whispers + recovery; PAR-07 modernized admin web UI; PAR-08 closed parity checklist drained
- [ ] Phase 5 HUMAN-UAT Test 5 (legacy `localList.txt` one-shot import) — still pending
- [ ] SRV-08 kill -9 mid-tick verification on Linux staging (procedure in `04-HUMAN-UAT.md` Test 1)
- [ ] prod environment provisioning (`rebno-prod` never created)
- [ ] CI restoration (GH Actions storage exhausted) OR commit to operator-local deploy permanently
- [ ] D-63 HARD-gate `tooling/no-inline-origin.ts` audit script (helper itself shipped; audit deferred from 06.4)
- [ ] `pnpm trace:check` placeholder sweep (`REQ-SRV-XX` / `REQ-X` noise in Phase 04 + 06.4 docs)

### Out of Scope

Unchanged from v1.0:

- **Mobile / native ports** — Chrome-first; PWA only.
- **GameMaker 5.3a engine emulation** — reimplement game logic, not GM5 runtime.
- **Re-running original Windows binary** — preservation belongs in `legacy/`.
- **Server-side behavioral anti-cheat** — only authoritative model + fall-reset (06.8); no ML detection in v1/v2.
- **Modding API / UGC** — no public mod surface until parity ships.
- **3D / VR / new gameplay** — faithful rebuild first.
- **Public marketing / asset rights** — legal posture deferred until functional.
- **Plaintext / weak password storage** — explicitly rejected per CLAUDE.md hard rule #2.
- **"Run clipboard as superuser" admin** — explicitly rejected per CLAUDE.md hard rule #3.
- **Federation / multi-server interop** — single canonical server.
- **Voice chat** — not core value.
- **Public username changes** — identity continuity for migrated accounts.
- **Socket.IO transport** — wrong tool; binary WS via Colyseus chosen.
- **Lucia auth library** — deprecated March 2025.
- **Postgres v1 persistence** — wrong fit for <50 CCU on single Fly machine.

## Context

- **Architecture**: pnpm monorepo. `apps/client` (Vite + Phaser 3.90 + TypeScript). `apps/server` (Node 22 + Colyseus 0.17 + Drizzle + Better-Auth + argon2id + better-sqlite3). `apps/obs` (OpenObserve sidecar Fly app). `packages/protocol` (wire types + msgpackr codec, PROTOCOL_VERSION 4). `packages/game-logic` (pure deterministic step + AABB collision + splitmix64). `packages/db` (Drizzle 8-table baseline). `tools/{extract-gmd, asset-pipeline, asset-catalog, protocol-doc, save-format-doc, room-converter, db-schema}`.
- **Persistence**: SQLite WAL + Litestream → Tigris S3-compatible bucket on Fly.io. RPO sub-second verified.
- **Deploy reality (post-2026-05-17)**: GitHub Actions storage quota exhausted; deploy is operator-local via `flyctl deploy`. `deploy-staging.yml` future-proofed but out of service. Full procedure: `docs/deploy/LOCAL-DEPLOY.md`.
- **Hosting**: single Fly.io machine, persistent volume mounted at `/data`. Client static assets split to `/data/client-assets/current` symlink for zero-cost client-only releases (Phase 06.5).
- **CCU target v1**: <50.
- **Source-of-truth files**: `legacy/open-source-release/BN Online Client 5-8.gmd` + `legacy/open-source-release/BN Online Master 5-4.gmd`. Older revisions in `legacy/source-archive/` + `legacy/servers/` reference-only.
- **Coordinate conventions pin** (D-63 from 06.4 helper): legacy GameMaker uses `(0, 0)` top-left for Navi + effects; Phaser uses `(0.5, 1)` bottom-center for Navi. Helper `phaserOriginForLegacyPlayerAttached` at `apps/client/src/render/legacy-origin.ts` is the canonical entry point for player-attached effect ports. Pinned in CLAUDE.md.
- **Repo stays private through next admin-UI milestone** — BNO is Capcom-derived (Mega Man Battle Network); legal/IP path deferred.

## Constraints

Unchanged from v1.0 (all validated):

- **Server**: Node 22 + TypeScript + Colyseus 0.17.42 + better-sqlite3 + Drizzle. **Validated.**
- **Client**: TypeScript + Vite + Phaser 3.90.0 (ADR 0001). **Validated.** Phaser 4 caveat held — nothing surfaced in v1.0 that required the Pixi fallback.
- **Transport**: WebSocket binary frames + msgpackr. **Validated.**
- **Movement trust**: client-authoritative (carve-out from Hard Rule 1; introduced 06.7). All other state (chat origin, inventory, scores, room transitions, persistence) remains server-authoritative.
- **Persistence**: SQLite + Litestream (ADR 0002). **Validated** at <50 CCU.
- **Hosting**: Fly.io single machine + `/data` persistent volume. **Validated.**
- **Auth**: Better-Auth + argon2id; legacy accounts rehashed transparently on first login. **Validated.**
- **Compatibility — runtime**: Chrome desktop only. **Validated.**

## Key Decisions

| Decision | Phase | Outcome |
|---|---|---|
| Server: Node 22 + TypeScript + Colyseus 0.17 | 3-4 | ✓ Validated — staging stable |
| Client: Phaser 3.90.0 (ADR 0001) | 2 | ✓ Validated — Phaser 4 caveat held |
| Persistence: SQLite + Litestream (ADR 0002) | 3 | ✓ Validated — RPO sub-second |
| Canonical snapshot resolution (ADR 0003) | 3 | ✓ Validated |
| Hosting: Fly.io single machine + `/data` | 5 | ✓ Validated |
| Transport: WebSocket binary + msgpackr | 4 | ✓ Validated |
| Hot-reloadable signed room registry (ADR 0004) | 4 | ✓ Validated |
| Persistence layer ADR 0005 / 0006 | 5 | ✓ Validated |
| Phaser viewport: Scale.NONE + manual integer setZoom (ADR 0008) | 06.6 | ✓ Validated |
| Static client asset split via `/data` symlink swap (Phase 06.5) | 06.5 | ✓ Validated — zero-cost client-only releases |
| **Movement trust: client-authoritative (Hard Rule 1 carve-out)** | 06.7 | ✓ Validated — eliminated reconciler-drift bug class; HUMAN-UAT PASS |
| Phase 7 Full Parity relegated to backlog | post-06.7 | ✓ Right call — spans many milestones |
| **Deploy: operator-local `flyctl deploy`** (GH Actions storage exhausted) | 06.7-09 | ⚠ Revisit — accept permanently OR restore CI in next milestone |
| Phase 06.8 anti-cheat fall-reset cut from 06.7 | 06.7 | ⚠ Carry forward — currently no fall-reset backstop on client-trust model |

## Scope Principle & Mandated Divergences

Established in the 2026-06 domain grilling session (see `CONTEXT.md` for the full glossary and
`.planning/legacy-recon/` for the supporting code recon).

**Dividing test (parity vs new system):** a change is in-scope **QoL** if it preserves the legacy
mechanic's *inputs, gating, and outcomes* and only improves presentation, ergonomics, feedback,
or accessibility. It is an out-of-scope **new system** if it adds a player capability, changes
progression gating, or changes what is collectable/unlockable. Worked calls: portal/Link-Data UX
overhaul, gamepad-first input, and cross-zone hexporters are **in** (the last is trivial under
ReBNO's chunked overworld); IP-shed + a battle system are **far-future, out**.

**Mandated divergences (faithful port deliberately rejected):**
- **Server authority** — legacy is fully client-authoritative (the Master only relays); ReBNO is
  server-authoritative across rules + inventory (Hard Rule 1). This is a ground-up rebuild of the
  rule layer, not a port.
- **Perception gating** — per-player visibility is server-computed (see ADR 0013).
- **No RCE** — the legacy clipboard-exec admin model is replaced by a vetted command set
  (Hard Rule 3); note not all legacy "mod items" are RCE (Location, Joker Shell, roster, spectate
  are fenced features worth keeping).
- **Passwords** — argon2id, never plaintext (Hard Rule 2).

**Identity reminder:** BNO is a social-exploration **Metroidvania-of-connectivity** — no combat,
no economy, no quests in the parity target. The MMBN aesthetic is skin, not mechanics.

## Evolution

This document evolves at phase transitions and milestone boundaries.

---
*Last updated: 2026-05-18 — v1.1 Map Groundwork milestone started.*
