Skip to content

server: relocate .server.pid to $XDG_RUNTIME_DIR so ~/.memtomem/ stays lazy #412

@memtomem

Description

@memtomem

Problem

memtomem-server writes ~/.memtomem/.server.pid at startup to hold an
advisory fcntl.flock for the server-lifetime and to give the uninstall
command a liveness probe target. That single write is the last thing
keeping ~/.memtomem/ from being fully lazy — #399 Phase 3 (ea 00e948b)
moved the SQLite DB + scheduler startup to the first tool call, but
Path("~/.memtomem").mkdir(...) in server/__init__.py:main still fires
on every MCP handshake (claude mcp list, Cursor/Windsurf probe, etc.),
even for a client that connects and never calls a tool.

Secondary concern: .server.pid is transient runtime state (auto-release
on process death), but it lives beside persistent data
(memtomem.db, config.json, memories/). Mixing the two means a
rm -rf ~/.memtomem workflow also deletes runtime-state knowledge about
live writers, and tooling has to special-case .server.pid when walking
the state dir.

This was called out as out-of-scope in the #399 Phase 3 plan and
referenced in #384 / #387 as "relocate to $XDG_RUNTIME_DIR".

Proposal

Relocate the server pid / lock file from ~/.memtomem/.server.pid to:

  • Linux w/ systemd: $XDG_RUNTIME_DIR/memtomem/server.pid
    (e.g. /run/user/1000/memtomem/server.pid). Kernel auto-cleans at
    logout; no stale files after reboot.
  • macOS, Linux w/o $XDG_RUNTIME_DIR, BSD: {tempfile.gettempdir()}/memtomem-{uid}/server.pid.
    On macOS this resolves to the per-user /var/folders/.../T/memtomem-{uid}/
    (already user-owned); on Linux fallback it's /tmp/memtomem-{uid}/
    created with mode 0700.
  • Windows: server is POSIX-only (fcntl), so not relevant for the
    write side. Uninstall probe is a no-op there.

With this change, ~/.memtomem/ is created only by code paths that
actually need persistent storage — index, add, first tool call. A user
who only ever runs claude mcp list against a clean machine gets a
truly empty home, closing the last gap from #399.

Design

  1. New helper memtomem._runtime_paths.runtime_dir() -> Path:
    • Check $XDG_RUNTIME_DIR; if set and is a dir, use {XDG}/memtomem.
    • Otherwise {tempfile.gettempdir()}/memtomem-{uid}.
    • mkdir(mode=0o700, exist_ok=True) on first access.
  2. Server (server/__init__.py:main) writes runtime_dir() / "server.pid"
    (drops the leading dot — dedicated runtime dir, no need to hide).
  3. Uninstall (cli/uninstall_cmd.py:_check_server_liveness) probes the
    new location; during the transition period also probes the legacy
    ~/.memtomem/.server.pid so a mixed-version upgrade (old server
    running, new uninstall) still refuses correctly.
  4. Uninstall cleanup removes the legacy file if present but does NOT
    rmdir $XDG_RUNTIME_DIR/memtomem — the kernel handles it.

Non-goals

  • Does not fix mm uninstall liveness check only sees MCP server pid — silently ignores mm web and other DB writers #384 (liveness probe only sees MCP server, ignores
    mm web / mm watchdog). That's a separate expansion — register
    per-process lock files and scan the directory. Out of scope here; the
    new runtime dir is the right place for those future lock files,
    but adding them is its own PR.
  • No change to .current_session. Session state is persistent
    (mm remembers the user's current session across reboots); it stays
    in the state dir.

Backward compat

  • Legacy servers (pre-relocation) still write ~/.memtomem/.server.pid.
    New uninstall probe checks both locations for one release cycle.
  • Next-major drops the legacy probe.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions