# NousResearch/hermes-agent · System service path mismatch when installed as root

## Open now

- Nothing open on GitHub.

A bug where the gateway system service unit uses incorrect paths when installed as root, and related fixes to remap Python, venv, and working directory paths to the target user's home directory.

## TL;DR

- 6 member(s): 0 open issue(s), 2 closed issue(s), 0 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #7109. Verify it addresses your case.
- **Recommended:** read PR #7109 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-7109`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.29`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #6989: \[Bug\] System gateway service fails with CHDIR error when installed as root — path mismatch between User= and WorkingDirectory/ExecStart
- #25282: hermes update resets gateway systemd unit paths to CLI user home, breaking service account setups

## Prior & competing PRs

- **#7109: fix(gateway): remap all paths in system service unit to target user's home**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/gateway.py`, `tests/hermes_cli/test_gateway_service.py`
  - fix hunks:
  - `hermes_cli/gateway.py`

    ```diff
    @@ -618,6 +618,24 @@ def _build_user_local_paths(home: Path, path_entries: list[str]) -> list[str]:
         return [p for p in candidates if p not in path_entries and Path(p).exists()]
     
     
    +def _remap_path_for_user(path: str, target_home_dir: str) -> str:
    +    """Remap *path* from the current user's home to *target_home_dir*.
    +
    +    If *path* lives under ``Path.home()`` the corresponding prefix is swapped
    +    to *target_home_dir*; otherwise the path is returned unchanged.
    +
    +      /root/.hermes/hermes-agent  -> /home/alice/.hermes/hermes-agent
    +      /opt/hermes                 -> /opt/hermes  (kept as-is)
    +    """
    +    current_home = Path.home().resolve()
    +    resolved = Path(path).resolve()
    +    try:
    +        relative = resolved.relative_to(current_home)
    +        return str(Path(target_home_dir) / relative)
    +    except ValueError:
    +        return str(resolved)
    +
    +
     def _hermes_home_for_target_user(target_home_dir: str) -> str:
         """Remap the current HERMES_HOME to the equivalent under a target user's home.
     
    ```

    ```diff
    @@ -665,6 +683,15 @@ def generate_systemd_unit(system: bool = False, run_as_user: str | None = None)
             username, group_name, home_dir = _system_service_identity(run_as_user)
             hermes_home = _hermes_home_for_target_user(home_dir)
             profile_arg = _profile_arg(hermes_home)
    +        # Remap all paths that may resolve under the calling user's home
    +        # (e.g. /root/) to the target user's home so the service can
    +        # actually access them.
    +        python_path = _remap_path_for_user(python_path, home_dir)
    +        working_dir = _remap_path_for_user(working_dir, home_dir)
    +        venv_dir = _remap_path_for_user(venv_dir, home_dir)
    +        venv_bin = _remap_path_for_user(venv_bin, home_dir)
    +        node_bin = _remap_path_for_user(node_bin, home_dir)
    +        path_entries = [_remap_path_for_user(p, home_dir) for p in path_entries]
             path_entries.extend(_build_user_local_paths(Path(home_dir), path_entries))
             path_entries.extend(common_bin_paths)
             sane_path = ":".join(path_entries)
    ```
  - `tests/hermes_cli/test_gateway_service.py`

    ```diff
    @@ -754,3 +754,63 @@ def test_launchd_plist_includes_profile(self, tmp_path, monkeypatch):
             plist = gateway_cli.generate_launchd_plist()
             assert "<string>--profile</string>" in plist
             assert "<string>mybot</string>" in plist
    +
    +
    +class TestRemapPathForUser:
    +    """Unit tests for _remap_path_for_user()."""
    +
    +    def test_remaps_path_under_current_home(self, monkeypatch, tmp_path):
    +        monkeypatch.setattr(Path, "home", lambda: tmp_path / "root")
    +        (tmp_path / "root").mkdir()
    +        result = gateway_cli._remap_path_for_user(
    +            str(tmp_path / "root" / ".hermes" / "hermes-agent"),
    +            str(tmp_path / "alice"),
    +        )
    +        assert result == str(tmp_path / "alice" / ".hermes" / "hermes-agent")
    +
    +    def test_keeps_system_path_unchanged(self, monkeypatch, tmp_path):
    +        monkeypatch.setattr(Path, "home", lambda: tmp_path / "root")
    +        (tmp_path / "root").mkdir()
    +        result = gateway_cli._remap_path_for_user("/opt/hermes", str(tmp_path / "alice"))
    +        assert result == "/opt/hermes"
    +
    +    def test_noop_when_same_user(self, monkeypatch, tmp_path):
    +        monkeypatch.setattr(Path, "home", lambda: tmp_path / "alice")
    +        (tmp_path / "alice").mkdir()
    +        original = str(tmp_path / "alice" / ".hermes" / "hermes-agent")
    +        result = gateway_cli._remap_path_for_user(original, str(tmp_path / "alice"))
    +        assert result == original
    +
    +
    +class TestSystemUnitPathRemapping:
    +    """System units must remap ALL paths from the caller's home to the target user."""
    +
    +    def test_system_unit_has_no_root_paths(self, monkeypatch, tmp_path):
    +        root_home = tmp_path / "root"
    +        root_home.mkdir()
    +        project = root_home / ".hermes" / "hermes-agent"
    +        project.mkdir(parents=True)
    +        venv_bin = project / "venv" / "bin"
    +        venv_bin.mkdir(parents=True)
    +        (venv_bin / "python").write_text("")
    +
    +        target_home = "/home/alice"
    +
    +        monkeypatch.setattr(Path, "home", lambda: root_home)
    +        monkeypatch.setenv("HERMES_HOME", str(root_home / ".hermes"))
    +        monkeypatch.setattr(gateway_cli, "get_hermes_home", lambda: root_home / ".hermes")
    +        monkeypatch.setattr(gateway_cli, "PROJECT_ROOT", project)
    +        monkeypatch.setattr(gateway_cli, "_detect_venv_dir", lambda: project / "venv")
    +        monkeypatch.setattr(gateway_cli, "get_python_path", lambda: str(venv_bin / "python"))
    +        monkeypatch.setattr(
    +            gateway_cli, "_system_service_identity",
    +            lambda run_as_user=None: ("alice", "alice", target_home),
    +        )
    +
    +        unit = gateway_cli.generate_systemd_unit(system=True)
    +
    +        # No root paths should leak into the unit
    +        assert str(root_home) not in unit
    +        # Target user paths should be present
    +        assert "/home/alice" in unit
    +        assert "WorkingDirectory=/home/alice/.hermes/hermes-agent" in unit
    ```
- **#7016: fix(gateway): remap python/venv/workdir to target user when installing system service as root**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#7109`
- **#7030: fix(gateway): remap all paths in system service unit to target user's home**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#7109`
- **#25298: fix(cli): preserve systemd unit paths when service user differs from CLI user**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `a1cb5fa2c7cd5239a4909261888453d97086986d`

## Fix status

- Merged to main in PR #7109 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-04-10`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `hermes_cli`
