Repository navigation
Update the application
An application can find its new versions, download them, and install them, and it installs only what your key signed. In this guide, a notes application at version 1.0.0 finds version 1.1.0, shows what changed, and downloads it.

A release is a directory on any web server: the package of every platform, a manifest.json that
names them with their size and SHA-256, and manifest.json.sig, the Ed25519 signature of the
manifest. The application downloads the manifest and the signature, checks the signature with the
public key that it was built with, and only then believes the manifest. It then checks the size and
the SHA-256 of the package that it downloads. HTTPS alone isn't trusted, so whoever controls the
server, or a cache in front of it, can't make the application install a file that your private key
didn't sign for.
-
Generate the keys once, for the life of the application:
./gradlew generateUpdateKeys
The task prints a public key and a private key. Keep the private key in a secret of your CI and never in the repository. A new pair locks the installed versions out of later updates, because they trust only the public key that they were built with.
-
Tell the plugin where the manifest lives, and give it the public key:
lwjwae { updates { manifestUrl = "https://example.com/notes/manifest.json" publicKey = "MCowBQYDK2VwAyEA..." notes = "Notes sync between your computers." } }The plugin writes both into the application, where
application.updater()finds them. The version that runs is the version of the project. -
Build a release. On each platform,
packageUpdateputs its package intobuild/lwjwae/update, named after the platform, such aslinux-x64.AppImage. With the files of every platform in one directory,updateManifestwrites the manifest and signs it with the private key from theLWJWAE_UPDATE_PRIVATE_KEYenvironment variable:./gradlew packageUpdate LWJWAE_UPDATE_PRIVATE_KEY=... ./gradlew updateManifest
-
Upload the directory to the manifest URL. The release is out.
minimumVersion = "1.0.0" in updates {} marks every older version, which then sees the update as
mandatory. What the application does with that is up to you.
validFor = Duration.ofDays(30) in updates {} makes the manifest expire 30 days after
updateManifest signs it, and the application refuses it after that. Someone who serves an old
manifest can then hold the application on an old version for that long at most. Sign the release
again before the time runs out; without validFor, the manifest never expires.
Updater updater = application.updater();
application.bind("currentVersion", Void.class, _ -> updater.currentVersion());
application.bind(
"checkForUpdate",
Void.class,
_ ->
updater
.check()
.join()
.map(update -> Map.of("version", update.version(), "notes", update.notes()))
.orElse(null));
application.bind(
"download",
Void.class,
_ -> {
Update update = updater.check().join().orElseThrow();
downloaded.set(
updater
.download(update, fraction -> application.emit("downloaded", fraction))
.join());
return update.installable();
});
application.bind(
"restart",
Void.class,
_ -> {
updater.installAndRestart(downloaded.get());
return null;
});| Method | What it does |
|---|---|
check() |
Downloads and verifies the manifest, and returns the Update for this platform if its version is newer than the running one. |
download(update, progress) |
Downloads the package, reports from 0 to 1, and checks its size and SHA-256. |
installAndRestart(downloaded) |
Starts the installer, which waits for the application to quit, puts the new version in place, and starts it. Then quits the application. |
An Update has the version, the notes, whether it is mandatory, and whether this copy is
installable. A version that isn't newer than the running one is no update, so a manifest can't
take the application back.
The guide application serves a signed release from the same machine, so that it runs without a web server. A real application needs nothing of the kind: it gets the URL and the key from the plugin.
lwjwae.updates does the same in two calls, once the application grants the page
PageCapability.UPDATES in ApplicationParameters.pageCapabilities:
const update = await lwjwae.updates.check();
if (update?.installable) {
await lwjwae.updates.install({ onProgress: (fraction) => bar.value = fraction });
}check resolves to { version, notes, mandatory, installable, size }, or to null when there is
no update. install downloads the update and installs it, which ends the application. Both reject
with no-update, not-installable, update-rejected, or download-failed.
| Platform | What gets installed | Installable when |
|---|---|---|
| Windows | The .msi of the new version, with msiexec /passive. |
The application runs as a native executable. |
| macOS | The .app bundle of the new version, signed by the same team. |
The bundle is in a directory that the user can write. |
| Linux | The AppImage of the new version, in place of the running one. | The application runs as an AppImage. A .deb or an Arch package updates through its package manager, so there check finds the update, but it isn't installable. |
-
checkfails withUpdateRejectedExceptionfor a signature that isn't the one of your key or a manifest that isn't one, and withUpdateDownloadFailedExceptionwhen either can't be downloaded. - Under
./gradlew run, the application isn't a package, socheckanddownloadwork, but no update is installable. - The taskbar can show the download too:
updater.download(update, application::progress). See Show progress in the taskbar.
Tutorial
- Set up the project
- Open a window
- Call Java from the page
- Send events from Java to the page
- Draw your own title bar
- Export notes through a native dialog
- Keep the application in the tray
- Show notifications
- Give the application an icon and a name
- Build a native executable
- Package the application
Guides
- Pass objects through the bridge
- Stream bytes between the page and Java
- Open several windows
- Place and control a window
- Make a transparent window
- Add menus
- Register global shortcuts
- Show progress in the taskbar
- Use the clipboard
- Handle dropped files
- Zoom the page
- Set the window icon
- Follow the theme of the desktop
- Use cookies
- Store data in SQLite
- Keep secrets
- Show dialogs
- Run a single instance
- Open your own links and files
- Update the application
- Handle page permissions
- Handle links and page loads
Reference