Update now: download, verify and install a release in one click (#1118)

Where SolidCraft can replace itself safely, the Update window gets Update now:
- AppImage: the new AppImage is downloaded next to the old one, verified and renamed over
  it; Restart now starts it as SolidCraft exits.
- Windows MSI: the verified .msi starts (msiexec /i … /passive) once SolidCraft has closed,
  after the usual questions about unsaved designs.
- Windows portable zip / standalone exe: the running solidcraft.exe is renamed to .old, the
  new one takes its name (solidcraft-cli.exe too), Restart now; .old goes at the next start.
- macOS: the verified .dmg is opened (the app is ad-hoc signed and not notarized, so it isn't
  replaced behind the user's back).
- Flatpak, Snap, deb, rpm, tar.gz: never replaced; the window shows the command.

Every package's SHA-256 must match the release's SHA256SUMS.txt (HTTPS, same release); a
mismatch deletes the download and changes nothing. With PUBLIC_KEY set in update/install.rs,
SHA256SUMS.txt.minisig must also verify (minisign-verify, MIT) with a trusted comment naming
the release. release.yml signs the list when the UPDATE_SIGNING_KEY secret exists and fails
when the app expects signatures but the secret is missing; docs/releasing.md has the steps.

Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-14 11:05:40 -04:00
1 parent a8258eb63a
commit d4a7b3abf3
16 files changed
+966 -12

No files matched your search

+27 -1
View File
@@ -17,6 +17,30 @@ choices:
**Preferences ▸ General ▸ Updates** has **Check for updates automatically** (on by default) and
**Check now**, which checks right away and shows the window even for a version you skipped.
## Updating in one click
Where SolidCraft can replace itself safely, the window also has **Update now**:
| Installed as | Update now does |
|---|---|
| AppImage | Downloads the new AppImage next to the old one, verifies it, renames it over the old file and offers **Restart now** |
| Windows installer (`.msi`) | **Install (closes SolidCraft)**: downloads and verifies the `.msi`, closes SolidCraft (asking about unsaved designs first) and starts the installer |
| Windows portable `.zip` or `.exe` | Downloads and verifies the package, swaps in the new `solidcraft.exe` (and `solidcraft-cli.exe`) and offers **Restart now**; the old file is removed at the next start |
| macOS | **Download and open**: downloads and verifies the `.dmg` and opens it; drag SolidCraft to Applications to replace the old copy |
| Flatpak, Snap, `.deb`, `.rpm`, `.tar.gz` | Nothing: your package manager updates these (the window shows the command), or you unpack the new archive |
Every download is verified before anything changes: its SHA-256 must match the release's
`SHA256SUMS.txt`, fetched over HTTPS from the same release, and, once the project signs its
releases, that list must carry a valid signature by SolidCraft's release key that names the
release (see [releasing](https://github.com/bherbruck/solidcraft/blob/main/docs/releasing.md#update-signing)).
A download that doesn't verify is deleted and the installed copy is left as it was; **Download**
still gets the package for a manual install. On macOS SolidCraft opens the disk image rather than
replacing the app itself, because the app is ad-hoc signed and not notarized: replacing it in the
background would install code macOS can't vouch for without you seeing it.
Update now needs write access to the installed file: an AppImage in a folder you can't write to
fails with a message, and **Download** is the way to update it.
## What is sent
One HTTPS request, at start and at most once a day while SolidCraft runs:
@@ -28,7 +52,9 @@ Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28
```
That's all: no version number, no machine or user identifier, no usage data, no cookies. GitHub
**Update now** then downloads `SHA256SUMS.txt` (and its signature) and the one package from the
release's own `github.com/bherbruck/solidcraft/releases/download/…` links, with the same
User-Agent. That's all: no version number, no machine or user identifier, no usage data, no cookies. GitHub
sees the request like any other visit to the API (your IP address, the time); see
[GitHub's privacy statement](https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement).
SolidCraft compares the answer with its own version on your computer.
+41
View File
@@ -206,6 +206,47 @@ Renew the credentials before `--expires` by repeating steps 2 and 3. The snap us
confinement and only plugs that connect automatically, apart from `removable-media`, which users
connect with `sudo snap connect solidcraft:removable-media` (getting-started.md).
## Update signing
The app's **Update now** (docs/book/src/guide/updates.md) only installs a package whose SHA-256
matches the release's `SHA256SUMS.txt`, fetched over HTTPS. That protects against corrupted and
tampered downloads in transit, but not against someone who can upload release assets. Signing
the checksum list closes that gap: once `PUBLIC_KEY` in `crates/ui-egui/src/update/install.rs`
is set, the app also requires `SHA256SUMS.txt.minisig`, a [minisign](https://jedisct1.github.io/minisign/)
signature by that key whose trusted comment names the release (`SolidCraft v<version>`), and
refuses to update in place otherwise. The release workflow signs the list when the
`UPDATE_SIGNING_KEY` secret exists, and fails when the app expects signatures but the secret is
missing.
To turn it on once (owner):
1. On a trusted machine with minisign (`sudo apt install minisign`, `brew install minisign` or
`winget install minisign`), make a key pair without a password (the workflow can't type one):
```sh
minisign -G -W -p solidcraft-update.pub -s solidcraft-update.key
```
2. Store the secret key as the repository secret and keep an offline backup of it (a lost key
means a new key and an app release that carries it; a leaked key means the same, quickly):
```sh
gh secret set UPDATE_SIGNING_KEY -R bherbruck/solidcraft < solidcraft-update.key
```
3. Put the public key (the second line of `solidcraft-update.pub`, starting with `RW`) into
`crates/ui-egui/src/update/install.rs`:
```rust
pub const PUBLIC_KEY: Option<&str> = Some("RW…");
```
and land that through a PR. Builds from then on refuse unsigned updates; releases built
from then on carry `SHA256SUMS.txt.minisig`.
4. Delete `solidcraft-update.key` from the machine once the backup is safe.
Older builds without the key keep updating with the SHA-256 check alone.
## Repository setup
The `release` branch is advanced only for intentional release batches. Restrict its writers to