{"repo":"NousResearch/hermes-agent","public_id":"5217","display_label":"chmod 0700 overwrites shared/system directory permissions","summary":"Multiple bugs report that Hermes aggressively applies chmod 0700 to home, skills, and parent directories on startup or update, breaking nginx serving, group-shared installs, multi-container setups, and potentially system directories including /. The accompanying fixes attempt to guard against dangerous targets, preserve existing permission bits, and allow overrides.","subsystem":"agent","counts":{"members":19,"open_issues":2,"open_issues_distinct":2,"open_prs":3,"closed_issues":4,"merged_prs":3,"attempt_prs":10},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":6993},"resolution":{"state":"partially_resolved","by":"pr-6993,pr-29670,pr-109168"},"freshness":{"state":"verified","last_verified_at":"2026-09-12T15:43:44+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.08654970760233917},"fix_edges":[{"issue_number":6991,"pr_number":6993,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":10757,"pr_number":109168,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":25821,"pr_number":29670,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":5217,"title":"[Bug]: Running hermes-agent get error \"permission denied\" on the second start of docker container","url":null,"state":"closed"},{"kind":"issue","number":6991,"title":"[Bug]: _secure_dir() resets ~/.hermes to 0700 on every gateway start, breaking nginx serving from subdirectories","url":null,"state":"closed"},{"kind":"issue","number":10757,"title":"[Bug]: Agent repeatedly resets $HERMES_DATA_DIR to chmod 700, breaking multi-container setups with hermes-webu","url":null,"state":"closed"},{"kind":"issue","number":25821,"title":"Bug: os.chmod(path.parent, 0o700) bricks host when path.parent resolves to /","url":null,"state":"closed"},{"kind":"issue","number":67496,"title":"skills_sync: _rmtree_writable error handler chmods the skills root to 0700, breaking group-shared installs on every update","url":null,"state":"open"},{"kind":"issue","number":68055,"title":"config: _secure_dir() chmods through a symlinked $HERMES_HOME subdir, clamping shared skills dirs to 0700 on every load_config()","url":null,"state":"open"},{"kind":"pr","number":6993,"title":"fix(config): allow HERMES_HOME_MODE env var to override _secure_dir() permissions","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/config.py"],"fix_hunks":[{"file":"hermes_cli/config.py","hunks":["@@ -158,16 +158,27 @@ def get_project_root() -> Path:\n     return Path(__file__).parent.parent.resolve()\n \n def _secure_dir(path):\n-    \"\"\"Set directory to owner-only access (0700). No-op on Windows.\n+    \"\"\"Set directory to owner-only access (0700 by default). No-op on Windows.\n \n     Skipped in managed mode — the NixOS module sets group-readable\n     permissions (0750) so interactive users in the hermes group can\n     share state with the gateway service.\n+\n+    The mode can be overridden via the HERMES_HOME_MODE environment variable\n+    (e.g. HERMES_HOME_MODE=0701) for deployments where a web server (nginx,\n+    caddy, etc.) needs to traverse HERMES_HOME to reach a served subdirectory.\n+    The execute-only bit on a directory permits cd-through without exposing\n+    directory listings.\n     \"\"\"\n     if is_managed():\n         return\n     try:\n-        os.chmod(path, 0o700)\n+        mode_str = os.environ.get(\"HERMES_HOME_MODE\", \"\").strip()\n+        mode = int(mode_str, 8) if mode_str else 0o700\n+    except ValueError:\n+        mode = 0o700\n+    try:\n+        os.chmod(path, mode)\n     except (OSError, NotImplementedError):\n         pass\n \n"]}]},{"kind":"pr","number":6994,"title":"fix(config): allow HERMES_HOME permissions override via HERMES_HOME_M…","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#6993","number":6993}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":7003,"title":"fix(config): use 0o701 for HERMES_HOME to allow web server traversal","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#6993","number":6993}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":25841,"title":"fix(security): guard os.chmod(parent) against / and system dirs (#25821)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#29670","number":29670},{"kind":"pr","value":"#25831","number":25831}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":26003,"title":"fix: guard os.chmod(path.parent, 0o700) against system directories","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#25841","number":25841},{"kind":"pr","value":"#25831","number":25831},{"kind":"pr","value":"#25841","number":25841}],"category":"superseded_by_pr","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":27384,"title":"fix(#25821): refuse to chmod top-level dirs from 5 token-storage call sites","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#25831","number":25831},{"kind":"pr","value":"#25841","number":25841},{"kind":"pr","value":"#25821","number":25821},{"kind":"pr","value":"#26003","number":26003}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":29670,"title":"fix(security): refuse to chmod(parent, 0o700) on / or top-level dirs (#25821)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/google_oauth.py","hermes_cli/auth.py","hermes_constants.py","tests/test_hermes_constants.py","tools/mcp_oauth.py"]},{"kind":"pr","number":67508,"title":"fix(skills): preserve group/other perms in _rmtree_writable error handler (#67496)","url":null,"state":"closed","close_reason":{"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":67511,"title":"fix(skills): merge owner bits in _rmtree_writable error handler instead of replacing mode (#67496)","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":67525,"title":"fix(skills): preserve existing permissions in _rmtree_writable (#67496)","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"0d632bcdd4ba","number":0},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#67511","number":67511},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#67511","number":67511},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#67511","number":67511},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#67511","number":67511},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#68193","number":68193},{"kind":"pr","value":"#374151","number":374151},{"kind":"pr","value":"#67496","number":67496},{"kind":"pr","value":"#67508","number":67508},{"kind":"pr","value":"#67525","number":67525},{"kind":"pr","value":"#68193","number":68193}],"category":"partial_fix","implication":"needs_llm"},"merged":false},{"kind":"pr","number":68059,"title":"fix(config): skip symlinked HERMES_HOME subdirs in _secure_dir (#68055)","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"768f42f72ff09ff37cb857141d94405529ad9bdc","number":0}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":68193,"title":"fix(skills_sync): merge owner-write into mode instead of clamping to 0o700 (#67496)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#67508","number":67508}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":109168,"title":"Containers no longer force HERMES_HOME to 0700, so shared data mounts keep their mode (#10757)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["cron/jobs.py","hermes_cli/config.py","tests/cron/test_file_permissions.py","website/docs/user-guide/docker.md"]}],"trust":{"default_tier":"verified","includes_suggested":false}}