Updating

One-click updates

A packaged Speculum can update itself from the web admin: it downloads the latest signed release, verifies it, installs it, and restarts the mirror — without you touching the Pi.

On this page

Using it

Open the web admin (http://<pi-ip>:8080) and find the Updates card:

After clicking, the card shows progress — Downloading… → Verifying… → Installing… — then "Speculum is restarting — this page will reconnect". When the new version answers, the version badge in the header flips to the new release.

The admin page intentionally loses its connection during the restart and reconnects on its own. That's expected — the whole app (mirror + admin server) restarts together.

How it works

The mirror runs unprivileged, so it can't install a system package on its own. The update is split across a privilege boundary:

[Updates card]  ── POST /api/update/start ──▶  [config-server, in the mirror JVM]
                                                  1. find the latest GitHub release
                                                  2. pick the asset for this OS + CPU
                                                  3. download it + SHA256SUMS + .asc
                                                  4. verify GPG signature + SHA-256   (unprivileged)
                                                  5. stage it under ~/.speculum/update
                                                  6. systemctl start speculum-update.service
                                                          │  (authorized by a polkit rule)
                                                          ▼
                                          [speculum-update.service — runs as root]
                                                  7. re-verify signature + checksum AS ROOT
                                                  8. apt-get / dnf / pacman install the package
                                                  9. restart the mirror

The unprivileged side only prepares the update; the privileged helper re-verifies everything independently before installing. The boundary passes no arguments — the helper finds the staged package itself and trusts nothing the app asserts.

How the mirror restarts

After the package is installed, the running mirror has to be replaced. Speculum picks the right method for how you launched it:

How you run the mirrorHow it's replaced
Desktop-session autostart (the default kiosk)The app relaunches the new binary in your session, then exits the old one.
A --user systemd serviceThe app restarts its own user unit (systemctl --user restart …).
A custom system service you createdThe root helper restarts it if it's active.
Run one launcher, not two. If two mirrors run at once (e.g. the kiosk autostart plus a hand-launched copy), they fight over port 8080 and the updater may restart the one that isn't on screen. Pick a single launch method — see Troubleshooting → two mirrors.

Security model

See Verifying releases to check a signature yourself.

Requirements

Manual update (fallback)

You can always update by hand — and must, for the first install or an unsigned/old build:

# download the asset for your arch from the Releases page, then:
sudo apt install ./speculum_<version>_arm64.deb      # Debian / Raspberry Pi OS
sudo dnf install ./speculum_<version>_arm64.rpm      # Fedora / RHEL
sudo pacman -U ./speculum-<version>-1-aarch64.pkg.tar.zst   # Arch

# then restart the mirror — relaunch the kiosk (log out/in),
# or just relaunch it however you started it
Releases ↗ Troubleshooting →