Skip to main content
Docs navigation

Upgrade runners and MCP bridges

Move a runner fleet and your local MCP bridges onto a new release through one canary, with a verification you can point at and a way back to the version you were running.

Before you start, you need:
  • Owner or admin in the account, so you can read runner versions and run history.
  • Root or sudo on each runner host — the installer writes to /usr/local/bin and the service unit.
  • One non-critical runner to go first.
  • The release you are currently on, written down. You need the exact tag to go back to it.

What upgrades on its own#

Four things version separately. Upgrading one never upgrades another.

Component Who upgrades it How
Control plane We do Nothing for you to run. Shipped changes land in the changelog.
Runner binary You, per host Re-run install.sh. Stops and restarts the service.
emisar-mcp bridge You, per workstation Re-run install-mcp.sh. Cloud OAuth clients run no bridge.
Action packs You, per runner A separate lifecycle with its own trust decision — Roll out and roll back packs.

Find the versions you run#

The console reports what each peer last told it: the Runners list carries each runner's version, and the Agents page carries each key's bridge version (blank for a cloud client, which has no bridge). On the host, ask the binary itself.

shell
# one machine-readable line: "emisar version 0.16.0"
$ emisar --version

# the same version plus Go, platform, and build metadata
$ emisar version

# the bridge, on the workstation that runs your LLM client
$ emisar-mcp --version

A locally built binary reports dev, which is neither current nor stale — it is simply unclassifiable, and the control plane never blocks it.

Compatibility status#

The control plane classifies every reported runner and bridge version against two thresholds it publishes: a minimum it still supports and a recommended version it would rather you were on. Today those are >= 0.10.0 for the runner and >= 0.3.0 for the bridge.

  • outdated is above the minimum but behind the recommended version. It runs normally; the console shows a version chip and nothing else changes.
  • unsupported is below the minimum. Enforcement is a deployment-level switch and is off today, so an unsupported peer is warned about rather than refused. When it is turned on, the control plane rejects an unsupported runner at connect and an unsupported bridge at initialize, naming the minimum in the error.
  • A version the control plane cannot parse is never blocked, even with enforcement on. The version is self-reported, so it is an operational hygiene signal — not an authorization control.
Before 1.0, components move together.

These thresholds are operational warning lines, not a certified interoperability matrix: no pair of 0.x artifacts is cross-version tested. Roll the portal, runner, and bridge releases that shipped together, and treat a mixed-vintage fleet as a state you pass through during a rollout, not one you park in.

Plan the rollout#

  1. Read the changelog for the releases between your current version and the target.
  2. Write down the version you run today. That tag is your way back; nothing in the product remembers it for you.
  3. Check the Runs list for work in flight on the hosts you are about to touch. Filter to running and sent, and let anything long-lived finish first.
  4. Pick one non-critical runner as the canary — ideally in a group whose actions you can exercise safely.

Upgrade one runner#

Re-running the installer is the upgrade. Pin the release rather than taking whatever is latest, and pass an explicit empty pack set so a binary upgrade stays a binary upgrade — without it, the installer offers host-matched pack recommendations.

shell
# on the canary host
$ curl -sSL https://emisar.dev/install.sh | sudo bash -s -- --version <target-version> --packs ""
Verify this download first

The installer checks the checksum itself; to prove the binary before it runs as sudo, download it and run these first — a green check names our source repository and the release workflow that built it.

shell
# provenance — built by our workflow, from our source
$ gh attestation verify emisar-<version>-linux-amd64.tar.gz --owner andrewdryga
# checksums — the bytes match what we published
$ sha256sum -c SHA256SUMS

More on the signing pipeline: Release integrity.

What survives the upgrade: /etc/emisar/config.yaml (an existing file is left alone), /etc/emisar/runner.env and the cached runner token, the installed packs under /etc/emisar/packs, the durable dispatch log, and the local audit journal. The installer downloads and stages the new binary, verifies its checksum, executes it to confirm the version, and checks the staged binary can read this host's dispatch state — all before touching the running service.

The service does stop.

Once the preflight passes, the installer stops the service, swaps the binary, and starts it again. A runner kills its children when it exits, so an action in flight on that host is interrupted. Drain the host first — do not treat a binary upgrade as invisible to running work.

If any step after that fails, the installer restores the previous binary, the previous runner.env and cached token, and restarts the service if it had been running, then exits non-zero. That is a restore of the runner and its service state — not a snapshot of the whole filesystem, so packs it already installed in that run stay installed.

Verify the canary#

Six checks, in order. Stop at the first one that fails.

shell
# 1. the binary is the version you pinned
$ emisar --version

# 2. the service came back and stayed up
$ systemctl status emisar

# 3. config, credential, packs, action binaries, and reachability
$ sudo emisar doctor

# 4. the packs and hashes this host will advertise
$ sudo emisar pack list

Then, in the console: the runner shows online with the new version and its expected actions (5), and one low-risk read-only action against it succeeds and lands in the audit trail (6). A run you can find in the audit trail is the completion condition — a green doctor alone only proves the host is ready, not that dispatch works end to end.

Roll through the fleet#

Repeat the same two steps — upgrade, then the full verification — in bounded batches, one runner group at a time. Halt the batch and stop rolling when any of these happens:

  • The installer exits non-zero. That host is already back on its previous binary; read the output before you touch the next host.
  • emisar doctor reports a failed check, or the runner does not come back online within its usual reconnect window.
  • The advertised action set changed in a way the release notes did not describe, or a pack lands as pending trust on the Packs page.

Production rollout owns fleet shape and batch sizing; Runner fleet owns groups, labels, and host-side removal.

Upgrade local bridges#

The bridge upgrades per workstation, on its own schedule. Its installer stages the new binary beside the current one, verifies the checksum and reported version, hard-links the old executable as a rollback, activates each install with a rename, re-verifies the installed digests, and only then discards the backups — a failure anywhere rolls every path back.

shell
# replaces the emisar-mcp binary in place
$ curl -sSL https://emisar.dev/install-mcp.sh | sudo bash

# confirm the new version
$ emisar-mcp --version
  • Client config and API key are untouched. The installer replaces the binary; an existing emisar entry in a detected client config is left as it is.
  • A running client keeps the binary it already loaded. The client spawns the bridge as a child process, so replacing the file on disk does not change the process already running. Restart the client to pick the new one up.
  • Then prove it works from the client. Ask the agent to list runners or find an action. A discovery call that returns is the completion condition; a version string alone does not prove the client relaunched the bridge.

Claude.ai and ChatGPT reach the control plane directly over OAuth, so there is no bridge on those connections and nothing to upgrade. API keys owns the bridge's key handling and its version chip.

Return to a known-good release#

Going back is the same installer with the tag you wrote down, followed by the same verification.

shell
# runner — the release you were on before
$ curl -sSL https://emisar.dev/install.sh | sudo bash -s -- --version <known-good-version> --packs ""

# bridge — same idea, with its own release tag
$ curl -sSL https://emisar.dev/install-mcp.sh | sudo bash -s -- --version <known-good-version>

Two limits worth knowing before you rely on this. Only an immutable published release installs — the installer refuses a release GitHub still reports as mutable. And the dispatch-state preflight runs with the older binary you are installing: if it cannot read the state a newer runner wrote, the installer refuses the swap and leaves the current install running rather than handing you a crash-looping host.

This is a return to a coordinated release, not a downgrade guarantee.

Before 1.0 there is no promise that an arbitrary older runner or bridge interoperates with the current control plane. Go back to the release you were actually running against this control plane — and if the portal has moved on since, say so when you open a support request rather than reaching further back.

Packs do not move with the binary in either direction. If the problem is an action's behavior rather than the runner, the version to change is the pack's — see Roll out and roll back packs.

Last reviewed August 3, 2026

Suggest a change