Repository navigation
1.15.0 — 2026-10-19
Release Notes
A Tower that is asked to stop now lets what it is running finish, and Layover can update itself: it
hears of a new release, gets it ready, verified and checked against the factory, and — asked —
installs it between runs and restarts on it, rolling back one that cannot prove itself.
Compatibility. Additive for layover.toml and the HTTP API: every factory that loaded under
1.14 loads unchanged, and the one new table, [update], is optional. What an existing factory will
notice:
- The first Ctrl+C on
layover servedrains instead of exiting at once: nothing new starts, what
is running finishes, and then it exits. A second Ctrl+C cuts what is running off and exits. - The Tower makes one request a day to github.com, for the newest release's manifest, and says
when a newer release exists. It downloads and changes nothing.[update] mode = "off"stops it
for a factory andLAYOVER_UPDATE=offfor a machine. layover doctorbegins with a line saying which Layover runs and what this machine last
learned about releases.- One Tower serves a factory. A second
serveon a factory another is serving refuses to start,
where it used to race the first for its queue. - The service files
layover autostartwrites for systemd and launchd set
LAYOVER_SUPERVISOR, so a Tower installing a release exits 75 for the service to restart it.
Regenerate an older one; a systemd service is recognised without it. validatewarns when[update] max_drainis shorter than a run'stimeout, which the
defaults never are.- The HTTP API:
GET /healthgainsstopping, and/updateis new, withapply,skipand
rollbackbeneath it.
Added
layover stopasks the Tower serving a factory to stop, from another terminal or a script: it
drains and exits, and--nowcuts what is running off. It writes.layover/stop-request.json,
which the Tower honours and removes, and which a Tower starting later ignores.- Cutting off kills the runs. A Tower cut off — a second Ctrl+C, or
layover stop --now— kills
each live run's process tree and leaves it for the next Tower to record asinterrupted, rather
than leaving it running with nobody listening. Every way of stopping exits 0, so a systemd or
launchd service that restarts on failure leaves a stopped Tower stopped. - Draining. While a Tower stops, nothing new starts — queued work stays queued for the next
start, and schedule ticks are held — except work that completes a rendezvous already holding an
arrival, which would otherwise be lost. The Tower exits once nothing is alive and nothing is
parked, so stopping it no longer interrupts runs or loses arrivals. - The dashboard says when a Tower is stopping: the version pill reads
· stopping, also from a
--watch-onlydashboard in another process. - Hearing about a newer Layover.
[update]—mode(notifyby default, oroff),channel
(stable, orprereleasefor release candidates) andcheck_every("24h"). The Tower looks on
that schedule; the version pill readsv1.14.0 → 1.15.0and opens the release's Compatibility
note and notes;doctorsays so. What was found is kept per machine, so factories sharing a
binary ask once between them. layover update checklooks now and prints what a newer release changes;layover update skip <version>stops offering one on this machine, and--clearundoes it.layover aboutsays what a binary is — version, platform, state layout, HTTP API and the
oldest release it can be updated from — and--jsonsays it to a program.- Staging a release, with
[update] mode = "stage"orlayover update stage: the newest
release is downloaded (capped at 64 MB), its SHA-256 matched to the manifest and its.sha256
file, its GitHub attestation verified withgh attestation verifypinned to this repository's
release workflow and tag, its binary unpacked with nothing allowed outside the staging folder and
asked what it is withlayover about --json, and the factory validated by it. Nothing downloaded
runs before it is verified; withoutgh,--trust-checksum-onlyruns it on a person's say-so.
Nothing is installed. - Installing a release:
layover update apply, or Update now in the dashboard. The
Tower drains — up to[update] max_drain("4h"), then cuts off what is still running for
recovery to restart on the new version, unless an agent withrecovery = "manual"is running,
which postpones it — swaps its binary, keeping the old one for[update] keep_previous("7d"),
and hands over: on Windows it starts the new version at its own address and waits for it to
answer, putting its own binary back if it does not; under systemd or launchd it exits 75. The new
Tower keeps the dashboard's token, proves itself healthy, and writes to.layover/serve.log.
With no Tower serving, the binary is replaced at once. A Homebrew or npm install is left to its
package manager, with the command to run. Exit codes say how it went. - Installing unattended, with
[update] mode = "auto": a patch or minor release (allow), a
day or two after it was published (min_age = { patch = "24h", minor = "48h" }), staged,
verified (require_provenance) and validating the factory with no new warning, is installed in
thewindow("Sat 02:00-06:00", local time) or once the factory has been idle foridle_for
("10m"). A major release and one that changes the state layout always wait for a person. An
install whose window closes before runs finish is postponed to the next. The dashboard's bell
rings for a new release, an update and a rollback. layover update rollbackgoes back to the version the last update replaced;status
says where things stand;logprints.layover/updates.jsonl.GET /update: what this machine last learned about releases, for this factory, and — once a
release is staged — how far staging got (staged), what it made of this factory (gate), the
install under way (request), the last update (last_update) and the version kept to go back
to (previous).POST /update/apply,DELETE /update/apply,POST /update/skip
andPOST /update/rollbackdo from the dashboard what the commands do.LAYOVER_UPDATE,LAYOVER_UPDATE_SOURCE(a mirror laid out as GitHub's releases) and
LAYOVER_GITHUB_TOKEN(for the API, on theprereleasechannel).cargo xtask verifyrefuses a CHANGELOG release section without a**Compatibility.**
paragraph, which the updater shows before an install, and adistupgrade until the pinned
release manifest the updater is tested against is refreshed.
Tried
Released first as two candidates, 1.15.0-rc.1 and rc.2, on GitHub only. A throwaway 1.14.0 install
found rc.1, verified it with gh, waited for a live run, installed it in place and rolled it back.
A factory on rc.1 in mode = "auto" then found rc.2, staged and verified it, waited out its day,
and installed it by itself once idle, keeping its dashboard's token. What the candidates turned
up — an install marked done before the new Tower had answered, and layover update check not
mentioning apply — is fixed here.
Install layover-cli 1.15.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/KotkaZ/layover-project/releases/download/v1.15.0/layover-cli-installer.sh | shInstall prebuilt binaries via powershell script
powershell -ExecutionPolicy Bypass -c "irm https://github.com/KotkaZ/layover-project/releases/download/v1.15.0/layover-cli-installer.ps1 | iex"Install prebuilt binaries via Homebrew
brew install KotkaZ/tap/layoverInstall prebuilt binaries into your npm project
npm install @layover/layover-cli@1.15.0Download layover-cli 1.15.0
| File | Platform | Checksum |
|---|---|---|
| layover-cli-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| layover-cli-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| layover-cli-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| layover-cli-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| layover-cli-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
Verifying GitHub Artifact Attestations
The artifacts in this release have attestations generated with GitHub Artifact Attestations. These can be verified by using the GitHub CLI:
gh attestation verify <file-path of downloaded artifact> --repo KotkaZ/layover-projectYou can also download the attestation from GitHub and verify against that directly:
gh attestation verify <file-path of downloaded artifact> --bundle <file-path of downloaded attestation>