fix: preserve file permissions on atomic writes (Docker/NAS fix) - #10618
Merged
Conversation
atomic_yaml_write() and atomic_json_write() used tempfile.mkstemp() which creates files with 0o600 (owner-only). After os.replace(), the original file's permissions were destroyed. Combined with _secure_file() forcing 0o600, this broke Docker/NAS setups where volume-mounted config files need broader permissions (e.g. 0o666). Changes: - atomic_yaml_write/atomic_json_write: capture original permissions before write, restore after os.replace() - _secure_file: skip permission tightening in container environments (detected via /.dockerenv, /proc/1/cgroup, or HERMES_SKIP_CHMOD env) - save_env_value: preserve original .env permissions, remove redundant third os.chmod call - remove_env_value: same permission preservation On desktop installs, _secure_file() still tightens to 0o600 as before. In containers, the user's original permissions are respected. Reported by Cedric Weber (Docker/Portainer on NAS).
konsisumer
added a commit
to konsisumer/hermes-agent
that referenced
this pull request
Apr 16, 2026
Resolve conflict in utils.py between this branch's umask-honoring fix (issue NousResearch#9239, NixOS /save) and main's preserve-existing-mode fix (PR NousResearch#10618, Docker/NAS). The merged policy: when an existing file is overwritten, restore its prior permissions (Docker/NAS); when a new file is created, apply 0o666 & ~umask — the mode a plain open() would yield — so NixOS managed mode (umask 0007) gets 0o660 instead of 0o600. The overwrite-permissions test in tests/test_utils_atomic_write_permissions.py is updated to assert the Docker/NAS preserve semantics, matching main's policy.
7 tasks
aj-nt
pushed a commit
to aj-nt/hermes-agent
that referenced
this pull request
May 1, 2026
…sResearch#10618) atomic_yaml_write() and atomic_json_write() used tempfile.mkstemp() which creates files with 0o600 (owner-only). After os.replace(), the original file's permissions were destroyed. Combined with _secure_file() forcing 0o600, this broke Docker/NAS setups where volume-mounted config files need broader permissions (e.g. 0o666). Changes: - atomic_yaml_write/atomic_json_write: capture original permissions before write, restore after os.replace() - _secure_file: skip permission tightening in container environments (detected via /.dockerenv, /proc/1/cgroup, or HERMES_SKIP_CHMOD env) - save_env_value: preserve original .env permissions, remove redundant third os.chmod call - remove_env_value: same permission preservation On desktop installs, _secure_file() still tightens to 0o600 as before. In containers, the user's original permissions are respected. Reported by Cedric Weber (Docker/Portainer on NAS).
02356abc
pushed a commit
to 02356abc/hermes-agent
that referenced
this pull request
May 14, 2026
…sResearch#10618) atomic_yaml_write() and atomic_json_write() used tempfile.mkstemp() which creates files with 0o600 (owner-only). After os.replace(), the original file's permissions were destroyed. Combined with _secure_file() forcing 0o600, this broke Docker/NAS setups where volume-mounted config files need broader permissions (e.g. 0o666). Changes: - atomic_yaml_write/atomic_json_write: capture original permissions before write, restore after os.replace() - _secure_file: skip permission tightening in container environments (detected via /.dockerenv, /proc/1/cgroup, or HERMES_SKIP_CHMOD env) - save_env_value: preserve original .env permissions, remove redundant third os.chmod call - remove_env_value: same permission preservation On desktop installs, _secure_file() still tightens to 0o600 as before. In containers, the user's original permissions are respected. Reported by Cedric Weber (Docker/Portainer on NAS).
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…sResearch#10618) atomic_yaml_write() and atomic_json_write() used tempfile.mkstemp() which creates files with 0o600 (owner-only). After os.replace(), the original file's permissions were destroyed. Combined with _secure_file() forcing 0o600, this broke Docker/NAS setups where volume-mounted config files need broader permissions (e.g. 0o666). Changes: - atomic_yaml_write/atomic_json_write: capture original permissions before write, restore after os.replace() - _secure_file: skip permission tightening in container environments (detected via /.dockerenv, /proc/1/cgroup, or HERMES_SKIP_CHMOD env) - save_env_value: preserve original .env permissions, remove redundant third os.chmod call - remove_env_value: same permission preservation On desktop installs, _secure_file() still tightens to 0o600 as before. In containers, the user's original permissions are respected. Reported by Cedric Weber (Docker/Portainer on NAS).
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…sResearch#10618) atomic_yaml_write() and atomic_json_write() used tempfile.mkstemp() which creates files with 0o600 (owner-only). After os.replace(), the original file's permissions were destroyed. Combined with _secure_file() forcing 0o600, this broke Docker/NAS setups where volume-mounted config files need broader permissions (e.g. 0o666). Changes: - atomic_yaml_write/atomic_json_write: capture original permissions before write, restore after os.replace() - _secure_file: skip permission tightening in container environments (detected via /.dockerenv, /proc/1/cgroup, or HERMES_SKIP_CHMOD env) - save_env_value: preserve original .env permissions, remove redundant third os.chmod call - remove_env_value: same permission preservation On desktop installs, _secure_file() still tightens to 0o600 as before. In containers, the user's original permissions are respected. Reported by Cedric Weber (Docker/Portainer on NAS).
19 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes dashboard config saves resetting file permissions in Docker/NAS deployments, causing
Permission deniederrors and provider auth failures.Reported by: Cedric Weber (Docker/Portainer on NAS)
Root Cause
Three layers of permission clobbering:
atomic_yaml_write()/atomic_json_write()—tempfile.mkstemp()creates temp files with0o600. Afteros.replace(), the original file's permissions are destroyed._secure_file()— explicitly sets0o600after every config savesave_env_value()— has its own inline mkstemp + replace + _secure_file + redundant os.chmod (triple enforcement)In Docker, volume-mounted config.yaml/env files may need broader permissions (e.g.
0o666). After any dashboard save, they get hammered to0o600, breaking multi-process access.Changes
utils.py:_preserve_file_mode()/_restore_file_mode()helpersatomic_yaml_writeandatomic_json_writenow capture original permissions before write and restore afteros.replace()hermes_cli/config.py:_is_container()— detects Docker/Podman/LXC/k8s via/.dockerenv,/proc/1/cgroup, orHERMES_SKIP_CHMOD/HERMES_CONTAINERenv vars_secure_file()— skips chmod in containers (matches existingis_managed()pattern for NixOS)save_env_value()— preserves original permissions, removed redundant thirdos.chmodremove_env_value()— same permission preservationBehavior
Test Plan