Skip to content

fix(update): scope install-method stamp to the code tree, not $HERMES_HOME - #48188

Merged
benbarclay merged 1 commit into
mainfrom
hermes/fix-install-method-scope
Jun 18, 2026
Merged

benbarclay merged 1 commit into
mainfrom
hermes/fix-install-method-scope

Conversation

@benbarclay

Copy link
Copy Markdown
Contributor

Summary

Fixes a class of "Desktop app cannot update" / "hermes update doesn't apply inside the Docker container" failures that hit users who run a Docker-backed gateway and a host install (macOS Desktop app, CLI) that share one ~/.hermes — the exact setup the Docker docs encourage (~/.hermes:/opt/data).

The bug

The install method (docker/git/pip/…) describes the binary that is running, but detect_install_method() read it from $HERMES_HOME/.install_method — a shared data directory. When a containerized gateway and a host install share one $HERMES_HOME, that file is a single slot describing two different installs:

  1. The published image stamps docker into the shared dir on every boot (docker/stage2-hook.sh).
  2. The host Desktop app reads the same dir, sees docker, and its in-app updater refuses to run (cmd_updateformat_docker_update_message), looping on "UPDATE DIDN'T FINISH."
  3. Reinstalling from the DMG doesn't help — the contaminated stamp is re-read every launch.

This is a category error: an install-identity marker was written into the data-sharing directory. It also collides with the #34397 design ("trust the stamp"), which assumed one install per $HERMES_HOME.

The fix (code-scoped stamp)

The running process knows its own install method without consulting a shared file, so scope the stamp to the install tree:

  • detect_install_method() reads <install tree>/.install_method first (next to the running code — immune to the shared data dir). Falls back to the legacy $HERMES_HOME stamp for back-compat, but ignores a docker home-stamp when not actually containerized — so already-poisoned shared homes self-heal.
  • stamp_install_method() writes the code-scoped stamp.
  • scripts/install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
  • Dockerfile bakes docker into /opt/hermes/.install_method at build time (inside the immutable block).
  • docker/stage2-hook.sh no longer writes the home stamp, and removes a stale docker one so existing shared homes recover on next boot.

Genuine containers still resolve to docker (baked stamp, or legacy home stamp honored when containerized). Unstamped installs in generic containers still fall through to git/pip — the #34397 fix is preserved.

How affected users fix an already-poisoned setup

After this lands, no manual action is needed in most cases — but for an existing shared ~/.hermes running an older image, any one of these resolves it immediately:

  1. Re-pull the gateway image (docker compose pull hermes && docker compose up -d --force-recreate hermes). The new image's stage2 hook deletes the stale docker stamp from the shared dir on boot. The host Desktop/CLI install then detects correctly and hermes update works again.
  2. Or re-run the host installer (curl … install.sh), which writes the code-scoped git stamp next to the host checkout — that wins over the shared dir regardless of what the container stamps.
  3. Or, as a one-liner without waiting for either, delete the contaminating marker so detection falls through to the real .git/pip signal:
    rm -f ~/.hermes/.install_method
    (Safe: a genuine container relies on the baked /opt/hermes stamp, not this one; a host install re-derives git/pip from its checkout.)

Tests

  • tests/hermes_cli/test_pip_install_detection.py — code-scoped stamp wins over a contaminating home docker stamp; docker home-stamp ignored on host (self-heal) but honored in-container; non-docker legacy home stamps still respected; stamp_install_method writes code-scoped. Existing [Bug]: Hermes dont discriminate between running in a docker container and being the docker image. #34397 container tests still pass.
  • tests/tools/test_dockerfile_immutable_install.pydocker stamp baked inside the immutable /opt/hermes block.
  • tests/tools/test_stage2_hook_install_method_stamp.py — stage2 no longer writes the home stamp and heals a stale docker one.
  • tests/test_install_sh_install_method_stamp.py — installer stamps $INSTALL_DIR, never $HERMES_HOME.

All affected suites green; verified end-to-end that the pre-fix home-first logic returned docker on the host (reproducing the bug) while the new logic resolves git.

Scope note for review

Touches hermes_cli/config.py and the install-method contract established by #34397 — general, non-Docker-scoped runtime behavior, so this wants core review rather than the Docker lane.

Infographic

install-method-stamp-code-scoped

…_HOME

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the #34397 fix).
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/fix-install-method-scope vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10996 on HEAD, 10995 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/tools/test_stage2_hook_install_method_stamp.py:27: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 5761 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working comp/cli CLI entry point, hermes_cli/, setup wizard area/docker Docker image, Compose, packaging P2 Medium — degraded but workaround exists labels Jun 18, 2026
@benbarclay
benbarclay merged commit 4440d77 into main Jun 18, 2026
35 checks passed
@benbarclay
benbarclay deleted the hermes/fix-install-method-scope branch June 18, 2026 04:14

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Comment

Scopes the install-method stamp to the code tree instead of $HERMES_HOME, ensuring the stamp follows the code version rather than the config directory. 8 files across update/install paths.

Looks Good

  • Well-scoped to the update module
  • Correct path resolution

Note

Multi-file change across install, cli, and hermes_cli submodules — verify rollback path if stamp format changes in future versions.


Reviewed by Hermes Agent

waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…_HOME (NousResearch#48188)

The install method (docker/git/pip/...) describes the *running binary*, but
detect_install_method() read it from $HERMES_HOME/.install_method — a shared
DATA directory. The Docker docs deliberately bind-mount $HERMES_HOME
(~/.hermes:/opt/data) so config/sessions/memory persist and can be shared with
a host-side Desktop/CLI install.

When a containerized gateway and a host install share one $HERMES_HOME, the
home-scoped stamp is a single slot describing two installs: the published image
stamps 'docker' on every boot, the host install then reads 'docker' and the
in-app updater refuses to run 'hermes update' ("doesn't apply inside the Docker
container"). Reinstalling the Desktop app from the DMG doesn't help because the
contaminated stamp is re-read every time.

Fix (option 1 — code-scoped stamp):
- detect_install_method() reads <install tree>/.install_method first (next to
  the running code, immune to the shared data dir). It falls back to the legacy
  $HERMES_HOME stamp for back-compat, but IGNORES a 'docker' home stamp when
  not actually containerized — so already-poisoned shared homes self-heal.
- stamp_install_method() writes the code-scoped stamp.
- install.sh stamps $INSTALL_DIR instead of $HERMES_HOME.
- Dockerfile bakes 'docker' into /opt/hermes/.install_method at build time
  (inside the immutable block); stage2-hook.sh no longer writes the home stamp
  and proactively removes a stale 'docker' one to heal existing shared homes.

Genuine containers still resolve to 'docker' (baked stamp, or legacy home stamp
honored when containerized). Unstamped installs in generic containers still fall
through to git/pip (preserves the NousResearch#34397 fix).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/docker Docker image, Compose, packaging comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants