Skip to content
Sirio is in private beta: sources and installer are sent to testers by email. Ask to join.

Updates

Sirio can update itself from Settings → Updates. Releases come from a private update server you run: static files over HTTPS, behind HTTP basic auth, with one credential per installation. The repository is private, so GitHub’s release links need authentication, and a GitHub token on every server would let each of them read the whole source. An installation key can only download releases, and it can be revoked for one machine without touching the others.

What makes a release installable is its signature, not the server it came from. Every release’s checksums.txt is signed with an ed25519 key that never leaves the maintainer’s machine, and the public half is compiled into Sirio. Someone who takes over the update server, or gets hold of an installation key, can hold updates back but can’t push a binary.

Open Settings → Updates and enter the update server’s address (e.g. https://updates.example.com) and the installation key, <name>:<secret>, issued by whoever runs the server. The key is stored encrypted and never shown again; the card only shows its name. Revoking it on the server cuts off that installation alone.

  1. Sirio reads the server’s index.json every 6 hours (or when you press Check now) and lists the releases newer than the one running.
  2. When you install one (or the automatic policy does), Sirio downloads it into /var/lib/sirio/update/staging, verifies the signature and the checksums, and drops a request naming the version. That’s all it can do on its own: it runs as an unprivileged user under ProtectSystem=strict, where /usr/local/bin is read-only.
  3. The sirio-update.path unit starts sirio-update as root. The helper copies the release out of Sirio’s directory and verifies it again, with the release keys of the binary already installed (root-owned, so it’s the one to trust). It also refuses a release that isn’t newer, so an old, genuinely signed release can’t be used to downgrade.
  4. It stops Sirio, backs up the binary (/usr/local/lib/sirio/sirio-sysadmin.prev) and the database (/usr/local/lib/sirio/rollback/), then runs the release’s own install.sh with the downloaded binary. That’s the same script as a manual installation, so units and helpers are updated too, and settings in the drop-in are kept.
  5. It waits for the new version to reach “active” and stay there for 15 seconds without systemd restarting it, on the expected version. If that doesn’t happen, or install.sh fails, it puts the previous binary and database back and starts Sirio again: the update shows as rolled back, with the reason.

The panel is unreachable for about half a minute. The Settings page keeps polling through the restart and reloads itself on the new version.

An update restarts Sirio, which interrupts pipeline runs and backups, so it waits for them. A manual update is refused while any are queued or running, and an automatic one is postponed until none are.

Only administrators see the Updates card and its endpoints. Updating replaces the binary that the root helpers trust, so it isn’t a permission that can be handed out.

Settings → Updates → Automatic updates sets how big a step Sirio takes on its own:

Setting Installs on its own
Off (default) nothing: it only checks and shows what’s available
Patch v1.4.2 → v1.4.3
Minor also v1.4.2 → v1.5.0
Major every release, v2.0.0 included

The levels are read literally from the tags: v0.14.0 → v0.15.0 is a minor step, even though semver allows a 0.x minor to break things. The newest release the policy allows is installed as soon as nothing is running. A release that failed or rolled back isn’t retried automatically: install it by hand, or wait for the next one. Development builds (not exactly a tagged commit) never update on their own.

What Sirio asks the update server, for whoever wants to serve it another way:

  • GET <server>/index.json → {"releases": [{"version": "v0.15.0", "date": "2026-10-20T10:00:00Z", "notes": "…"}]}. Entries that aren’t vX.Y.Z are ignored.
  • GET <server>/releases/<version>/<file> for checksums.txt, checksums.txt.sig (base64 ed25519 signature of the exact bytes of checksums.txt), install.sh and sirio-sysadmin-<version>-linux-<arch>.
  • Every request carries basic auth (installation name and secret) and a User-Agent: sirio-sysadmin/<version> (linux/<arch>). 401 or 403 means the key was refused. Redirects are followed, without the credentials if they point to another host.
  • The address has to be https. Plain http is only accepted towards loopback, for testing.
  • /var/lib/sirio/update/: staging/ (the downloaded release, removed after a successful update), request, result, update.log (the download) and install.log (the installation). The helper reads and writes them as Sirio’s user, never as root (see security.md).
  • /usr/local/lib/sirio/sirio-sysadmin.prev and /usr/local/lib/sirio/rollback/: the previous binary and database, kept until the next update.
  • journalctl -u sirio-update: the helper’s own output.
  • SIRIO_UPDATES_ENABLED=false removes the section; SIRIO_UPDATE_HELPER points to a helper installed elsewhere.

A rollback restores the binary and the database, not the units and helpers: the new release’s stay. install.sh keeps those compatible with the previous release, and an older install.sh can’t be rerun without its release.

The installation key is decryptable by anything running as Sirio’s user, pipeline shell steps included. That’s acceptable because all it opens is the release downloads, and it’s why each machine gets its own key.