This page documents the binary self-update path used by animus update,
the canonical self-update command. The former animus self update group
was retired in v0.5.x with no aliases; --check replaces the old
--check-only, and --force / --prerelease are folded into
animus update.
- Releases are fetched from
launchapp-dev/animus-cliGitHub Releases (the public mirror of this repo). - The CLI calls
GET https://api.github.com/repos/launchapp-dev/animus-cli/releases/latestfor--channel stable, orGET .../releases(listing) for--channel nightly. - User-Agent is
animus-update/<CARGO_PKG_VERSION>on every request.
The release workflow (.github/workflows/release.yml) publishes one
archive per supported (os, arch) target. As of v0.5.8 the live mapping
is:
| Host | Target triple | Asset name |
|---|---|---|
| macOS, Apple Silicon | aarch64-apple-darwin |
ao-vX.Y.Z-aarch64-apple-darwin.tar.gz |
| macOS, Intel | x86_64-apple-darwin |
ao-vX.Y.Z-x86_64-apple-darwin.tar.gz |
| Linux, x86_64 | x86_64-unknown-linux-gnu |
ao-vX.Y.Z-x86_64-unknown-linux-gnu.tar.gz |
| Windows, x86_64 | x86_64-pc-windows-msvc |
ao-vX.Y.Z-x86_64-pc-windows-msvc.zip |
Notes:
- Linux
aarch64is not currently published.animus updatewill exit withno release asset matched platform ...if invoked on that host. - Windows
.ziparchives are published butanimus update's in-tree extractor handles.tar.gz/.tgzonly. Windows users should download the zip from the Releases page and replace the binary manually until a.zipextractor lands. apply_updatefalls through to a bare-binary install when the asset has no archive extension —looks_like_installable_assetis the allowlist.
Asset selection is handled by pick_asset_for_host in
crates/orchestrator-cli/src/services/self_update.rs. It accepts
whole-token matches against the os and arch components and recognises
common aliases (darwin for macos, arm64 for aarch64, amd64
for x86_64).
- The release asset metadata carries a
digestfield of the formsha256:<hex>per GitHub Release API. When present, the staged download is hashed via [sha2::Sha256] and the result is compared byte-for-byte. Mismatch aborts the install with a clear error. - When the inline
digestis absent, the installer looks for a sibling<asset>.sha256asset in the same release (literal match; the.sha256sumextension is NOT searched — thefind_sidecarimplementation only constructsformat!("{}.sha256", asset.name)). If found, the leading hex token is parsed and compared. - The release workflow currently publishes a single aggregate
SHA256SUMS.txtrather than per-asset sidecars. The inlinedigeston the asset metadata is the primary verification path. - Cosign keyless signatures are not currently produced by the
release workflow. There is no
*.bundleartifact to verify. Ifcosignlands on the release pipeline in a later iteration,apply_updateshould add a verify pass alongside the existing sha256 check — soft-fail (warn + continue) whencosignis not on the localPATH, hard-fail when verification itself fails.
The HTTP client in build_client enforces three defences:
- A short per-request timeout (
STARTUP_FETCH_TIMEOUT/MANUAL_FETCH_TIMEOUT) so a hung GitHub call never delays a subcommand by more than a few seconds. - A redirect cap of 5 hops (
MAX_REDIRECTS). The release-download path is one or two hops in practice. - A host allowlist via
is_allowed_release_host— every redirect target must resolve toapi.github.com,github.com,codeload.github.com, or any*.githubusercontent.comsubdomain. A redirect to any other host fails the request before bytes are read.
atomic_install is the only file-level mutator. It:
- Resolves the running binary via
std::env::current_exe()— stable on Linux + macOS for/usr/local/bin/animus-style installs. - Writes the new binary to
<current_exe>.newin the same directory (POSIX requiresrename(2)to be atomic only within the same filesystem; cross-mount renames fail withEXDEV). - Sets executable mode (
0o755on Unix) viaset_permissions. - Calls
std::fs::rename(<new>, <current_exe>).
rename(2) on POSIX is atomic: the running process keeps its open
file descriptor pointing at the old inode, but every subsequent
exec of the path picks up the new bits. This is the only safe shape
— writing directly to <current_exe> would corrupt a running daemon,
and staging in /tmp then mv-ing would fail with EXDEV whenever
/tmp lives on a separate filesystem.
There is no built-in rollback. To revert manually:
# 1. Find the version you want to revert to:
gh release list --repo launchapp-dev/animus-cli
# 2. Download + extract the archive for your host:
TAG=v0.5.5
TARGET=aarch64-apple-darwin
curl -fL \
-o /tmp/ao-${TAG}.tar.gz \
https://github.com/launchapp-dev/animus-cli/releases/download/${TAG}/ao-${TAG}-${TARGET}.tar.gz
tar -xzf /tmp/ao-${TAG}.tar.gz -C /tmp/
# 3. Replace the running binary at its current path:
INSTALL_PATH=$(which animus)
sudo install -m 0755 "/tmp/ao-${TAG}-${TARGET}/animus" "${INSTALL_PATH}"
# 4. Restart any running daemons:
animus daemon stop
animus daemon startThe same-directory rename in step 3 keeps any running animus daemon
process pointed at the old inode until restart, so the rollback is
race-free.
--channel |
AutoUpdateChannel |
Behaviour |
|---|---|---|
stable (default) |
Stable |
Calls GET /releases/latest, accepts only prerelease: false records. |
nightly |
Prerelease |
Calls GET /releases (listing), accepts any newer-than-current release including prereleases. |
There is no separate *-nightly tag stream today — nightly maps
to the GitHub prerelease admission rule. If a dedicated nightly
publishing cadence ships later, the mapping can be tightened without
changing the CLI surface.
These act on the background startup check (when the daemon spawns
animus for routine work), not on the manual animus update command:
ANIMUS_AUTO_UPDATE_DISABLE=1— skip the startup check entirely.ANIMUS_AUTO_UPDATE_MODE={off,notify,prompt,auto}— override the configured mode in the project-local.animus/config.json(read first byresolve_effective_config_block) or the global~/.animus/config.json(read second; resolved viaConfig::global_config_dir()). The project-local block wins when both are present.
Manual animus update invocations always run regardless of these
env vars — the operator is asking for it explicitly.