Updating a fleet that is already installed
The same delivery, repeated — and the traps that make it look like it worked when it did not.
An update is the same delivery as the first install, repeated — there is no separate upgrade path and no in-place self-update. What changes is only that machines already have binaries, so every step below must be safe against a machine that is running work right now.
Step 1 — know which release you are delivering.
Name the source deliberately: the public line, a release channel (--channel stable), or a
distribution tree you hold (--from <path or URL>). ⚠ A source that was not refreshed delivers
the version it last held, with no error anywhere — so the first question of an update is which
release the source is actually serving, and
Verifying an install is how you answer it by content
rather than by a version string.
Step 2 — deliver, one of two ways.
With the installer — the same line as the first install, run on each machine:
⚠ Saphan Studio Solo is in final testing. The install line below works from the release day; until then the download is not available.
It re-detects OS and architecture, downloads the whole machine payload —
{saphan,saphan-agent,saphan-gateway,saphan-server} — re-verifies it against SHA256SUMS, and
refuses on a mismatch with nothing installed. When the source publishes a version manifest, it
also refuses to install an older version over a newer one unless you pass --allow-downgrade.
Prefer this route: it is the only one that delivers every binary and verifies what it
delivered.
⚠ --no-serve is what makes it an update. Run by a person at a terminal without it, the
installer takes the first-install path: it refuses while a Saphan Studio process of the same
installation is running, and otherwise sets the workspace up and starts the console again. With
--no-serve it replaces the binaries and starts nothing.
Over ssh, without the installer, when you just want the new binaries on hosts you already reach,
from a distribution tree laid out as <os>-<arch>/<binary>:
⚠ Ship the WHOLE payload the installer would have. A machine left with a new saphan and a
stale saphan-agent reports the mismatch nowhere.
⚠ This route verifies nothing. Unlike the installer, scp checks no checksum; the content
check in Step 3 is the only verification you get. ⚠ And "same version" is not "same artifact":
two builds of one release can differ byte for byte, so compare against the tree you actually
shipped from and never against a version string.
Two properties of that pair are load-bearing, not style:
- Match the architecture per machine.
uname -s/uname -mon each host; a fleet is routinely mixed (Linux runners plus a macOS machine). mv -fis a rename, not an overwrite. A rename publishes a new inode, so work already executing the old binary keeps running against its own open file instead of being killed mid-run; an in-placecpover a live binary can also poison macOS's per-inode code-signature cache.
Then restart what runs. A running process keeps the binary it started with: until the console
and the server are restarted (saphan-gateway stop / start, saphan-server stop / start, or
your supervisor's own restart), they are the old release.
⚠ A far machine must run the same build as the machine that dispatches to it, or its governed
runs are refused by name (legacy-far-wall-unavailable, at no cost, nothing spawned).
Step 3 — verify every machine, by content and by behaviour (Verifying an install). A fleet where some hosts took the update and others did not is worse than one where none did: the halves disagree, and the disagreement is silent. Verify the whole roster before calling the update done:
What the installer does
What the one line fetches, what it verifies before anything lands, and the admission request it makes for this machine.
Run Studio as containers
Run the Studio server — gateway, launcher, console server and CouchDB — as container images on volumes: first start, upgrade, roll back, and rotate the CouchDB administrator.