# Launcher Self-Update

MoonWell Launcher uses `auto_updater` 1.0.0, backed by WinSparkle 0.8.1, to
update installed Windows launchers from an AppCast feed. This is independent of
the manifest-based World of Warcraft client synchronization.

The launcher performs one background update check after its window is visible.
WinSparkle shows its native prompt only when a newer version is available.
Missing configuration, network failures, and malformed feeds do not block the
launcher. Periodic WinSparkle checks are disabled.

## Build Configuration

Pass the public feed URL at build time:

```powershell
flutter build windows `
  --dart-define=MOONWELL_API_BASE_URL=https://host `
  --dart-define=MOONWELL_APPCAST_URL=https://host/launcher/updates/appcast.xml
```

The feed URL must be an absolute HTTPS URL. Omit
`MOONWELL_APPCAST_URL` to disable self-update, including for ordinary local
development.

## Signing Keys

WinSparkle verifies installer artifacts with a DSA signature. The public key is
committed at `windows/runner/resources/dsa_pub.pem` and embedded as the
`DSAPub` resource. The corresponding private key is local signing material at
`.moonwell_signing/dsa_priv.pem`; it is ignored by Git and must be backed up to
the project secret store before publishing the first update.

Never commit or upload the private key. Losing it prevents existing
installations from accepting future updates. Rotating it requires shipping an
ordinary installer containing the replacement public key before publishing
artifacts signed by the replacement private key.

The AppCast signature is not Authenticode. Publicly trusted Authenticode
certificates and managed signing services are paid products, so MoonWell does
not currently Authenticode-sign the installer. Windows can therefore display
an unknown-publisher or SmartScreen warning. A free self-signed Authenticode
certificate would not establish trust on player machines.

## Version and AppCast Contract

Increase the `X.Y.Z` version in `pubspec.yaml` for every launcher release. A
build-number-only change such as `1.0.0+2` is not sufficient because WinSparkle
compares the Windows product version. Pass the same `X.Y.Z` value to Inno Setup:

```powershell
ISCC.exe /DMyAppVersion=1.1.0 installer\moonwell_launcher.iss
```

The resulting artifact is
`build/installer/moonwell_launcher_1.1.0_windows_setup.exe`.

Use a public AppCast with absolute URLs:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle">
  <channel>
    <title>MoonWell Launcher</title>
    <description>MoonWell Launcher updates</description>
    <language>ru</language>
    <item>
      <title>MoonWell Launcher 1.1.0</title>
      <sparkle:releaseNotesLink>https://host/launcher/updates/1.1.0/release-notes.html</sparkle:releaseNotesLink>
      <pubDate>Sun, 21 Jun 2026 12:00:00 +0400</pubDate>
      <enclosure
          url="https://host/launcher/updates/1.1.0/moonwell_launcher_1.1.0_windows_setup.exe"
          sparkle:version="1.1.0"
          sparkle:os="windows"
          sparkle:dsaSignature="SIGNATURE_FROM_SIGN_UPDATE"
          length="ACTUAL_INSTALLER_SIZE_IN_BYTES"
          type="application/octet-stream" />
    </item>
  </channel>
</rss>
```

The installer URL and release-notes URL must be absolute. The publication host
must serve the feed, notes, and installer without authentication or redirects
to an authenticated endpoint.

## Manual Release

Perform Windows signing on the Windows release machine because the package
invokes WinSparkle's Windows signing utility.

1. Update `pubspec.yaml` to the new `X.Y.Z+N` version.
2. Run `flutter pub get`, formatting, analysis, and all tests.
3. Build Windows with both production `--dart-define` values.
4. Compile the installer with `ISCC.exe /DMyAppVersion=X.Y.Z`.
5. Make the private key available outside the repository and sign the artifact:

   ```powershell
   dart run auto_updater:sign_update `
     build\installer\moonwell_launcher_X.Y.Z_windows_setup.exe `
     C:\secure-path\dsa_priv.pem
   ```

6. Copy the emitted `sparkle:dsaSignature` into the enclosure. The package's
   Windows helper reports `length="0"`; replace it with the actual value from:

   ```powershell
   (Get-Item build\installer\moonwell_launcher_X.Y.Z_windows_setup.exe).Length
   ```

7. Upload the versioned installer and release notes first.
8. Download the hosted installer, verify its byte length, and test its DSA
   signature before changing the feed.
9. Publish `appcast.xml` last so clients never observe an incomplete release.

Keep the preceding installer available for rollback and support. Rollback is a
new release with a product version higher than the faulty release; WinSparkle
does not install a lower version as an update.

## Release Verification

Test from an installed older version on a clean Windows x64 machine:

- offline or invalid feed: launcher remains usable and writes a log entry
- same or older feed version: no update prompt
- valid newer release: prompt, download, launcher exit, install, and relaunch
- modified installer or invalid DSA signature: WinSparkle rejects the update
- launcher session and selected game directory remain after installation

Watch the debug output for Flutter platform-channel thread warnings during the
test. `auto_updater` has an open Windows callback-thread issue; do not release
if it reproduces. Patch and pin `auto_updater_windows` so native callbacks are
marshalled onto Flutter's platform thread, then repeat the complete update test.
