Major Changes
-
Refactor(updater):
downloadUpdate()andUpdateCheckResult.downloadPromiseresolve with aDownloadExecutorResultobject instead of a positionalArray<string>#101640d47ef5@claudeBREAKING CHANGE: the promise returned by
AppUpdater.downloadUpdate()(andUpdateCheckResult.downloadPromisewhenautoDownloadis enabled) now resolves with{ updateFile, packageFile? }instead of[updateFile]/[updateFile, packageFile]. The array shape depended on element order to tell the installer apart from the optional NSIS web-installer package; the newDownloadExecutorResulttype (exported fromelectron-updater) names both files.UpdateDownloadedEventadditionally gains an optionalpackageFilefield for web installers.// Before (v6) const files = await autoUpdater.downloadUpdate() const installer = files[0] const webInstallerPackage = files[1] // only for NSIS web installers // After (v7) const { updateFile, packageFile } = await autoUpdater.downloadUpdate()
The same applies to the result of
checkForUpdates():// Before (v6) const result = await autoUpdater.checkForUpdates() const [installer] = (await result?.downloadPromise) ?? [] // After (v7) const result = await autoUpdater.checkForUpdates() const download = await result?.downloadPromise const installer = download?.updateFile
The underlying cache-consistency fix from #10098 already produced this object internally; this change stops converting it back to an array at the public API boundary.
Minor Changes
-
Feat: v27 upgrade guardrails: make every breaking change self-announcing
#10182318f6fb@mmaietta -
Feat(updater): improve PowerShell invocation reliability for Windows code-signature verification
#9764df1bce3@mmaietta -
Feat(security): signed update manifests (Ed25519) with trust lists and multi-signature manifests
#9877d45536f@mmaiettaOptional Ed25519 signing of auto-update manifests (
latest*.yml). When signing keys are configured
(updateManifest.signingKey/signingKeyFilein config, orELECTRON_BUILDER_UPDATE_SIGN_KEY/ELECTRON_BUILDER_UPDATE_SIGN_KEY_FILE
env vars), each manifest is signed over its integrity-critical fields and the matching public keys are
embedded intoapp-update.yml(both resolved from the same keys on the platform packager, so signing and
embedding cannot disagree). electron-updater verifies the signature before downloading and refuses to
update on tamper/missing-signature (fail-closed). Opt-in: when no public key is configured, verification is
skipped with a one-time warning. New CLI:electron-builder create-update-key(prints the public key and its key id).Key rotation without a flag day: an install trusts a list of public keys (
updateManifestPublicKeyis a
string or an array;updateManifest.publicKey,signingKeyandsigningKeyFileaccept arrays, a PEM value may
hold several concatenated keys, andELECTRON_BUILDER_UPDATE_SIGN_KEY_FILEaccepts several paths joined with
the OS path delimiter), and a manifest may carry several signatures (signatures: [{ keyId, signature }],
one per signing key, next to the legacysignatureof the first key). A manifest is accepted when any trusted
key validates any of its signatures, so a release signed with[old, new]verifies on installs that trust
either.AppUpdater.updateManifestPublicKeyaccepts a string or an array. A build-time warning flags an
explicitpublicKeylist that contains none of the signing keys.Gating of the Linux package-manager signature-bypass flags landed separately as
AppUpdater.allowUnverifiedLinuxPackages(#9990).
Patch Changes
-
Fix: make multi-range differential downloads work on real servers. Three independent defects made
downloadUpdatefall back to a full download withResponse ends without calling any handlers:#101928e22767@yi-geDataSplitteronly recognised CRLF. Some CDNs answermultipart/byterangeswith bare LF line endings, so no part was ever split and the whole response accumulated in memory. Header lists now end at whichever of\r\n\r\n/\n\ncomes first, and the<EOL>--boundaryseparator size follows the line ending the server actually uses.- A header-list terminator split across two chunks was never found: only the new chunk was searched, the buffered bytes never were, so the parser locked onto the next part's header instead. Only the last few bytes of an unfinished header list are now carried over and searched together with the next chunk, instead of accumulating the whole list.
- The 10s watchdog armed when a batch response ends was never disarmed after that batch succeeded. With more than 1000 operations (several range requests) it failed the whole download whenever a later batch took longer than the grace period.
-
Fix: preserve fractional staged rollout percentages
#1011423bccfb@OskarEichler -
Fix: strip
PSModulePathfrom the PowerShell child environment case-insensitively during Windows code-signature verification. Windows environment variable names are case-insensitive but JS object keys are not, so a differently-cased key (e.g.PSMODULEPATH) could previously survive into the spawned PowerShell process.#1015961bd5f6@claude -
Fix: keep the cached blockmap consistent with the cached installer. A download round that did not produce a new blockmap (e.g. the differential download was skipped because the cached installer was evicted) now removes the cached
current.blockmapinstead of leaving a stale one next to the freshly cached file, which poisoned the next differential download and surfaced as a generic sha512 checksum mismatch before falling back to a full download (#10097). Leftover pending blockmaps from previous update rounds are also cleared before a fresh download. sha512-mismatch logging now distinguishes a differential download that failed against stale/corrupt cached inputs (including whether the old blockmap came from the local cache or the server) from a genuine checksum failure of a fully downloaded file.#100989306160@claude