Files
moonwell-launcher/docs/launcher_web_api_spec.md
T
sindoring f834c5fa86 Synchronize the client and select installed language packs (#3)
The launcher synchronizes the current native client, retains manifest-listed ruRU archives and launches the game with WOW_LOCALE=auto. Includes the prepared cleanup of obsolete Extentions directories in both client roots while preserving manifest-required files and unrelated user state.

Version: 1.0.7+10. Deployment now judges native OpenSSL commands by their exit codes while retaining key and signature verification.

Verified on Windows through FVM: full format/analyze, all 76 tests, production dry run, x64 runtime imports and DSA installer signature. The original dirty checkout was preserved. Publication follows the localized client release.
2026-10-10 16:50:35 +03:00

13 KiB
Raw Blame History

Moonwell Launcher Web API Spec

Scope

This document specifies the current launcher behavior for:

  • authentication against the Moonwell Web API
  • installation directory selection
  • local file scan and hash calculation
  • manifest-based synchronization
  • launch and game authorization for the Rust MoonWell 1.12.1 client

The launcher no longer uses S3/MinIO directly. All server interaction goes through the Web API.

Configuration

The launcher API base URL is provided via:

  • MOONWELL_API_BASE_URL
  • optional MOONWELL_AUTH_ADDRESS for the game auth endpoint (host[:port])

Current integration mode is compile-time:

  • flutter run -d windows --dart-define=MOONWELL_API_BASE_URL=https://host
  • flutter build windows --dart-define=MOONWELL_API_BASE_URL=https://host

API Flow

The launcher uses these endpoints from openapi.json:

  1. POST /api/launcher/login
  2. POST /api/launcher/register
  3. GET /api/launcher/manifest
  4. GET /api/launcher/download/{path}
  5. GET /api/launcher/news
  6. GET /api/launcher/realms
  7. GET /api/launcher/account
  8. POST /api/launcher/game-ticket

Registration sequence:

  1. User opens the registration tab and provides username, email, password, password_confirmation, optional invite_code, and accepts terms.
  2. Launcher sends the payload to POST /api/launcher/register.
  3. After a successful 201 response, the form returns to the login tab and displays the API success message.
  4. Validation and invite errors returned by the API are shown in the form.

Authentication sequence:

  1. User enters launcher credentials.
  2. Launcher requests a bearer token from POST /api/launcher/login.
  3. Launcher requests the manifest from GET /api/launcher/manifest.
  4. Successful login persists LauncherSession locally.
  5. On next launcher start, saved session is reused to fetch manifest again.
  6. LauncherSession and ClientManifest are passed into the home screen.
  7. If an installation directory is saved, synchronization starts automatically with the fetched manifest.
  8. While the launcher is open, it refreshes the manifest every five minutes.
  9. A server build hash that differs from the verified local build hash starts synchronization automatically.

Logout sequence:

  1. User presses Logout.
  2. Launcher cancels any active sync.
  3. Launcher clears the locally persisted LauncherSession.
  4. Launcher returns to the login screen.

File download sequence:

  1. Launcher resolves files that are missing, stale, or changed.
  2. For each required file, launcher calls GET /api/launcher/download/{path} with bearer auth.
  3. API returns JSON with a temporary presigned url and expires_in.
  4. Launcher downloads the file directly from storage using the presigned URL.
  5. Download is written into *.moonwell.part.
  6. Downloaded file is verified against manifest sha256.
  7. Temporary file replaces the destination file only after successful verify.

News sequence:

  1. After the launcher reaches the authenticated home screen, it requests GET /api/launcher/news.
  2. The response payload is read from the top-level data array.
  3. Each news item provides id, title, body, optional image_url, and created_at.
  4. News failures do not block patching or play flow; the launcher shows a local error state only inside the news panel.
  5. Pressing a news item opens <MOONWELL_API_BASE_URL>/news/{id} in the system browser.

Realm status sequence:

  1. After the authenticated home screen opens, the launcher requests GET /api/launcher/realms.
  2. The first item in the top-level data array supplies the realm name, online state, and game build shown in the sync panel.
  3. Realm information is refreshed every minute while the launcher remains open.
  4. A failed refresh keeps the last known realm state and is retried on the next interval.

Account sequence:

  1. After the authenticated home screen opens, the launcher requests GET /api/launcher/account with bearer authentication.
  2. Only username is stored in launcher UI state and displayed in the account menu.
  3. The response balance field is intentionally ignored and never displayed.
  4. Account metadata failures do not block patching or launching the game.

Game authorization sequence:

  1. Immediately before starting the game, the launcher calls POST /api/launcher/game-ticket with bearer authentication and { "client_build": 5875 } in the JSON body.
  2. The API returns account, a 16-character A-Z0-9 single-use ticket, and expires_at. The account is 3–16 uppercase A-Z0-9 characters. The ticket must remain valid for at least five more seconds, including at process start.
  3. Issuing a ticket atomically revokes any previous unused game ticket for the same account. Production lifetime must not exceed 60 seconds.
  4. The launcher never persists or logs the ticket.
  5. The auth server uses the ticket as the temporary SRP password and consumes it atomically after a successful logon proof.

Installation Directory Rules

The user selects a single installation root directory.

All paths in the manifest are treated as relative to that root.

Path safety rules:

  • backslashes are normalized to /
  • leading ./ is removed
  • absolute paths are rejected
  • path traversal via .. is rejected

Ignored Directories

Unmanaged files in these directories outside Data/ and WoW/Data/ are preserved and excluded from verification:

  • Cache
  • Errors
  • Logs
  • Screenshots
  • WTF
  • benilla-config
  • .moonwell_launcher
  • Interface/AddOns

Files under these directories:

  • are not included in local scan
  • do not participate in local buildHash
  • are not compared against server manifest

Inside Data/ and WoW/Data/, the manifest is authoritative for every file, including nested files whose names contain wtf, cache, or addons. The exclusions above do not exempt files inside Data from cleanup.

Local Scan

For every existing manifest file in the installation directory, the launcher computes:

  • normalized relative path
  • file size in bytes
  • file sha256

The result is stored as a ClientInstallationSnapshot. Paths are always relative to the selected installation root, including files in nested directories.

The scan separately collects stale paths without hashing their contents:

  • every file absent from the manifest in Data/ and WoW/Data/
  • every file absent from the manifest in obsolete Extentions/ and WoW/Extentions/ directories, including unused subdirectories and the Extentions directory itself when no manifest files require it
  • WoW.exe and *.dll absent from the manifest directly in the installation root or WoW/ (binary names are matched without case sensitivity)
  • unused Data subdirectories, ordered after their children; the Data roots and directories needed by manifest files are retained

These paths do not contribute to buildHash, but prevent sync from completing until cleanup and final verification succeed. Other unmanaged files are preserved. On Windows, manifest membership checks for cleanup ignore path letter case. Directory links are not traversed during cleanup. Links absent from the manifest inside Data are removed as links, and deletion through a parent pointing outside the installation is rejected. Directory deletion is never recursive.

Hash Cache

To accelerate repeated scans, the launcher stores a local hash cache in:

  • <install-root>/.moonwell_launcher/hash_cache.json

This metadata directory is excluded from verification.

Each cache entry is keyed by normalized relative path and stores:

  • size
  • modifiedMs
  • sha256

Cache reuse rule:

  • if path, size, and modifiedMs still match, the cached sha256 is reused
  • otherwise sha256 is recomputed and the cache entry is replaced

The cache is an optimization only. The server remains the source of truth via the manifest comparison.

Diagnostics

During sync, the launcher writes a log file to:

  • <install-root>/.moonwell_launcher/launcher.log

If an installation directory is not available yet, the launcher falls back to a temporary system log directory.

For failed downloads:

  • HTTP and storage download failures are logged with file path and status code
  • checksum mismatches log expected and actual size and sha256
  • invalid payloads are preserved as *.moonwell.part.failed for inspection

buildHash Algorithm

buildHash is deterministic and calculated identically for local snapshot and remote manifest.

For each file entry, the launcher builds a canonical string:

<normalized-path>:<size>:<sha256-lowercase>

Then:

  1. sort all canonical strings lexicographically
  2. join them with \n
  3. compute sha256 of the resulting UTF-8 payload

This means file ordering in the manifest does not affect buildHash.

Sync Algorithm

The current sync flow is:

  1. Load manifest.
  2. Scan local installation.
  3. Compute local buildHash.
  4. Compare local files to manifest files by path, size, and sha256.
  5. Identify stale paths in the cleanup areas described above.
  6. If local buildHash matches server buildHash and there are no stale files and no mismatches, finish successfully.
  7. Delete stale paths, including unused Data subdirectories after their children.
  8. Download changed and missing files.
  9. Verify every downloaded file by sha256.
  10. Re-scan the installation.
  11. Recompute local buildHash.
  12. Fail if final snapshot still differs from manifest or stale paths remain.

Pause and Resume Semantics

Pause is implemented as cooperative cancellation:

  • the current sync stream is cancelled
  • the next sync starts a fresh comparison from current disk state
  • already replaced files remain valid
  • partial *.moonwell.part files are removed before the next download attempt

Launch Behavior

When the user presses Play:

  1. launcher requests a single-use game ticket for build 5875
  2. launcher resolves <install-root>/MoonWell.exe on Windows, <install-root>/MoonWell.app/Contents/MacOS/launch on macOS, or <install-root>/MoonWell on Linux
  3. launcher reads the auth address from MOONWELL_AUTH_ADDRESS when configured, otherwise from set realmlist <host[:port]> in realmlist.wtf; this file is never modified and the default auth port is 3724
  4. launcher resolves the player's data in Data/, falling back to WoW/Data/, and verifies that the ticket still has at least five seconds of validity
  5. launcher starts MoonWell with working directory set to the installation root, no authorization command-line arguments, and these child environment values:
    • WOW_USER=<account>
    • WOW_PASS=<ticket>
    • WOW_HOST=<auth-address>
    • WOW_DATA=<absolute-data-directory>
    • BENILLA_HOME=<install-root>/benilla-config
    • WOW_LOCALE=auto (select the installed Data/ruRU or WoW/Data/ruRU language pack, otherwise English; override any locale inherited from the launcher's parent)
  6. launcher retains a process handle; while the process is alive, it disables repeated game launches and client synchronization
  7. when the process exits, the launcher returns to the ready-to-play state

Launching never edits WTF/Config.wtf or clears Cache. macOS executes the bundle launcher directly; that script execs MoonWell so the handle follows the game rather than an open helper. Settings remain outside the .app. If the executable, data directory, or auth endpoint is missing or invalid, launch fails with an error. A consumed ticket cannot authenticate again; close the client and press Play for a fresh ticket after a session ends.

Language archives are ordinary client-manifest entries, including their nested paths, sizes and SHA-256 hashes. Synchronization verifies and retains required ruRU files and removes obsolete archives under the same manifest-authoritative Data policy.

Error Behavior

The launcher surfaces errors for:

  • invalid API configuration
  • authentication failure
  • registration validation or server failure
  • manifest load failure
  • path traversal attempts
  • checksum mismatch after download
  • missing downloaded temp files before verification
  • final verification mismatch after update
  • missing MoonWell executable, WoW data, or game auth endpoint
  • invalid, expired, or unavailable game ticket

Tested Invariants

The automated tests cover:

  • deterministic buildHash
  • path normalization used by buildHash
  • preservation of user state and unrelated unmanaged files during cleanup
  • removal of unmanaged Data files, unused subdirectories, WoW.exe, DLLs, and obsolete Extentions directories
  • preservation of manifest-listed DLLs and WoW.exe
  • cache cleanup behavior
  • safe path resolution
  • sync flow with fully up-to-date client
  • sync flow with stale files and changed files
  • cleanup when all manifest files are already up to date
  • rejection of stale paths remaining after final verification