OpenPairings

Andere programma's 9 minuten lezen

Ascella

Het installatieprogramma van de zaal

Ascella installeert, werkt bij, start en stopt de programma's van de zaal op één computer. Dat zijn OpenPairings, Alnasl, Arkab, Polis en Nunki, elk een tegel op één lokale pagina (de geïnstalleerde versie, de nieuwste versie, of het draait), met een knop Alles bijwerken. Elke download wordt gecontroleerd tegen de controlesommen van zijn release voor hij uitgevoerd wordt, en Ascella komt nooit aan de gegevens van een programma: niet bij een update, niet bij een terugzetting.

Wanneer je het nodig hebt. Als één computer in de zaal meerdere van deze programma's draait en je ze liever vanaf één pagina bijwerkt dan met vijf installatieprogramma's. Het weigert een programma bij te werken dat open staat, dus een update valt nooit midden in een ronde. Gebruik je OpenPairings alleen op de gehoste server, dan heb je het niet nodig.

Waar het past. Het installeert Alnasl, Arkab, Polis en Nunki, en OpenPairings zelf; hoe je OpenPairings zonder Ascella installeert en bijwerkt, staat in Aan de slag.

De rest van deze pagina is alleen in het Engels beschikbaar.

Ascella installs, updates, starts and stops the chess hall's programs on one computer: OpenPairings, Alnasl, Arkab, Polis and Nunki. It is one local page (http://localhost:4090, opened when Ascella starts) with a tile per program: its mark, the version installed, the version out, whether it runs, and Install / Update / Start / Stop / Open buttons. Update all does the lot, one program at a time.

It never touches a program's data. Not on an update, not on a rollback, not when it is asked nicely. That is the whole design, and the rest is plumbing.

Named after zeta Sagittarii, which was there first.

What it does, per system#

Every action keeps to the same order: refuse if the program is open ("Close X first, then update", with a Stop button beside the message: it may be in the middle of a round); download and verify; unpack and check beside the program; swap. Nothing waits for ever: every command Ascella runs has a deadline (Ascella.Runner), and a program that does not answer in time ends the action with an error the tile shows in words. Ascella never kills anything.

A catalogue entry with "rollback": false has no way back, and the button and the function both refuse. That is OpenPairings and Alnasl: their databases migrate forward on an update, and an older program over a newer database is how tournaments get damaged.

install / updatestoprollback
Windowsthe program's own Velopack: Setup.exe --silent the first time, Update.exe apply --norestart --package <verified .nupkg> after. Installed-for-everyone copies (.msi) are left to their .msiWM_CLOSE to the launcher window, the launcher's own no-questions closenone: Velopack keeps no previous version
macOSthe release zip, unpacked with ditto, the .app swapped by renames in the same folderSIGTERM to the app's launcher, which stops its server cleanlythe previous .app is kept in .ascella-previous/ and swapped back
Linux / Pithe program's own install.sh, then its own update.sh --from <verified folder>; its own systemd unit runs itsystemctl stopthe program's previous release from /opt/<id>/releases
portable (OpenPairings on Linux)versions side by side in Ascella's folder, current switched by a renameSIGTERM (Windows: taskkill without /F)the previous version is still there

On Windows, programs are started through WMI, not as Ascella's children: Ascella's launcher holds what it starts in a job object that dies with it, and OpenPairings closing in round five because someone closed Ascella would be a poor advertisement.

On Linux and macOS a program is started from an empty environment (env -i, with only what Finder or a desktop session would give it: home, user, locale, a system PATH and the display). Ascella's own PORT and friends are not theirs, and an Arkab that reads a PORT it did not mean to inherit will conclude that the port is taken by something else.

Shortcuts and removal#

Right after an install the tile asks whether to make the program easy to start, and every installed tile has a Shortcuts list: a Start menu and Desktop shortcut on Windows (to pin to the taskbar, right-click the shortcut), the Dock (edited through defaults export / PlistBuddy / defaults import, because the Dock's preferences live in a daemon that ignores a plist written behind its back, and read back the same way) and login items on a Mac, a menu entry, Desktop file and GNOME favourite on Linux (Ascella.Launchers, one adapter per system). What Ascella makes is recorded in launchers.json in its own folder, and only that is ever removed. They point at paths that an update does not move. Remove on a tile stops the program and removes it the system's way (Velopack's Update.exe, the Bin, the app's uninstall.sh), keeps the data unless asked twice, and takes the shortcuts with it.

Downloads, and what is trusted#

Only from the GitHub repositories in the catalogue, only over HTTPS, and redirects only to GitHub's own hosts. Every file is checked against the SHA256SUMS its release publishes, and checked again just before it is run. A release without SHA256SUMS, a file not on it, or a file whose sum is wrong is refused and deleted. Nothing unverified is ever unpacked or run.

That catches a broken or swapped download, not a release that was bad when it was published: the sums come from the same release. That would take signatures. Releases now carry them: from the next release on, every file and SHA256SUMS itself have a GitHub artifact attestation (keyless, Sigstore). Ascella does not check them yet; a person can, with gh attestation verify <file> --repo teapot-chess/<repo>.

The data#

Each program's data directories are in the catalogue (data). Ascella.Guard refuses any write, move or delete that is in a data directory or contains one, and every installer goes through it. On Windows the install folder (%LOCALAPPDATA%\<pack id>, which Velopack's uninstall deletes whole) must not be a data folder and must not hold a database; a catalogue whose pack id names a data folder does not load. test/ascella/data_safety_test.exs runs install, update and rollback for every method against data directories full of files and checks them byte for byte afterwards.

Settings#

variablewhat
PORTthe page's port, default 4090; always on 127.0.0.1
ASCELLA_DATAAscella's own folder (downloads, portable programs, its secret)
ASCELLA_CATALOGUEanother catalogue
ASCELLA_APPS_DIRmacOS: where the apps go, instead of /Applications (then ~/Applications)
ASCELLA_RUN_ASLinux: the user portable programs run as when Ascella runs as root, and the user who may read the key to the page (see below). Set it in /etc/ascella/ascella.env
ASCELLA_TOKEN_FILELinux service: where the page's key is written at every start (the unit sets /run/ascella/token); unset everywhere else

On a box without a screen: ssh -L 4090:127.0.0.1:4090 box.

The key (Linux service)#

The Linux service is root and listens on 127.0.0.1, which every process of the box can reach, so its page wants a key. At every start the service writes a random one to /run/ascella/token, readable by root and by the user ASCELLA_RUN_AS names and by nobody else. The way in is a link that carries it once:

sh /opt/ascella/current/ascella.sh open           # opens it in the browser (as that user)
sh /opt/ascella/current/ascella.sh open --print   # prints http://localhost:4090/?k=...

The page takes the key, remembers the browser with a signed cookie and sends it on to the page without the key in the address. A request with neither gets a 403 that says so. Through an SSH tunnel: run open --print on the box and paste the link into the browser of the machine you came from. A restart makes a new key, so every browser comes in again by the link. Windows, macOS and a Linux start in a terminal have no key and nothing changes there.

Portable programs started by the root service (OpenPairings on Linux) run as ASCELLA_RUN_AS: before each start Ascella gives that user a way to walk to the program (the "execute" bit of the data folder, which is otherwise 0750 root) and a log folder of its own, programs/<id>/logs. A program that is already gone a second after it started is a failed start and the tile shows the end of its log. Ascella's pid file records when the process started beside its number (/proc/<pid>/stat on Linux, ps -o lstart on macOS), and a pid whose start time differs is another process: it is never reported as running and never sent a signal.

Installing Ascella#

Like Alnasl: a Velopack installer and a portable zip on Windows, a zipped .app on macOS 11 and newer (Apple Silicon) or on macOS 10.13 and newer (Intel Macs; that build is made for releases only), and install.sh with a systemd unit on Linux. On a Mac the first install is one manual step, because the app is not notarised: see docs/macos.md. After that, nothing Ascella downloads is quarantined. Removing Ascella itself has its own page, /remove-ascella (linked from the help), with the exact steps per system, because a program that uninstalls itself while it runs has opinions about the order. Releasing: docs/releasing.md.

Ascella updates itself#

A newer Ascella is looked for the way every other program is: the latest GitHub release, its files checked against that release's SHA256SUMS (Ascella.Fetch, the same code). The page shows "Ascella X.Y.Z, update available" with an Update Ascella button. Two rules, both kept in Ascella.Apps and Ascella.Updates.decide/1: Ascella updates last (Update all does the other programs first and itself after them), and it refuses while anything is being installed, updated or rolled back. The data folder is never touched.

how
macOSAscella.SelfUpdate.Mac: download and verify the zip, unpack beside the running Ascella.app (same volume), make sure the new app has no com.apple.quarantine, rename the running app to .ascella-previous/Ascella.app (exactly one previous copy) and the new one into place. A detached /bin/sh helper, started under env -i like every program Ascella starts, waits for the old Ascella to quit, opens the new one and asks its port for an answer; if there is none within 45 seconds it puts the previous app back and opens that. Then Ascella quits the way Quit does. Where Ascella.app is in a folder you may not write to (or runs from macOS's read-only copy of a download), nothing is attempted: the page says where Ascella is and to move it to /Applications.
Windowsthe launcher (rel/windows/launcher.c) sees Ascella quit with exit code 90 and has Velopack apply the update and start the new version. A portable zip has no updater and is updated by hand.
Linuxthe request goes to ascella-update.path, whose ascella-update.service runs update.sh: download, verify, switch, restart, and back again if the new version does not answer.

What you do once. On a Mac there is nothing to do: the app is signed and notarised and opens with a double-click (a build from a fork is only ad-hoc signed; see docs/macos.md). Ascella updates itself, and what it downloads is never quarantined.

Licence#

Apache-2.0. See LICENSE and NOTICE.