hermes update Stops Sabotaging Your Work: Draining Gateways, Surviving Backends, Precise Windows Kills


Ever had this happen? A long-running task is halfway through, you casually type hermes update, and the next second the gateway is killed outright — the conversation dies mid-turn and everything you were waiting for vanishes. On Windows it was worse: the updater once killed every hermes.exe on the machine, taking unrelated installs and projects down with it. Over the past few weeks the team has been rebuilding the upgrade flow end to end (campaign #91277), and the latest batch just landed. Upgrades are now gentle: running turns drain before exit, remote connections stay up, and container-based installs are protected outright.

Updating Used to Be a Gamble

The old update pipeline was essentially a force-kill path: hermes update needed to unlock the venv files, so it tree-killed any running gateway regardless of whether it was stuck mid-turn. Identifying “which processes are mine” relied on argv-pattern scanning — imprecise, and prone to collateral damage.

On Windows the trade-off was even sharper: the gateway had to die so the update could proceed, and the old pause machinery forced you to choose between “gateway survives app close” and “update can proceed”. An update landing mid-turn simply destroyed that turn.

Improvement 1: Gateways Drain Instead of Getting Killed (#95695)

hermes update now asks a running gateway to drain and exit over its control socket, using a new pause-for-update verb: refuse new turns, finish the active one, deliver its final response, release every venv file handle, then stop — the exact drain path SIGUSR1 and service restarts already use.

If the socket can’t be reached (a step-1 gateway without the verb, or no socket at all), the client gets None and falls back to the legacy path byte-for-byte. A successful ACK carries pausing / already_stopping / pid / drain_timeout, and the updater extends its wait to the gateway’s own declared drain budget (plus 10s teardown grace) instead of force-killing a mid-turn gateway at the end of a too-short local default.

For most users this means: the turn that’s running when you upgrade gets to finish.

Improvement 2: Remote Serve Backends Survive the Update (#95576)

If you run hermes serve --host <ip> to power a remote Desktop, the old pipeline didn’t know the process existed: it was absent from the runtime inventory, invisible to --status, and once killed it was never relaunched — stranding the remote client on a dead endpoint.

Serve/dashboard backends now register themselves in the spawn ledger with their actually bound host/port/profile (deliberately self-registered identity, not another argv-pattern scan). After an update they keep running on their recorded endpoint, so remote connections are not interrupted. This also closes a long-standing asymmetry: serve backends were killable via hermes dashboard --stop yet hidden from --status — now --status lists them.

Improvement 3: The Windows Updater Kills Only This Install’s hermes.exe (#95086)

This was the scariest one. The old Windows updater ran taskkill /IM hermes.exe — an image-name-wide kill that took down every process named hermes.exe on the machine, including unrelated installs and active work (issue #91964 was a real case of the updater shim killing unrelated installs mid-run).

force_kill_other_hermes() now terminates only processes whose full executable path matches this install’s own venv Scripts directory (Toolhelp32 snapshot + QueryFullProcessImageNameW, case-insensitive path predicate). Multiple Hermes installs on one machine no longer interfere with each other — an update only ever touches its own.

Improvement 4: SSH Backends and Foreign HOMEs Are No Longer Collateral (#95641)

The stale-backend cleanup got the same discipline: the sweep no longer kills SSH-owned backends (SSH ownership is preserved during updates), and it never respawns backends from a foreign HERMES_HOME (#94030). Previously an update could kill your remote SSH sessions and processes in unrelated directories; those boundaries are now enforced.

Improvement 5: Image-Managed Installs Refuse In-Place Updates (#95722)

Docker, Nix, and apt installs should be updated by pulling a new image and rebuilding the container, not by running hermes update inside a container and mutating an immutable image. Before, the three update surfaces (hermes update, hermes update --check, and the desktop Update button) each carried their own Docker/Nix/apt heuristics — and a bind-mounted checkout inside a container looked like a git install, fooling the checks. Refusals also left no record.

The image build now bakes /etc/hermes/image-provenance.json into the image (outside both the bind-mountable checkout and the HERMES_HOME volume), and all three entry points route through one shared admission gate:

  • Marker present (even malformed) → image-managed; in-place updates are refused, the CLI exits with code 2 (refused-by-contract) and prints not updatable in place (<code>); use: <command>;
  • Marker absent → existing heuristics apply as before;
  • hermes update --plan now honestly reports updatable_in_place=False, even when a bind-mounted checkout would fool the old git heuristics.

What Your Upgrade Flow Should Look Like

Standard installs (the curl script) work as before:

hermes update --check    # review the plan first — no surprises
hermes update            # gateways drain, serve backends survive

Docker users, remember: don’t run hermes update inside a container. Pull the new image and rebuild instead:

docker pull <your-hermes-image>:latest
docker compose up -d --build   # or however you orchestrate it

Multiple installs on Windows? You can relax now — an update only touches its own. Before upgrading, hermes status shows the serve/dashboard backends so you know what’s running.

Wrapping Up

This round adds no new features, but it turns “upgrade” from something that could blow up at any moment into something you can run safely in the middle of a workday: turns finish, remote connections hold, other installs are left alone, and container installs can’t break themselves. Combined with the gateway loop watchdog and shared Docker containers we covered before, day-to-day operations get noticeably more reliable. These changes are all on main right now, not yet in a release tag — to try them today, hermes update to the latest dev build; meanwhile, the v0.20.5 release notes summarize the previous wave of operational improvements.