Skip to content

Per-user MEMORY.md under memory/<chat_id>/ #347

@dcellison

Description

@dcellison

Multi-user broke the MEMORY.md write path silently. /var/lib/kai/memory/MEMORY.md is single-global, owned by the service user mode 644. When Kai spawns inner Claude as a non-service os_user via sudo -H -u <os_user>, the subprocess can read the file but cannot write it. The subprocess reports this in-session as "I can't write to the memory file ... (owned by the kai user, I'm running as <os_user>)".

Mem0 facts and chat history are already per-user (user_id=chat_id filters at memory.py:380/949/1038, history at history/<chat_id>/). MEMORY.md is the only plaintext memory surface still single-global.

Scope

Move MEMORY.md to DATA_DIR/memory/<chat_id>/MEMORY.md, mirroring the history layout.

  • backend.py:226 - build memory_path from chat_id, same scoping as history_dir on the line below.
  • main.py:93 _bootstrap_memory - becomes lazy per-chat-id bootstrap invoked from the per-message path, not at startup. Startup cannot know the full chat_id set.
  • install.py migration - existing memory/MEMORY.md relocates to the primary operator's chat_id dir (first user in users.yaml) on next sudo make install. One-time, idempotent.
  • install.py:1715 ownership - each memory/<chat_id>/ subdir chowned to that user's os_user, resolved from users.yaml. mem0_history.db and qdrant/ stay service-owned (the service writes them, not the subprocess).

Out of scope

  • Mem0/Qdrant: already per-user by code-layer filter; filesystem ownership unchanged.
  • history/<chat_id>/: already per-user on disk.
  • Cross-user memory sharing: not a feature; each user's MEMORY.md is theirs alone.

Tests

  • _build_context reads memory/<chat_id>/MEMORY.md for the given chat_id; does not read other users' files.
  • Bootstrap creates the per-chat dir with correct ownership on first message.
  • Migration moves legacy memory/MEMORY.md to the primary chat_id dir and leaves no stub.
  • Two chat_ids get independent MEMORY.md files; writes to one do not appear in the other.

Acceptance

Inner Claude running as any os_user can write its own chat's MEMORY.md. A user's notes do not appear in another user's context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions