hermes - 💡(How to fix) Fix [Bug]: Memory file writes do not preserve existing file permissions [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

Fix Action

Fixed

Code Example

import os
import stat
import tempfile
from pathlib import Path

from tools.memory_tool import MemoryStore

with tempfile.TemporaryDirectory() as d:
    p = Path(d) / "MEMORY.md"
    p.write_text("old", encoding="utf-8")
    os.chmod(p, 0o660)

    before = stat.S_IMODE(p.stat().st_mode)
    MemoryStore._write_file(p, ["new entry"])
    after = stat.S_IMODE(p.stat().st_mode)

    print(f"before={oct(before)} after={oct(after)}")

---

before=0o660 after=0o600

---

N/A

---



---

fd, tmp_path = tempfile.mkstemp(
    dir=str(path.parent), suffix=".tmp", prefix=".mem_"
)

try:
    with os.fdopen(fd, "w", encoding="utf-8") as f:
        f.write(content)
        f.flush()
        os.fsync(f.fileno())
    atomic_replace(tmp_path, path)

---

def _preserve_file_mode(path: Path) -> "int | None":
    ...

def _restore_file_mode(path: Path, mode: "int | None") -> None:
    ...

---

from utils import atomic_replace, _preserve_file_mode, _restore_file_mode

original_mode = _preserve_file_mode(path)

...

real_path = atomic_replace(tmp_path, path)
_restore_file_mode(Path(real_path), original_mode)
RAW_BUFFERClick to expand / collapse

Bug Description

Memory file writes do not preserve existing file permissions

Hermes' built-in memory writer appears to unintentionally change the permissions of existing memory files when saving updates.

On NixOS, with Hermes Agent v0.13.0 (2026.5.7) and Python 3.12.13, I observed that memory files changed from group-readable/group-writable permissions, such as 0660, to owner-only permissions, such as 0600, after Hermes updated them.

This is a problem for managed or group-shared deployments, where the Hermes runtime user may own the files but another user in the same group needs to read, inspect, or commit them.

Steps to Reproduce

import os
import stat
import tempfile
from pathlib import Path

from tools.memory_tool import MemoryStore

with tempfile.TemporaryDirectory() as d:
    p = Path(d) / "MEMORY.md"
    p.write_text("old", encoding="utf-8")
    os.chmod(p, 0o660)

    before = stat.S_IMODE(p.stat().st_mode)
    MemoryStore._write_file(p, ["new entry"])
    after = stat.S_IMODE(p.stat().st_mode)

    print(f"before={oct(before)} after={oct(after)}")

Observed output:

before=0o660 after=0o600

Or using the agent

  • Set permissions on .hermes/memories/*.md to 660
  • Ask Hermes to add something to memory/user files
  • Check permissions, they will be set to 600

Expected Behavior

When Hermes updates an existing memory file, it should preserve the file's existing permissions.

For example, if a memory file is 0660 before an update, it should remain 0660 afterwards.

Actual Behavior

After a memory update, an existing memory file can become 0600, removing group access.

Affected Component

Agent Core (conversation loop, context compression, memory)

Messaging Platform (if gateway-related)

N/A (CLI only)

Debug Report

N/A

Operating System

NixOS 26.05

Python Version

3.12.13

Hermes Version

v0.13.0 (2026.5.7)

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

The issue appears to be in tools/memory_tool.py, in MemoryStore._write_file():

fd, tmp_path = tempfile.mkstemp(
    dir=str(path.parent), suffix=".tmp", prefix=".mem_"
)

try:
    with os.fdopen(fd, "w", encoding="utf-8") as f:
        f.write(content)
        f.flush()
        os.fsync(f.fileno())
    atomic_replace(tmp_path, path)

tempfile.mkstemp() creates the temporary file with restrictive permissions, typically 0600. When atomic_replace() replaces the existing memory file with the temp file, the resulting file inherits the temp file's permissions rather than keeping the original target file's mode.

Proposed Fix (optional)

Hermes already appears to have helper functions in utils.py for preserving file modes across atomic writes:

def _preserve_file_mode(path: Path) -> "int | None":
    ...

def _restore_file_mode(path: Path, mode: "int | None") -> None:
    ...

These are already used by other atomic write helpers such as atomic_json_write() and atomic_yaml_write().

MemoryStore._write_file() could use the same pattern:

from utils import atomic_replace, _preserve_file_mode, _restore_file_mode

original_mode = _preserve_file_mode(path)

...

real_path = atomic_replace(tmp_path, path)
_restore_file_mode(Path(real_path), original_mode)

This would preserve the current atomic-write behaviour while avoiding unexpected permission changes on existing memory files.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING