Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b2d4fb9
Define isolated multiple accounts mode
Mat4m0 Aug 13, 2026
00770d6
Add multiple accounts workspace storage
Mat4m0 Aug 13, 2026
f13cf06
Scope saved login to account profiles
Mat4m0 Aug 13, 2026
d661692
Add profile-aware window authority
Mat4m0 Aug 13, 2026
0a3c242
Bind game IPC to registered windows
Mat4m0 Aug 13, 2026
37f9ef3
Isolate window state per game window
Mat4m0 Aug 13, 2026
07554d4
Launch isolated multiple account profiles
Mat4m0 Aug 13, 2026
b55d622
Add multiple accounts setup and picker
Mat4m0 Aug 13, 2026
3a2606e
Manage multiple account profiles
Mat4m0 Aug 13, 2026
7002402
Share and reconcile account resources
Mat4m0 Aug 13, 2026
f4b48d5
Verify multiple account runtime isolation
Mat4m0 Aug 13, 2026
a0270aa
Harden multiple account lifecycle recovery
Mat4m0 Aug 13, 2026
f089020
Align policies with account profile identifiers
Mat4m0 Aug 13, 2026
73c0fe4
Cover account channels in release preload tests
Mat4m0 Aug 13, 2026
0f574b6
Close profile windows without touching destroyed state
Mat4m0 Aug 13, 2026
d163f0a
Cover account namespace in sandbox boundary
Mat4m0 Aug 13, 2026
abaf1c9
Show Multiple Accounts settings pane
Mat4m0 Aug 13, 2026
17a34e5
Make multi-account window launches deterministic
Mat4m0 Aug 13, 2026
50d9fa9
Expose canonical account launch progress
Mat4m0 Aug 13, 2026
97cd769
Polish the Multiple Accounts Hub
Mat4m0 Aug 13, 2026
655f348
Document the Multiple Accounts experience
Mat4m0 Aug 13, 2026
0616880
Document Electron packaging audit exception
Mat4m0 Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Guild Wars Reforged Interface

The interface has one visual contract: Guild Wars supplies atmosphere and
macOS supplies interaction.

## Visual system

- Use the reviewed static Reforged landscape as environmental key art. Do not
load the launcher video in utility windows.
- Use the existing Reforged logo as the only ornamental brand element.
- Use the macOS system font for application controls, labels, forms, and status.
- Use one ember tint (`#b84618`) for selection and primary action. Reserve red
for destructive actions and green for an already-open account.
- Prefer native hierarchy: system-sized controls, semantic checkboxes, sheets,
menus, concise labels, and visible keyboard focus.
- Use translucent material only where it improves separation from key art.
Reduced-transparency mode replaces it with an opaque warm-dark surface.
- Keep motion short and functional. Reduced-motion mode removes nonessential
transitions and animation.

## Multiple Accounts Hub

The Hub uses a 960×700 hidden-inset window with a 640×560 minimum. Its chooser
is content-sized and anchored at bottom-right; compact widths turn it into a
bottom sheet. Account rows scroll within the chooser, so many accounts do not
move the primary action off-screen.

The launch surface answers one question: which accounts should open? Account
administration uses progressive disclosure:

1. The chooser shows selection, launch state, Retry, and the primary action.
2. A row's More menu exposes Edit Account and Archive Account.
3. Modal sheets handle creation and editing.
4. Hub Settings handles archived accounts, permanent deletion, and account-mode
switching.

Player text says **account**. Internal code may retain **profile** for stable
IDs, paths, partitions, and existing domain types.
10 changes: 9 additions & 1 deletion PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ wants the official game without Windows, Wine, or a browser tab.
Some returning players also want a small set of familiar tools. They do not
need a plugin platform.

Some players use more than one Guild Wars account. They can explicitly enable
**Multiple Accounts** mode to open independently controlled accounts in
separate windows. The normal Single Account mode stays the default.

## Product promise

- Keep the official game playable after an unknown ArenaNet update.
Expand All @@ -31,6 +35,8 @@ need a plugin platform.
- Keep host-owned Builds and Teams available without live Tools.
- Give players clear Stable and Beta application-update behavior.
- Keep local data and diagnostics under the player's control.
- Keep Single Account data unchanged when a player enters or leaves Multiple
Accounts mode.
- Keep the project understandable for one new contributor.

## Tools
Expand Down Expand Up @@ -67,7 +73,9 @@ See [Release verification](docs/release-verification.md).
- No Windows or Linux version.
- No redistribution of ArenaNet game binaries.
- No autonomous gameplay.
- No bots, macros, multiboxing support, or trading tools.
- No bots, macros, input broadcasting, synchronized control, or trading tools.
- No cloned application installations or duplicated ArenaNet game downloads
for Multiple Accounts mode.
- No generic memory, packet, command, or plugin API.
- No port of the Windows plugin ABI.
- No gwonmac telemetry from the Mac app.
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ its rules.
| --- | --- |
| How does a player use or recover the app? | [User guide](user-guide.md) |
| Which process owns this work? | [Process model](process-model.md) |
| How do Single and Multiple Accounts mode isolate player data? | [Multiple Accounts](multiple-accounts.md) |
| How do ArenaNet client files and game data update? | [Content pipeline](content-pipeline.md) |
| How does the official client host and certification work? | [WASM host](wasm-host.md) |
| What can diagnostics record and export? | [Diagnostics](diagnostics.md) |
Expand Down
155 changes: 155 additions & 0 deletions docs/multiple-accounts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Multiple Accounts

This document owns the player-data boundary between Single Account mode and
Multiple Accounts mode.

## Product boundary

Single Account mode is the default. It starts Guild Wars directly and keeps
the existing saved login, Guild Wars files, builds, settings, and window state.

Multiple Accounts mode is an explicit opt-in workspace. It starts at the
Account Picker. The player can open one or more independently controlled Guild
Wars accounts. The app does not broadcast input or automate gameplay.

The active mode is fixed for the lifetime of the app process. A mode change
takes effect after a restart.

## Canonical data owners

| Data | Owner |
| --- | --- |
| Verified client, chunks, compatibility artifacts, and skill assets | Shared app infrastructure |
| Application updater and update preferences | Shared app infrastructure |
| Active account mode | Launcher-mode document |
| Single saved login | Existing fixed Keychain items |
| Single Guild Wars files and templates | Default Electron session |
| Single builds and teams | Existing root build library |
| Single window state | Existing root window state |
| Multiple Accounts profile registry | Multiple Accounts workspace |
| Profile saved login | Profile-scoped Keychain items |
| Profile Guild Wars files | Profile persistent Electron session |
| Profile window state | Profile window-state document |
| Shared Multiple Accounts templates | Multiple Accounts shared template library |
| Private Multiple Accounts templates | Profile template library |
| Shared Multiple Accounts builds | Multiple Accounts shared build library |
| Private Multiple Accounts builds | Profile build library |
| Ready, queued, opening, checking, running, and failed status | Main-process runtime store and live window registry |

Single Account mode is not a Multiple Accounts profile. No Multiple Accounts
game window uses the default Electron session or the fixed Single Account
Keychain items.

## Setup and mode transitions

Settings shows Multiple Accounts setup only in the Accounts pane until
the player enables the mode.

Setup creates a staged Multiple Accounts workspace and at least one profile.
The player signs in separately for every profile. Setup can copy templates,
builds, and teams from Single Account mode. This import reads a stable snapshot
and writes a new Multiple Accounts destination. It never moves, links, mirrors,
or later synchronizes the Single Account source.

The app publishes the workspace before it publishes the selected mode. A
cancelled or failed setup leaves Single Account mode selected. An import failure
does not change its source or the previous destination revision.

Returning to Single Account mode preserves the complete Multiple Accounts
workspace. Re-enabling it restores the profiles and libraries. Neither
transition copies data automatically.

## Multiple Accounts sharing

Sharing applies only among Multiple Accounts profiles. Each profile selects
**Shared** or **Private** independently for templates and for builds and teams.

Build libraries remain main-process documents. Main serializes writes per
library and refuses a save whose last-read baseline is stale, so one profile
cannot silently replace another profile's newer shared library.

Every profile keeps an isolated IDBFS mount. A profile that uses Shared
templates receives a working projection of the canonical Multiple Accounts
template library. The app reconciles that projection before launch and after a
clean close or reload. It does not mutate another running renderer's filesystem.

Template reconciliation preserves both contents when two different templates
use the same normalized path. A deletion cannot silently discard a concurrent
edit. The canonical library and each profile checkpoint use revisions, so a
projection can be rebuilt. Private template libraries use the same snapshot
format but never reconcile with another profile.

## Lifecycle and recovery

Every cold Multiple Accounts launch opens the Account Picker with no account
selected. One profile ID maps to at most one live game window. The Hub shows
only bounded runtime language: **Ready**, **Waiting**, **Starting**, **Checking
updated client**, **Open**, and **Needs Attention**. It never describes a loaded
renderer as game-ready.

The app starts selected accounts in a bounded queue and presents every new game
window inactive. It confirms a new client generation with one canary renderer
before it starts the remaining accounts. A canary failure stops the unopened
queue and returns those rows to Ready. An ordinary account failure does not
close or stop another account.

After complete success, the Hub hides and focuses the first selected account
once. Selecting an already-open account shows its existing window. If any
account fails, the Hub stays visible for recovery and successful accounts stay
open. Brand-new windows cascade by 32 pixels where display space permits;
saved window positions take precedence after the first launch.

A renderer gets one automatic recovery per deliberate launch. Recovery keeps
the same profile ownership and does not affect other accounts. A second crash
becomes a persistent Needs Attention row with Retry. The Hub remains
recoverable from Dock activation, and Settings is available with Command-,.

Closing a profile flushes its filesystem and closes only its sockets. Quitting
the app flushes all live profile filesystems in parallel. After an application
update or process crash, Multiple Accounts mode returns to the Account Picker.
It does not reopen profiles automatically.

If the selected mode or profile registry is missing, corrupt, or from an
unsupported future format, startup does not guess at its contents. It offers to
preserve the unreadable document and restart in Single Account mode. This
recovery quarantines the unreadable document and changes the launcher-mode
document only; it does not open, copy, or clear either mode's player data or
Keychain items.

Archive is the normal account-removal action. It preserves the profile session,
private libraries, and Keychain items. Permanent deletion is a separate,
confirmed action in Hub Settings. It never removes a shared library or Single
Account data.

## Hub interaction

The Hub is a focused chooser, not an account dashboard. Rows use native
checkbox semantics and the entire non-action area is clickable. The primary
action reflects the selection: Open, Open _Account_, Show _Account_, Retry
_Account_, or Open _n_ Accounts. Edit and Archive live in each row's More menu.

New and Edit Account are modal sheets. Player-facing sharing choices are
**Builds and teams** and **In-game templates**, each either **Shared between
Multiple Accounts** or **Separate for this account**. Shared is the default.
The sheet states that login, game settings, screenshots, chat logs, and Single
Account data are never shared.

Mode switching, archived-account restoration, and permanent deletion live in
Settings rather than the launch surface. Game Settings → Accounts carries the
same mode explanation and Return to Single Account action.

Reset actions name their scope. A Single Account saved-files reset clears only
the default session. A profile reset clears only the selected persistent
session. Clearing downloaded game data affects the shared app infrastructure
and does not clear player files or saved login.

## Security and privacy

The window registry derives profile authority from the trusted sender. A
renderer cannot choose a profile ID, native path, Electron partition, Keychain
item, or socket owner.

The Account Picker cannot access game sockets, saved login, player files, or
build writes. Diagnostics use ephemeral window identifiers. They do not record
profile names, stable profile IDs, account identifiers, credentials, template
contents, or game traffic.
46 changes: 35 additions & 11 deletions docs/process-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ those facts.
```text
Electron main process
application lifecycle
active Single or Multiple Accounts mode
game-window registry
ArenaNet client and content updates
verified client generations and rollback
native chunk storage
Expand All @@ -30,7 +32,7 @@ Sandboxed preload
| frozen window.gwNative capabilities
v
Chromium renderer
launcher and settings
account picker, launcher, and settings
Guild Wars Module host
input and presentation
required Core features
Expand All @@ -47,6 +49,23 @@ routes.
The preload exposes one frozen `window.gwNative` object. It transports
capabilities. It does not own game rules or persistence rules.

## Account modes

The process captures one account mode at startup. It does not switch storage
owners while it runs.

Single Account mode uses the existing default Electron session, saved-login
items, build library, and window state. Multiple Accounts mode does not treat
Single Account mode as a profile. Each Multiple Accounts profile uses a
non-default persistent Electron session and profile-scoped native stores.

Both modes use the same verified client generation, chunk store, derived
client artifacts, and application updater. These stores contain rebuildable
client infrastructure. They do not contain player account state.

[Multiple Accounts](multiple-accounts.md) owns the complete data and transition
contract.

## Client generation ownership

Three types have different jobs:
Expand Down Expand Up @@ -163,10 +182,12 @@ The main process owns these native stores:
- verified ArenaNet client generations;
- the content-addressed chunk store;
- bounded diagnostics files;
- two saved-login items in Apple's Data Protection Keychain.
- Single Account and profile-scoped saved-login items in Apple's Data
Protection Keychain.

The renderer owns the Guild Wars IDBFS mount under the `gw://app` origin. This
mount contains game preferences, templates, screenshots, and chat logs.
Each game renderer owns one Guild Wars IDBFS mount under its isolated
`gw://app` session. The mount contains game preferences, templates,
screenshots, and chat logs. Two renderers do not mount the same browser store.

Derived WASM modules and caches are rebuildable. They are never certification
authority.
Expand All @@ -176,9 +197,10 @@ authority.
The Release, Preview, and signed Development identities use separate Keychain
authority. Each identity can read only its own provisioned items.

One item stores the ArenaNet user name and password. One item stores the Steam
access token and expiry. A read failure does not delete an item. The game can
continue to its login screen when an item is unavailable.
Each account scope has one item for the ArenaNet user name and password and one
item for the Steam access token and expiry. The existing fixed items belong
only to Single Account mode. A read failure does not delete an item. The game
can continue to its login screen when an item is unavailable.

Unpackaged and ordinary local builds use volatile storage. They do not claim a
provisioned Keychain item. There is no file or `safeStorage` fallback.
Expand Down Expand Up @@ -209,11 +231,13 @@ rollback procedures.
## Application lifecycle

The app acquires a single-instance lock before it reads or cleans profile-owned
files. A second launch focuses the existing window and exits.
files. In Single Account mode, a second launch focuses the game. In Multiple
Accounts mode, it opens or focuses the Account Picker.

Closing the game window quits the application. Quit follows one bounded cleanup
path. It saves the renderer filesystem, closes sockets, stops background work,
flushes diagnostics, and exits.
Closing the Single Account game window quits the application. Closing one
Multiple Accounts game window closes only that profile. Application quit saves
all live renderer filesystems in parallel, closes sockets, stops background
work, flushes diagnostics, and exits through one bounded cleanup path.

Main-to-renderer events stop after the window or its `webContents` is destroyed.
The app attempts renderer recovery only after unexpected renderer loss. It does
Expand Down
33 changes: 33 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,39 @@ the client files, asks how to store game data, and starts Guild Wars. Later
starts use verified cached data when possible. Login and online play still need
ArenaNet.

## Single and Multiple Accounts

Single Account mode is the default. It starts Guild Wars directly and keeps
the login, templates, builds, settings, and window state that you already use.

Open **Settings → Accounts**, name the first account, choose its Shared or
Private libraries, and select **Enable and Restart…** to create a separate
Multiple Accounts workspace. Every later Multiple Accounts start opens the
Account Picker with nothing preselected. Select one or more accounts and choose
**Open**. If an account is already running, the action changes to **Show**.

Each account signs in separately and keeps separate Guild Wars preferences,
screenshots, chat logs, saved login, and window position. Profiles can use the
shared Multiple Accounts template and build libraries or private libraries.

Setup can copy templates, builds, and teams from Single Account mode. This is a
one-time copy. The originals remain in Single Account mode. Later changes do
not synchronize between the two modes.

Use Command-, in the Account Picker to open Settings, then choose **Return to
Single Account…** to change the next launch. Your accounts and saved logins stay
available if you restore Multiple Accounts from **Settings → Accounts** later. The modes
share verified game downloads, so creating an account does not download another
complete copy of Guild Wars.

Use **New Account…** in the Account Picker to add accounts. The row's More menu
contains **Edit Account…** and **Archive Account**; close its game window before
changing sharing. Archive keeps all account data and saved login. Hub Settings
can restore it or permanently delete it after a native confirmation.

Every account window is independently controlled. The app does not broadcast
keyboard, mouse, or controller input between windows.

A local source build has a temporary identity. It does not share saved-login
access with the published Release app.

Expand Down
5 changes: 5 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ auditConfig:
ignoreGhsas:
- GHSA-w3rx-r6r6-pgpr
- GHSA-5p2g-fcmc-qvqq
# Electron Forge 7 reaches extract-zip only through Electron Packager's
# extraction of the trusted Electron distribution during packaging. The
# application never passes user-controlled archives to this dependency,
# and no patched extract-zip release currently exists.
- GHSA-jmr9-qjv8-65gv
# The affected esbuild serve-directory path exists only on Windows. Owned
# automation runs on macOS/Linux, and fontless invokes only transform.
- GHSA-g7r4-m6w7-qqqr
5 changes: 5 additions & 0 deletions scripts/copy-renderer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import path from "node:path";
// editor and OS files part of the build, so two clean checkouts could package
// different applications. A new asset must be reviewed here.
const ASSETS = [
"accounts.css",
"accounts.html",
"favicon.ico",
"favicon.png",
"fonts/COPYING-QUALITYPE",
Expand All @@ -33,6 +35,9 @@ const ASSETS = [
const SHARED_ASSETS = [
["src/shared/ui/tokens.css", "ui/tokens.css"],
["src/shared/ui/components.css", "ui/components.css"],
// The website and Hub use one reviewed static painting. Keep it canonical in
// the website package and copy it into the renderer at build time.
["apps/website/public/bg-reforged.jpg", "images/bg-reforged.jpg"],
];

const dest = path.resolve("build/renderer");
Expand Down
Loading
Loading