Skip to content

fix(core): isolate HOME per profile to persist system credentials and…#4437

Closed
devorun wants to merge 1 commit into
NousResearch:mainfrom
devorun:patch-25
Closed

fix(core): isolate HOME per profile to persist system credentials and…#4437
devorun wants to merge 1 commit into
NousResearch:mainfrom
devorun:patch-25

Conversation

@devorun

@devorun devorun commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

… fix Docker setup (#4426)

What does this PR do?

Fixes #4426

The Issue:
Previously, Hermes correctly set HERMES_HOME for each profile but left the system's HOME environment variable untouched (defaulting to /root in Docker). This caused two critical bugs:

  1. System tools (git, ssh, gh, npm) wrote their configurations to the shared /root/ directory, causing credentials to leak across different agent profiles.
  2. In Docker environments, tool configurations were lost upon container recreation because /root/ is not part of the persistent /opt/data volume.

The Fix:
Modified resolve_profile_env to automatically inject a per-profile HOME directory (<profile_dir>/home) into os.environ.
This ensures that all subprocesses and system tools write their configurations (like .gitconfig and .ssh/) directly into the profile's isolated and persistent storage, fixing both the isolation leak and the Docker persistence issue simultaneously.

Related Issue

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

How to Test

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform:

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

For New Skills

  • This skill is broadly useful to most users (if bundled) — see Contributing Guide
  • SKILL.md follows the standard format (frontmatter, trigger conditions, steps, pitfalls)
  • No external dependencies that aren't already available (prefer stdlib, curl, existing Hermes tools)
  • I've tested the skill end-to-end: hermes --toolsets skills -q "Use the X skill to do Y"

Screenshots / Logs

@ferranbonas

Copy link
Copy Markdown

Thanks for the quick fix! Worth noting that setting $HOME won't cover tools that resolve the home directory via getpwuid(getuid()) at runtime. Those will still see /root regardless of $HOME. Might be worth documenting this limitation or considering a more complete solution long-term.

@ferranbonas

ferranbonas commented Apr 1, 2026

Copy link
Copy Markdown

Another edge case: Playwright installs browser binaries to $HOME/.cache/ms-playwright/ at build time, which resolves to /root/.cache/ during docker build. After this fix, HOME moves to the profile directory at runtime and Playwright won't find its binaries, even with a single profile.
Setting PLAYWRIGHT_BROWSERS_PATH to a fixed path inside the image like /opt/hermes/.playwright doesn't fully solve it either, as that path doesn't survive docker pull + recreate.
The clean fix would be moving Playwright installation to entrypoint.sh with PLAYWRIGHT_BROWSERS_PATH pointing to somewhere inside /opt/data, so it installs once and persists in the volume. Something like:
if [ ! -d "$PLAYWRIGHT_BROWSERS_PATH" ]; then npx playwright install --with-deps chromium --only-shell fi
with ENV PLAYWRIGHT_BROWSERS_PATH=/opt/data/.playwright"

@devorun

devorun commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ferranbonas

Your proposed fix for Playwright makes sense. I'll add the entrypoint.sh and Dockerfile changes to this PR so the binaries persist in the volume.

I'll also include a quick note in the docs about the getpwuid(getuid()) limitation.

@teknium1

teknium1 commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR and the thorough issue writeup in #4426 — the problems you identified (credential sharing across profiles, Docker non-persistence) are real.

Unfortunately, mutating HOME globally would break the profiles system itself. Several core functions are deliberately anchored to Path.home():

  • _get_profiles_root()Path.home() / ".hermes" / "profiles"
  • _get_default_hermes_home()Path.home() / ".hermes"
  • _get_wrapper_dir()Path.home() / ".local" / "bin"
  • systemd service discovery → Path.home() / ".config" / "systemd" / ...

This is by design — profile operations need the real user HOME so that hermes -p coder profile list can see all profiles regardless of which one is active. Overriding HOME would also affect every Python library and system tool in the process (os.path.expanduser, pip, virtualenv, shell ~ expansion, etc.).

The right approach for credential isolation would be tool-specific env vars (GIT_CONFIG_GLOBAL, GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL, XDG_CONFIG_HOME, etc.) set per-profile, and for Docker persistence, documenting an additional volume mount for /root. We'll keep #4426 open to track this.

Appreciate the contribution!

@teknium1 teknium1 closed this Apr 1, 2026
@ferranbonas

Copy link
Copy Markdown

Thanks for taking the time to review and explain the internals! I suspected there might be deeper Python-level issues I wasn't seeing. Appreciate the thorough response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: profiles do not isolate system credentials (/root shared across profiles) and Docker setup does not persist tool configuration outside /opt/data

3 participants