Skip to content

In-app updater

mnml watches its own releases. On launch a background thread pings the GitHub releases API, and if the latest tag differs from the running version a toast fires hinting at :update.install_latest. That command spawns a Pty pane that downloads the right artifact for your platform, SHA256-verifies it against the published sha256.sum, extracts the tarball / zip, and drops the new binary in ~/.cargo/bin/mnml. No sudo / UAC prompts — the user-scope cargo bin dir is user-writable on every platform.

The updater is deliberately split into two halves so neither blocks the other: a passive check that costs one HTTP request a session, and an active install that you opt into when you’re ready to take the new version.

When mnml starts, src/update_check.rs spawns a single background thread that:

  1. GETs https://api.github.com/repos/chris-mclennan/mnml/releases/latest with a 10-second timeout.
  2. Parses tag_name from the response and strips the leading v.
  3. Compares to the built-in CARGO_PKG_VERSION via plain string equality.
  4. Stashes the result on a shared Arc<UpdateCheck> that App::tick polls.

The first tick after the data arrives surfaces a one-shot toast:

mnml v0.1.5 available — :update.install_latest · https://github.com/chris-mclennan/mnml/releases/tag/v0.1.5

The toast fires once per session — dismissing it (or just letting it time out) won’t trigger another for the same version. The check is intentionally simple: string equality on the tag, no semver parsing. The only false-positive case is running a local dev build whose Cargo.toml version equals the latest published tag; the session-once flag stops the toast from reappearing if you ignore it.

~/.config/mnml/config.toml
[ui]
check_updates = false # skip the GitHub API call on launch

Default is true. The check is also skipped automatically when --headless is set (no toast surface).

The toast hints at :update.install_latest. Run it from the ex-command line (vim mode), the command palette (Ctrl-Shift-P → “Update mnml”), or any keymap you’ve bound to it. There’s no default chord — updates are rare enough that the palette / ex-cmd is fine.

The command:

  1. Reads latest_version from the launch-time check. If the check is disabled, hasn’t resolved, or returned no newer version, you get a toast (update check disabled or not started / no newer mnml release found) and nothing else happens.
  2. Writes the platform-specific install script to a temp file (chmod 755 on Unix).
  3. Opens a Pane::Pty running the script via bash (macOS / Linux) or powershell -File (Windows).

The Pty pane shows download progress, the SHA256 verification, and the install step live. The currently-running mnml process keeps working throughout — its binary is in memory, not re-read from disk. When the script finishes:

──────────────────────────────────────────────────────
Quit mnml (Ctrl+Q) and relaunch to use v0.1.5.
Your current session is still running the old binary.
──────────────────────────────────────────────────────

Quit with Ctrl+Q (or :qa! in vim mode) and relaunch to pick up the new version. The flow deliberately avoids the “kill the process that’s running the install” circle — no self-exec dance, no in-place binary swap. You get to decide when to take the new version.

Each script is templated at compile time via cfg! macros so the binary ships with exactly one script branch baked in. Architecture is also resolved at compile time (aarch64 vs x86_64), so the script downloads the artifact that matches the binary you’re running.

1/4 downloading sha256.sum…
2/4 downloading mnml-rs-aarch64-apple-darwin.tar.xz…
3/4 verifying SHA256…
✓ verified
4/4 extracting + installing to ~/.cargo/bin/mnml…
✓ installed: /Users/chris/.cargo/bin/mnml

A bash script. Downloads mnml-rs-<target>.tar.xz (Apple Silicon: aarch64-apple-darwin; Intel: x86_64-apple-darwin), verifies via shasum -a 256, extracts, and install -m 0755s the binary to ~/.cargo/bin/mnml. No sudo needed — ~/.cargo/bin is user-writable and on your PATH if you use Rust. If you installed mnml via Homebrew, the fresh binary in ~/.cargo/bin shadows the Homebrew copy as long as ~/.cargo/bin appears earlier on PATH.

1/4 downloading sha256.sum…
2/4 downloading mnml-rs-x86_64-unknown-linux-gnu.tar.xz…
3/4 verifying SHA256…
✓ verified
4/4 extracting + installing to ~/.cargo/bin/mnml…
✓ installed: /home/chris/.cargo/bin/mnml

A bash script. Downloads mnml-rs-<target>.tar.xz (aarch64-unknown-linux-gnu or x86_64-unknown-linux-gnu), verifies via sha256sum, extracts the tarball, and install -m 0755s the binary to ~/.cargo/bin/mnml. No sudo — ~/.cargo/bin is on a Rust user’s PATH and is user-writable. If you installed mnml somewhere else (/usr/local/bin, a .deb, your distro’s package manager), the in-app updater drops a fresh binary in ~/.cargo/bin that will shadow the system copy as long as ~/.cargo/bin appears earlier on PATH.

1/4 downloading sha256.sum…
2/4 downloading mnml-rs-x86_64-pc-windows-gnu.zip…
3/4 verifying SHA256…
✓ verified
4/4 extracting + installing to %USERPROFILE%\.cargo\bin\mnml.exe…
✓ installed

A PowerShell script. Downloads mnml-rs-<target>.zip (x86_64-pc-windows-gnu today), verifies via Get-FileHash, extracts, and copies mnml.exe into %USERPROFILE%\.cargo\bin\. No elevation needed — the user-scope cargo bin dir is on PATH if you use Rust. Alternatively, winget upgrade mnml from the terminal.

The script cleans up the temp dir on success and prompts Press Enter to close this pane. so you can read the relaunch hint before the pane goes away.

BSDs and other unknown OSes fall through to a stub that points you at the release URL for manual download:

── mnml in-app update ──
version: v0.1.5
In-app install isn't wired for this platform.
Download the installer manually:
https://github.com/chris-mclennan/mnml/releases/download/v0.1.5/
Press Enter to close this pane.

If you’re on FreeBSD, OpenBSD, NetBSD, or an OS we haven’t seen, the artifacts on the GitHub release should still install via the platform’s normal tooling.

Every installer downloads two files: the platform artifact and the sha256.sum published alongside it. The script computes the artifact’s hash locally and compares to the published value:

Platform Hash tool Format read
macOS shasum -a 256 <hash> *<artifact> (BSD checksum format)
Linux sha256sum <hash> *<artifact> (coreutils format)
Windows Get-FileHash -Algorithm SHA256 first hex64 token in the line matching the artifact name

Mismatch refuses to install and the script exits non-zero:

✗ SHA256 mismatch — refusing to install

The check defends against partial downloads, CDN corruption, and a transparent proxy injecting a different binary. It does not defend against a compromised GitHub Releases page that publishes a forged sha256.sum alongside a forged artifact — that’s the same exposure normal users have when downloading manually. mnml doesn’t currently ship a separate signing chain (gpg / sigstore / cargo-dist’s signing); when it does, this section will document it. For now, the SHA256 step gives you “the bytes you got match the bytes the release page advertises”, which is what curl | sh flows give you and no more.

The launch toast never appears. Check three things in order:

  1. [ui] check_updates in ~/.config/mnml/config.toml — if it’s false, the background thread doesn’t spawn.
  2. You’re not running with --headless — the check is skipped in that mode.
  3. The version you’re running is genuinely behind. The check uses string equality on the tag; a local build whose Cargo.toml equals the latest published tag will look like “already on latest”. Run mnml --version and cross-check against https://github.com/chris-mclennan/mnml/releases/latest.

update check disabled or not started. The command ran but App.update_check is None. Same three causes as above — most often, you set check_updates = false or launched headless.

no newer mnml release found. The check completed but the latest tag matches the running version. Nothing to install.

update: couldn't write install script: <error>. mnml failed to write the script to std::env::temp_dir(). Usually a disk-full or permissions issue on the temp dir.

SHA256 mismatch. The script exits 1 and the Pty pane stays open with the mismatch printed. Retry the install — if it persists, the release artifact is probably mid-upload or got re-uploaded out of sync with sha256.sum. File an issue at github.com/chris-mclennan/mnml/issues.

Extract or install step fails. Usually a permissions issue on ~/.cargo/bin (or %USERPROFILE%\.cargo\bin\ on Windows). Create the dir manually (mkdir -p ~/.cargo/bin) and re-run. On Windows also make sure Defender isn’t quarantining fresh .exe downloads.

Windows “press Enter to close” doesn’t close the pane. That’s the script waiting — press Enter inside the pty pane (not in the editor). Or kill the pane with the close-pane keymap.

You want to skip the in-app updater entirely. Use the same installer flow that gave you mnml the first time — brew upgrade mnml, winget upgrade mnml, redownload the tarball from the Install page, or cargo install mnml-rs. The in-app updater is a convenience, not the only path.