Skip to content

Five HERMES_HOME / profile-isolation leaks: gateway honcho session, hardcoded SKILL paths, install.sh --dir, honcho config fallback, skill_manager_tool external_dirs write-through #5947

@vadimcomanescu

Description

@vadimcomanescu

Summary

While running hermes-agent with a non-default HERMES_HOME (a standalone profile at ~/Code/wolf/ instead of ~/.hermes/), I discovered five distinct bugs where features assume HERMES_HOME isolation but don't actually enforce it. All five were verified on the current main branch as of 2026-04-07.

Filing as one consolidated issue because they're the same category of leak. Happy to split into separate issues if preferred.

Prior art: commit 88bba31b ("fix: use `get_hermes_home()` for profile-scoped storage in supermemory") fixed a similar category in the supermemory provider on 2026-04-06, so the maintainers are already tracking this class of bug.


Bug A — Gateway never threads `session_title` through to the honcho memory provider (regression from PR #4623)

Introduced in commit `924bc67e` (PR #4623, "feat(memory): pluggable memory provider interface with profile isolation, review fixes, and honcho CLI restoration", 2026-04-02).

The honcho plugin at `plugins/memory/honcho/init.py:275-280` resolves the session key via:

```python
session_title = kwargs.get("session_title")
self._session_key = (
cfg.resolve_session_name(session_title=session_title, session_id=session_id)
or session_id
or "hermes-default"
)
```

But `grep -rn "session_title" gateway/` only finds hits in `gateway/run.py` at lines 5017, 5040, 5048, 5122, 5165, 5200 — all of which are `self._session_db.get/set_session_title(session_id, ...)` operating on the gateway's own SQLite session-title store. None of them thread `session_title` through to the `MemoryProvider.initialize()` call.

The `_init_kwargs` dict in `run_agent.py:1091-1105` passes `session_id`, `platform`, `hermes_home`, and `agent_context` but omits `session_title` entirely.

Effect: any multi-user gateway running v0.5.0+ with honcho memory silently merges all users into a single Honcho peer (`user-default-<cwd_name>`). Honcho falls back to `Path(os.getcwd()).name` as the session key. On my install I found two orphan sessions literally named `Code` and `hermes-agent` — exactly the kind of leakage this produces. Wolf only escapes by pinning the session/peer names in a local `honcho.json` override.

Fix: pass the gateway's per-chat `session_key` (or a structured `{platform, chat_id, user_id}`) through `MemoryProvider.initialize()` so honcho can produce chat-scoped session/peer names instead of a cwd-derived default.


Bug B — Ten bundled skill files hardcode `~/.hermes/skills/` (3 SKILL.md + 7 scripts/references)

`grep -rn "~/.hermes/skills" skills/` finds 10 files:

SKILL.md files (3):

  1. `skills/autonomous-ai-agents/hermes-agent/SKILL.md:295` — documentation
  2. `skills/github/github-code-review/SKILL.md:337` — `source ~/.hermes/skills/github/github-auth/scripts/gh-env.sh`
  3. `skills/red-teaming/godmode/SKILL.md` lines 63, 195, 232 — three `exec(open(...))` calls

Scripts and references (7):
4. `skills/red-teaming/godmode/scripts/parseltongue.py:14` — worst offender: literal `open("~/.hermes/skills/...")` with no `expanduser` and no env var fallback, will fail outright on any non-default HERMES_HOME
5. `skills/red-teaming/godmode/scripts/load_godmode.py:6` — comment + has env var fallback at runtime
6. `skills/red-teaming/godmode/scripts/godmode_race.py:10` — `open(os.path.expanduser("~/.hermes/skills/..."))`
7. `skills/red-teaming/godmode/scripts/auto_jailbreak.py` lines 10, 41, 63 — comment + env var fallback
8. `skills/red-teaming/godmode/references/refusal-detection.md:132`
9. `skills/red-teaming/godmode/references/jailbreak-templates.md:117`
10. `skills/github/github-repo-management/references/github-api-cheatsheet.md:9`

Fix: replace `/.hermes/skills/` with `${HERMES_HOME:-$HOME/.hermes}/skills/` in shell, or `os.environ.get("HERMES_HOME", os.path.expanduser("/.hermes"))` in Python.


Bug C — `scripts/install.sh` writes to `~/.hermes/` even when `--dir` is used

`scripts/install.sh` is 1133 lines. Relevant lines:

  • Line 31: `HERMES_HOME="$HOME/.hermes"` — hardcoded, never reassigned anywhere in the file.
  • Lines 65-68: the `--dir` flag handler: `--dir) INSTALL_DIR="$2"; shift 2 ;;` — only sets `INSTALL_DIR`, never `HERMES_HOME`.
  • Line 813: `mkdir -p "$HERMES_HOME"/{cron,sessions,logs,pairing,hooks,image_cache,audio_cache,memories,skills,whatsapp/session}` — always writes to `~/.hermes`.
  • Lines 862-873: the `skills_sync.py` invocation always targets `$HERMES_HOME/skills/`.

Effect: `install.sh --dir /custom/path` puts the hermes-agent code at `/custom/path` but still creates and populates `~/.hermes/{config.yaml, SOUL.md, .env, sessions, memories, skills, ...}` as a side effect. Confusing dual-state install.

Fix: honor `--dir` as the source of truth for both `INSTALL_DIR` and `HERMES_HOME` (unless `HERMES_HOME` is explicitly set in env), or add a separate `--hermes-home` flag.


Bug D — `plugins/memory/honcho/client.py` falls back to `~/.hermes/honcho.json` ignoring `HERMES_HOME`

File: `plugins/memory/honcho/client.py` lines 56-75. `resolve_config_path()` checks:

  1. `$HERMES_HOME/honcho.json` — honored correctly
  2. `~/.hermes/honcho.json` — hardcoded fallback, ignores HERMES_HOME
  3. `~/.honcho/config.json` — global

Effect: if a non-default profile doesn't have its own `honcho.json` yet, it silently inherits the default profile's honcho config (workspace, peer prefix, session strategy). Any fresh non-default profile setup that doesn't pre-create `honcho.json` before first run will leak the default profile's settings.

Fix: remove step 2 (or restrict it to `~/.hermes` only when `HERMES_HOME` actually points there).


Bug E — `tools/skill_manager_tool.py` mutates `external_dirs` skills on edit/patch/delete

File: `tools/skill_manager_tool.py`, functions `_edit_skill` (~line 342), `_patch_skill` (~line 395), `_delete_skill` (~line 467).

`_find_skill()` searches both the local skills dir and all `skills.external_dirs` entries. All three mutating functions then operate on `existing["path"]` — which can resolve to a path inside an external directory. If a skill name only exists in an external dir, edit/patch/delete writes through to that external location.

`_create_skill` is safe (uses `SKILLS_DIR = get_hermes_home() / "skills"` directly).

Effect: a profile that uses `external_dirs` to share another profile's skills can silently corrupt the shared tree via agent tool calls. This violates the isolation promise of `HERMES_HOME` + `external_dirs`.

Fix: detect when the resolved `existing["path"]` is outside `SKILLS_DIR = get_hermes_home() / "skills"` and either (a) refuse with an explanatory error or (b) copy-on-write the skill into the local dir before mutating.


Category summary

All five bugs share the pattern "feature assumes HERMES_HOME isolation but a specific code path hardcodes or falls back to `~/.hermes`." Same category as commit `88bba31b` (supermemory provider fix, 2026-04-06).

Happy to open PRs for any subset. Let me know if you'd like these split into separate issues or if this consolidated form is easier to triage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions