Reconnecting
Restoring connection…
Reconnecting
Restoring connection…
Docs navigation
Get started
Connect an LLM
Deploy runners
Team & account
Access
Identity providers
Account
Govern actions
Operate
Roll out and roll back packs
A pack update changes the bytes a host may execute, so it takes two decisions: putting the new version on a runner, and letting the account dispatch it. Here is how to do both through a canary, and how to get back to the exact version you were on.
- Admin or owner, to review and trust a pack version for the account.
-
Root or sudo on each runner host — packs live under
/etc/emisar/packsand the tree is owned by root. - One non-critical runner to update first.
- The pack id, version, and content hash you run today. The rollback below needs the exact version.
Five rules that decide everything#
- — Installing is not trusting. The runner command puts bytes on a host. Whether those bytes may be dispatched is a separate account-level decision — settled by the published catalog when it carries that exact hash, and yours to make in the console otherwise.
- — Trust binds one exact content hash. A hash the published catalog carries is trusted for the account on sight. Any other hash — a pack you maintain, a hand-edited tree, a version the catalog no longer carries — lands as pending, and dispatch for that pack version is refused until someone trusts or rejects it.
- — A published version is immutable. Its tarball is content-addressed and the registry is append-only. The same id and version with different bytes is drift, not an update — the fix is to publish a new version, never to republish the old one.
- — Binary upgrades do not move packs. Re-running the runner installer leaves the installed packs where they are. See Upgrade runners and MCP bridges for that lifecycle.
- — Retirement blocks dispatch, not installation. Nothing here deletes an immutable artifact or erases the history of a run that used it.
Record what you run today#
Start from the host, because that is where the bytes are. Write down the id, version, and hash for the pack you are about to move, and note which runners or groups carry it.
# id, version, action count, and content hash per installed pack $ sudo emisar pack list # one pack's setup requirements and action profile $ sudo emisar pack info redis
The console's Packs page is the account-side view of the same facts: each version, the hash the account trusts, and the hash each runner is advertising.
Read what actually changed#
Before you move any bytes, look at what the new version changes in the action contract — that is what an LLM and your runbooks are written against. Open the pack on the registry and compare against the version you run now. Four kinds of change deserve a pause:
- — An action that was added, removed, or renamed — a removed id breaks any runbook step that names it.
- — An argument whose name, type, default, or validation moved, which changes what callers may send.
- — A risk tier that went up, which can newly route the action through approval under your policy.
- — A new required binary or environment variable, which is a host-readiness change rather than a code one.
The full action schema and what each field means is in Pack reference.
Preview the update#
pack update
compares each installed pack's content hash against the registry's index and reports what
it would do. --dry-run
stops there and changes nothing.
# every installed pack, no changes made $ sudo emisar pack update --dry-run redis v0.2.3 → v0.2.4 (update available) linux-core up to date (v0.4.1) billing-tools not in registry — left as-is # or validate a tree you are about to install, without loading it $ sudo emisar pack validate ./redis
A pack the registry does not carry — one you authored yourself — is reported and left alone.
Update one runner#
Name the packs you mean rather than updating everything at once, and do it on one non-critical runner first.
# move one pack to the registry's current version $ sudo emisar pack update redis # or pin an exact published version and its hash $ sudo emisar pack install redis=0.2.4 --hash sha256:… --force
- — The new tree is verified before it is activated. The tarball is fetched, validated, and hash-checked against the registry index, staged beside the live pack, and only then swapped in. An activation failure restores the previous tree immediately. This is a staged replacement with a deterministic backup, not a crash-atomic one — a host that loses power mid-swap can be left with the backup beside the pack, which the next install or update recovers.
- — A running runner reloads itself. The command signals the daemon so it re-reads the catalog and re-advertises. No restart, and runs in flight are not dropped.
- — Several packs update independently. Each one succeeds or fails on its own; the command prints per-pack lines and a count, and exits non-zero if any failed. A partial success is a normal outcome — read the lines, not just the exit code.
Trust the exact hash#
The canary now advertises new bytes. When that hash is one the published catalog carries,
the Packs page already shows the version as trusted and there is nothing to click — skip
to the proof below. Anything else shows as pending
with the exact hash it is advertising, and dispatch for it is refused. Check that hash
against the one
emisar pack list
printed on the host, then review and trust that version. Trusting snapshots the complete
action set for those exact bytes, so a later advertisement that disagrees is caught rather
than absorbed.
Then prove it: run one low-risk read-only action from the pack against the canary and confirm it succeeds and appears in the audit trail. If a published runbook uses actions from this pack, run its preflight before you go any wider — that is where a changed argument or a removed action surfaces as a broken step rather than as a failed run at three in the morning.
Rejecting the pending hash keeps the version's existing trusted bytes in force and remembers the refused ones, so a runner re-advertising the same rejected bytes stays quiet instead of re-opening the review. Only genuinely new bytes ask again.
Roll through the fleet#
Repeat the update on bounded runner groups. Because trust is account-wide and bound to the hash, a second runner arriving at the same version and hash you already trusted needs no second decision — a runner that lands on a different hash for that version is drift, and it will stop and ask. After each group, confirm the advertised versions and hashes match what you trusted and run one representative action.
Group and label mechanics are in Runner fleet; batch sizing for a production fleet is in Production rollout.
Go back to the prior version#
There is no rollback command. Recovery is installing the exact earlier version again, which works precisely because published versions are immutable — the bytes you had are still there, at the same address, with the same hash.
# the version and hash you recorded before the update $ sudo emisar pack install redis=0.2.3 --hash sha256:… --force
The --hash
pin is what makes this a recovery rather than a hope: the install aborts unless the
computed hash matches exactly. Each version carries its own trust decision. Going back to
bytes the account already trusted resumes dispatch without a new review, and so does an
exact hash the published catalog still carries — that one never needed a click here in the
first place. On first sight, a version the catalog has dropped or any other hash the
account has never trusted lands as pending.
Do this per runner, in the same bounded groups you rolled forward in, and verify the same
way. A pack you no longer want on a host at all comes off with emisar pack uninstall, which
reloads the runner so its actions leave the advertised catalog.
Retired versions#
When a published pack ships a security or critical-correctness fix, it declares a retirement floor and every version below it is retired. A runner still advertising a retired version fails closed at dispatch with a message telling the operator to update the pack. An admin can deliberately re-trust a retired version on the Packs page; that override is audited, and revoking trust clears it so a later re-trust has to decide again.
- — Floors move upward at publish time. The build that produces a catalog refuses to lower or drop a floor already in it, so a routine republish cannot quietly un-retire a version.
- — Retirement arrives with the catalog, not with a deploy. The control plane tries to refresh its configured pack catalog every ten minutes and keeps the last accepted snapshot on a fetch or validation failure, so a new floor starts blocking after the next successful refresh.
- — A retired version stays installable. The defense is at the trust gate, so rolling back into a retired version leaves you with a pack that cannot dispatch — check the floor before you plan a rollback that far back.
If you maintain the pack yourself, publishing, versioning, and declaring a retirement floor are in Author your own pack and Host your own registry.
Last reviewed August 4, 2026