# MapleWeb project status

Last updated: 2026-08-21 (America/Los_Angeles)

## Current stage

Stage 1 — reproducible foundation: **complete (2026-08-20)**.

Stage 2 — production web shell and transport: **in progress**.

Stage 3 — registration and login state machine: **complete (2026-08-20)**.

Stage 4 — core playable v83 slice: **in progress**.

Stage 7 — asset/content pipeline: **in progress**.

The full “all GMS v83 features” scope is approximately **35% complete** as a
planning estimate, not an acceptance-gate score. The public smoke-test milestone
is available now, but Stages 4–9 remain incomplete.

### Latest public QA checkpoint

Release `public-test-qa-stage-c4` publishes the browser-tested login-notice
stacking and selected-world fallback fixes from `636020d` and `985e002` plus
this documentation checkpoint.
It passes the exercised public path
from simple username/password registration through PIN-free login, PIC, world and
character selection, character creation, channel redirect, and initial field
entry. The v83 HUD, minimap, inventory, equipment, skills, stats, world map,
movement, fullscreen, and chat render and remain operational. The release-scoped
browser console is error-free after the Stage C4 blocked-name, Cancel, and
selected-world layout checks. See
`docs/QA_LOG.md` for the staged evidence, defect ledger, open manual checks, and
exact continuation point.

## Completed evidence

- Cloned `nmnsnv/maplestory-wasm` at `bc0234fe7c7f53322453e7bdd79564d9aca4cd8b`.
- Created working branch `codex/mapleweb-port`.
- Audited client architecture, feature coverage, stubs, build/runtime needs, security risks, assets, tests, and license.
- Audited the local Moonlight tree read-only and established the v214/v83 clean boundary.
- Inspected pinned P0nk/Cosmic reference at `fec53bc7714dc0f1ae3f50b2986cdf2727e0912a`.
- Audited optional `P0nk/Cosmic-client` at `6b7328b1593d34a4b134fe6b8a6d20119e526030`; it is a native executable/WZ LFS distribution, not client source, and is constrained by ADR 0004.
- Performed a read-only preflight of the earlier Plesk target; it was superseded by the 2026-08-20 Docker/public-IP target change and no remote state was changed.
- Added the controlling blueprint, feature matrix, and initial architecture decisions.
- Generated the first deterministic client/protocol/Cosmic opcode inventory:
  - client → server: 178 catalogued, 45 client-named, 133 missing;
  - server → client: 307 catalogued, 55 concrete handlers, 21 no-ops, 6 named/unregistered, 225 missing.
- Stage 0 acceptance gate passed on 2026-08-19.
- Installed and activated project-local Emscripten 4.0.21, CMake 3.31.6, and Ninja 1.12.1 without Docker.
- Completed two independent warning-free Release builds (175 steps each). The output hashes matched exactly:
  - `JourneyClient.js`: `f307ab30da548b3086a7b5b866088f8408d2b9024149e4d9165eb860f8dd3a25`;
  - `JourneyClient.wasm`: `af9eb7fb48157f289722a23281cd1e5671e8ab5c3b3169295f299f82b1bac969`.
- Scanned both build artifacts for absolute workspace/developer paths; none were present.
- Verified the pinned Cosmic commit with project-local Temurin 21.0.12+8: `mvnw.cmd clean verify` passed 1,886/1,886 tests with no failures, errors, or skips.
- Verified `Cosmic.jar` archive integrity (`53,192,844` bytes; SHA-256 `d5f291d59e9d4c507932394c38a905d0172618abc64b3fb66a81c15f67a1478b`).
- Added pinned Windows/Linux toolchain and Cosmic bootstrap/build wrappers, dependency locks, deterministic SPDX 2.3 generation, build attestations, and release packaging.
- Rebuilt the transport/login milestone WASM client twice, including one empty build directory (176 compile/link steps), with warning-as-error enabled and byte-identical outputs:
  - `JourneyClient.js`: `7663ae7ba97224fbdb2c2a02eaa5417d62a988713d9f2f1b3d8ce753018bb699`;
  - `JourneyClient.wasm`: `b2cf4db0a79f2e71a59167a0373674aee6892e97ff09ba842b9a6b0cd5c7a3b7`.
- Latest verified movement-code checkpoint `52fd004358cc0a37aa56f8628699e8f57e1cf99e` packages JS `7b888c4c7ef1e17bf3686d5a5267812c1e35e3f336c3ddfada737d9fabc82f81` and WASM `5e7cf0b6a7938e32e032abb84b17cd5c42b8520ca4b45faa1ab4d21a8d6364b0` from a clean source tree.
- Hardened the game and asset WebSocket services with fixed destinations, exact origin/path checks, bounded frames/requests/connections/lifetimes, graceful shutdown, and secret-free logs.
- Added the base-path-aware portal, release integrity checks, browser diagnostics, loading/retry/audio/fullscreen states, source/license surfaces, and same-origin registration dialog.
- Passed 41 web/transport tests and 24 registration unit/API tests. One registration test is the opt-in disposable-MySQL fixture; one Windows-only symbolic-link assertion is skipped when the process lacks link privileges.
- Passed the registration adapter against the pinned Cosmic `accounts` DDL in a digest-pinned disposable MySQL 8.4 container, including bcrypt verification, first-login defaults, duplicate handling, and an INSERT-only database identity.
- Browser-tested the packaged portal: all compatibility diagnostics passed, no warnings/errors reached the console, and missing production assets produced a bounded actionable client error.
- Wired the in-game v83 `BtNew` registration button to the portal's same-origin registration flow without adding a non-v83 game opcode.
- Closed the Stage 1 clean-commit gate at `66ba377f9ba036db2614a4a764bc7ca4d5dbb417`: the warning-free build attestation records `sourceClean: true`, matches the exact commit and output hashes, and release packaging produced the corresponding-source archive and machine-readable offer.
- Added warning-free native client fixtures for connection-generation invalidation, TOS/gender/PIN ordering, malformed state transitions, and complete/truncated login-account packet parsing.
- Removed the shared hard-coded character-selection MAC/HWID. The browser now supplies a stable random pseudonymous identity, and exact packet fixtures cover normal selection, PIC registration, and PIC-authenticated selection.
- Re-ran the consolidated checkpoint suite: five native client fixtures, 39 web/transport tests, 24 registration tests, the WASM transport smoke suite, and the disposable MySQL/Cosmic-schema integration all pass.
- Added exact Cosmic request fixtures for world/channel selection and character name/create/delete, and corrected the delete-response mapping for system, rate-limit, birthday, PIC, guild, wedding, transfer, and family failures.
- Passed the packaged registration form through a real browser, same-origin CSRF cookie/header flow, registration API, INSERT-only disposable MySQL identity, and bcrypt verification. Registration is now intentionally limited to username and password—no PIN/PIC, confirmation field, or acknowledgement checkbox—and clears the password after submission.
- Made registration fail closed outside a browser secure context: a flagged plaintext release shows `HTTPS required`, keeps registration controls disabled, and never opens or submits the credential form.
- Added exact Cosmic response fixtures for character list/name/create/delete, channel redirect, and relog. The bounded parser consumes complete bodies, rejects malformed/truncated data, preserves the selected channel instead of mistaking Cosmic's status byte for it, and registers the previously missing `RELOG_RESPONSE` handler.
- Regenerated the deterministic inventory after the controlled-mob work: 61 server-to-client opcodes now have concrete handlers, 20 remain explicit no-ops, 6 are named/unregistered, and 220 are missing.
- Added the production Docker topology and runbook: digest-pinned nginx is the only port-publishing service, while registration/game/assets share its network namespace on loopback and use a separate private backend network.
- Passed the repeatable Docker edge rehearsal on random loopback ports: HTTP 308, HTTPS 200, security headers, cache policy, WASM `application/wasm`, 206 range delivery, dotfile denial, WSS upgrade through the allowlisted bridge, TCP echo, two-port isolation, and preservation of unrelated containers.
- Extended that rehearsal through nginx's root-path registration API and digest-pinned disposable MySQL: same-origin CSRF, column-limited INSERT grants, Cosmic defaults, cost-12 bcrypt verification, and zero MySQL/registration published ports all pass.
- Added bounded `LOGIN_STATUS` fixtures for success, wrong password, unknown account, already-online, rate-limit, TOS, permanent/temporary bans, truncation, reserved-field corruption, and trailing bytes. The handler now consumes every Cosmic field and distinguishes temporary bans from permanently blocked accounts.
- Added a resolved-Compose security test; 42 web/transport tests and 24 registration tests pass, with the same two environment-dependent skips.
- Added a warning-free native live-login probe plus an isolated Docker rehearsal using the pinned Cosmic JAR/WZ tree, digest-pinned Corretto 21 and MySQL 8.4, a generated database credential, no database host port, and a random loopback-only login port. The real encrypted v83 flow passes fresh-account/TOS reason 23, verifies Cosmic's cost-12 bcrypt row, rejects a wrong password with reason 4, and parses a complete successful account response after the fixture completes TOS/gender state.
- Extended the same secret-safe live session through TOS acceptance, gender persistence, PIN registration and returning PIN verification, world/character lists, available-name validation, a server-validated Beginner creation, PIC registration, channel redirect, a second v83 handshake on a random loopback-only channel port, `PLAYER_LOGGEDIN`, and receipt of the initial `SET_FIELD`. Disposable SQL assertions confirm the account, PIN/PIC, and character rows.
- Extended live recovery coverage through wrong-PIN retry, wrong-PIC retry, permanent ban, and future-dated temporary ban. The live permanent packet revealed that Cosmic uses `DEFAULT_TIME` (`150842304000000000`) for `getPermBan`; the runtime and exact fixture previously used the unrelated item-expiry sentinel and would have displayed permanent bans as temporary. Both now match the real wire packet.
- Added the outgoing `RELOG (0x1C)` packet with an exact byte fixture and live encrypted response proof. Cosmic requires re-authentication before world-list requests. The live matrix also proves PIN cancellation releases `loggedin`, a subsequent connection completes the full reconnect path, and an isolated online-state fixture returns reason 7.
- Completed the relog UI lifecycle: world and character selection expose the v83 exit control, dispatch `RELOG`, and a successful Cosmic response reconstructs a fresh login form instead of requesting worlds while unauthenticated. The portal announces `Sign in required` and focuses the canvas; no password is stored or replayed. The packaged portal loaded cleanly in the in-app browser and its event contract is covered by the web suite; 46 web/transport tests, 24 registration tests, six warning-as-error native fixtures, the WASM transport smoke suite, a warning-free WASM build, and the complete live Cosmic recovery matrix pass.
- Closed the first-time account PIN browser lifecycle defect at `b3c7686`. Cosmic's successful PIN update requires a new unauthenticated login connection; the WASM client now reconnects to the configured login route and rebuilds a fresh credential form instead of reporting a terminal disconnect. A newly created public account passed registration, Terms, gender, PIN creation, automatic return to sign-in, reauthentication, saved-PIN verification, and Scania world selection. The warning-free release build, 51 web tests (one environment skip), and all 21 native state/protocol fixtures pass.
- Closed the portal/native authentication-state mismatch at `ac79cc8`. The client now notifies the shell when its authenticated world-list request begins, restoring `Client ready` and the disabled `Client launched` control after a relog or first-PIN reconnect. Public Stage C QA created two characters, navigated both, registered PIC, entered a field, reconnected, and deleted an explicitly selected second character while preserving the first. Stage D captured jumping, Heena's complete tutorial dialogue, the transition from Mushroom Town to In a Small Forest, key configuration, and party UI. The quest-log binding remains an explicit implementation gap. The warning-free release build, 52 web tests (one environment skip), and all 21 native state/protocol fixtures pass.
- Disabled account PIN on the public Cosmic deployment with its supported `ENABLE_PIN: false` configuration. A returning account now proceeds directly from username/password authentication to Scania; no PIN is embedded, stored, or auto-submitted by the public client. PIC remains independently enabled.
- Restored visible character-name rejection feedback at `636020d` by rendering login notices after full-screen login scenes. Public C3/C4 browser QA proves that a blocked name shows the v83 notice, its OK button dismisses it, and creation Cancel returns to the roster.
- Filled the original-v83 selected-world asset gap at `985e002` with server-derived world/channel text inside the native panel. Public C4 shows `Scania` and `CH. 1`, keeps the character information panel inside 800×600, and reports zero browser warnings/errors. The warning-free WASM build, 56 web tests (one environment skip), and all 21 native fixtures pass.
- Began Stage 4 with a bounded `SET_FIELD` envelope/tail parser and a seventh warning-as-error native fixture covering initial entry, normal/chasing map changes, malformed reserved fields, truncation, and trailing data. Fixed the missing skill marker and final sentinel/timestamp consumption, bounded signed collection counts, implemented complete New Year card consumption, and now applies channel-authoritative stats instead of discarding them in favor of the stale login snapshot. The live Cosmic probe validates the real initial envelope and character ID.
- Made initial full-character application atomic with respect to live gameplay state: inventory, skills, cooldowns, quests, teleport rocks, and monster-book data decode into an off-stage player, which is moved into `Stage` only after the full tail validates. Strict linked-name, monster-book, and New Year flag decoding now rejects non-boolean wire values.
- Introduced an off-stage `InitialCharacterData` transfer object for the complete initial-character domain. Stats, buddy/link metadata, inventory, skills, cooldowns, quests, rings, teleport rocks, monster book, New Year cards, area info, and server time are retained before `Player` construction and `Stage` mutation. This is packet-test isolation only; compatible NX remains the required runtime source for rendering and game content. All seven warning-as-error native fixtures and the warning-free release WASM build pass after the refactor.
- Extracted the complete initial-character body decoder from the UI handler. An eighth warning-as-error native fixture consumes and asserts a representative use item plus linked name, skill, cooldown, started/completed quests, all ring families, teleport rocks, monster book, New Year card, area info, terminator, and server timestamp; malformed boolean, truncation, and trailing-byte cases fail closed. The move also sequences multi-field reads explicitly, eliminating compiler-dependent packet cursor ordering.
- Extended the exact decoder fixture through equip, pet, and fourth-job skill records. Corrected three live-v83 defects: equip item EXP is a 32-bit field rather than two 16-bit halves, pet classification covers Cosmic's complete 5000xxx family, and the Cash-inventory terminator is the same zero byte as the SkillInfo marker rather than two bytes. Item-type and cash markers now fail closed. The complete isolated encrypted Cosmic matrix passes while decoding the entire real initial `SET_FIELD`, checking the authoritative name, and requiring zero unread bytes.
- Replaced incomplete inbound movement handling with the complete Cosmic v83 command table (`0`–`22`). Exact decoding now covers absolute, relative, teleport/skill, jump-down, and metadata fragments, retains both jump-down footholds, ignores non-positional metadata safely, and rejects empty/negative counts, unknown commands, truncation, and trailing bytes. A ninth warning-as-error native fixture exercises every command plus exact outbound `MOVE_PLAYER` bytes; the warning-free release WASM build passes.
- Bounded the inbound remote-player and mob movement envelopes around that fragment parser. Positive object IDs, reserved fields, and mob boolean markers now fail closed, while activity, skill, option, and starting-position metadata remain available to the client. The outgoing controlled-mob API now names the Cosmic wire fields and preserves its 13 opaque pre-position bytes. A tenth warning-as-error native fixture covers exact valid bodies, malformed/truncated/trailing rejection, and exact outbound `MOVE_LIFE` bytes; all ten fixtures and a warning-free release WASM build pass.
- Completed the controlled-mob request/acknowledgement slice. `MOVE_MONSTER_RESPONSE (0xF0)` is now a strict concrete handler retaining the echoed movement ID, unsigned MP, and proposed skill fields. Each controlled mob advances one movement ID at a time, retries an unacknowledged ID, ignores wrong/duplicate acknowledgements, and resets on control transfer. Corrected the mislabeled `PLAYER_UPDATE` packet to `PLAYER_MAP_TRANSFER (0xCF)` and moved it into the asynchronous map-load completion callback, which prevents Cosmic from discarding movement while `mapTransitioning` is set. The exact native fixture covers its two-byte packet and the response lifecycle. The isolated encrypted Cosmic matrix now places its disposable character on a 52-mob map, drains the initial field burst, sends the transition completion, receives real control, sends `MOVE_LIFE`, and validates Cosmic's exact `0xF0` object/movement echo; the full relog, reconnect, PIN/PIC, online, and ban matrix remains green.
- Completed strict mist/affected-area lifecycle parsing and the first NX-backed field-area renderer. Cosmic's fixed 39-byte `SPAWN_MIST` and four-byte `REMOVE_MIST` bodies validate fully before field mutation; same-OID replacement, removal-before-construction, map-clear cleanup, server-authoritative lifetime, all four pinned kinds, distinct mob/player Skill NX paths, grouped/direct tile animations, visible-area culling, and a hard draw cap are implemented. All 15 warning-as-error native fixtures and the warning-free release WASM build pass. The generated inventory now reports 69 concrete inbound handlers, 19 no-ops, and 213 missing server-to-client opcodes; representative compatible NX/browser/live rendering remains unproven.
- Replaced `FIELD_OBSTACLE_ONOFF`, its full status list, and all-reset no-ops with strict atomic parsers. Map objects retain their NX `name`, the status list resets before applying its complete snapshot, mode 0 resets/stops, mode 1 animates, and Cosmic's emitted mode 2 is retained without claiming unproven reverse playback. All 16 warning-as-error native fixtures and the warning-free release WASM build pass. The generated inventory now reports 72 concrete inbound handlers, 16 no-ops, and 213 missing; named-object NX/browser/live scenarios remain required.
- Replaced the shifted remote-player spawn parser with a complete off-stage Cosmic DTO, including exact foreign-buff variants, appearance, embedded pets, mount, shop/minigame, chalkboard, rings, New Year cards, and final markers. Embedded pets no longer consume a nonexistent slot byte and retain their full wire unique ID. Character spawn queues now cancel on removal/map clear and replace reused IDs safely. All 17 warning-as-error native fixtures and the warning-free release WASM build pass; a two-client live/browser presence scenario remains required.
- Completed strict inbound pet summon/dismiss, movement, chat, rename, pickup-exception, command, and food-response envelopes. Runtime pets now apply authoritative movement, display chat bubbles, rebuild renamed labels, and replace exception state; command IDs are retained pending a proven NX action mapping. All 18 warning-as-error native fixtures and the warning-free release WASM build pass. The generated inventory advances to 77 concrete handlers, 16 no-ops, and 208 missing; two-client/live pet behavior remains required.
- Completed strict Mystic Door route/spawn/remove envelopes and exact outbound use serialization. Field doors now have owner-keyed replacement/removal/clear behavior, use the v83 NX `tp` artwork, and dispatch field-to-town travel from Up-key proximity with cooldown protection. All 19 warning-as-error native fixtures and the warning-free release WASM build pass. The generated inventory advances to 80 concrete handlers, 16 no-ops, and 205 missing; compatible NX/browser/live proof and town-to-field party-door owner selection remain required.
- Completed the full inbound summon family (`0xAF`–`0xB4`) with strict spawn/remove/move/attack/damage/skill parsers, owner-correlated queued lifecycle state, v83 Skill NX summon animations, authoritative movement, attack/Beholder/damage/death transitions, and summon/target damage numbers. All 20 warning-as-error native fixtures and the warning-free release WASM build pass. The generated inventory advances to 86 concrete handlers, 16 no-ops, and 199 missing; representative NX/two-client/browser proof plus local-owner C2S movement/targeting/attack/damage control remain required.
- Completed the Mystic Door town-return integration from Cosmic's six owner-aligned party-door records. Valid town/target maps and town coordinates persist across field changes, reset on party/session replacement, and drive exact Up-key `USE_DOOR(..., townToField=1)` dispatch; field-to-town remains driven by the dynamic door owner. The warning-free release WASM build passes, while a two-map party live/browser proof remains required.
- Added byte-exact C2S summon movement, attack, and damage serializers matching Cosmic's skipped tick/metadata regions and target position/delay body. A 21st warning-as-error native fixture and the warning-free release WASM build pass; the client-to-server inventory now reports 50 named and 128 missing opcodes. These are a tested boundary for, not a substitute for, the still-open local-owner summon simulator.
- Recovered the official v83 installer without executing it, combined its missing original-v83 WZ files with the operator-supplied Cosmic custom WZ set in `Cosmic-client-main/cosmic-wz`, and converted the complete 16-file set to ignored NX runtime data. `assets/` remained untouched. The generated manifest records exact sizes and SHA-256 hashes, and every NX file has valid `PKG4` magic.
- Corrected the Docker WZ converter for Windows bind mounts by converting on the container filesystem before copying the completed NX output back. This prevents sparse-looking, all-zero memory-mapped outputs. The converter entrypoint also normalizes Windows line endings inside the image.
- Packaged release `public-test-7c37ceb` with registration enabled and the NX manifest, then deployed the isolated production topology locally: only the nginx edge publishes host ports 80/443; registration, asset streaming, game bridge, Cosmic, and MySQL remain private or share the edge loopback namespace.
- Published a temporary trusted HTTPS/WSS smoke-test origin. External checks passed for the portal, security headers, WASM MIME, manifest, registration health, real Cosmic handshake, asset WebSocket delivery, and disposable account create/store/cleanup. A fresh browser session reached `Client ready` with no warning/error console entries, and the registration dialog contains only username and password.
- Re-ran 46 web/transport tests and 24 registration tests on 2026-08-21; both suites pass with one environment-dependent skip each. The currently deployed WASM is the previously verified warning-free build; no client source changed during this deployment pass.
- Reproduced the public first-login crash at the in-game **Log in** button and symbolized it to `UIYesNo`: original-v83 `UI.nx` lacks the post-Chaos `Basic.img/Notice6/box` tile, so the inherited notice layout divided by a zero tile height when Cosmic requested Terms acceptance. Added a generic v83-safe notice fallback with visible, clickable Yes/No and OK controls. Browser evidence shows the Terms prompt renders without errors and advances to the gender prompt after Yes.
- Isolated the login artwork mismatch to the client's documented mixed-version asset contract: gameplay content remains v83, while this client expects post-Chaos login UI nodes. Added a login-only `LoginTheme.nx` overlay containing the beige 800×600 frame, compact credential board, MapleStory logo, v83-style button states, placeholders, and checkbox. The legacy UI remains the fallback, and no gameplay UI package or Cosmic server behavior changed. Added a warning-as-error NX packer, expanded the restricted browser inventory to 17 files, passed 46 web tests, completed a warning-free release WASM build, and deployed the exact clean-source package. Public browser evidence confirms the overlay and `Ver. 0.83` render correctly, the client reaches `Client ready`, and the updated runtime has no warning/error log entries.

## Decisive findings

- The baseline client is substantial but far from all-feature v83 completeness: 45 outgoing opcodes, 55 concrete inbound handlers, and 21 inbound no-ops.
- Moonlight is GMS v214.1 and cannot serve as the protocol/server implementation for this port.
- The operator explicitly authorized NX runtime assets. A complete ignored 16-file v83 NX set plus an isolated post-Chaos login compatibility overlay is staged in `nxFiles/`. Browser delivery and client initialization pass; representative maps/content still require gameplay compatibility proof before Stage 7 can close.
- The baseline game proxy is unsafe for public use because it accepts arbitrary TCP targets.
- The deployment target is the local Docker host behind public edge `99.26.147.74`: Docker now binds host ports 80/443, the LAN target is `10.0.0.57`, and direct router/firewall forwarding remains unproven. The temporary tunnel supplies trusted TLS independently of that NAT route.

## Current blockers

1. **Production content proof:** prove representative initial-map loading, movement, portal transitions, two-client behavior, and the remaining v83 content-ID/UI compatibility cases against the staged NX set before Stage 7/production sign-off.
2. **Permanent secure public origin:** the temporary Quick Tunnel supplies trusted HTTPS/WSS for testing, but its URL and uptime are not durable. `99.26.147.74` is a NAT edge and router-forwarding evidence has not been supplied; plaintext registration/login on the raw HTTP address remains prohibited.
These production blockers do not prevent client, protocol, registration, transport, Docker-design, or test work from continuing.

## Exact next actions

1. Use the public test origin to prove initial-map loading, walking/jumping, a representative portal transition, and mob/player mist rendering/removal against the staged NX set.
2. Prove field-obstacle mode-2 reverse/toggle playback from a compatible named-object corpus and a scripted Cosmic scenario.
3. Implement local-owner summon movement/targeting/attack/damage C2S control, then prove Mystic Door bidirectional party travel in a two-map browser scenario.

## Deployment state

A temporary public **test** deployment is live at
`https://necklace-really-cited-improved.trycloudflare.com/` while the local Docker
stack and Quick Tunnel process remain running. It is not the Stage 9 production
deployment: the URL is ephemeral, the requested raw public-IP route is unproven,
and full-v83 feature/content/operations gates are still open.
