memtrace stop
Stop the workspace daemon and everything it left behind: OS supervisors, the daemon process, the dashboard port, and detached memcore/Cortex sidecars.
Usage#
memtrace stopmemtrace stop shuts down the workspace daemon started by memtrace start — and cleans up everything it left running. It takes no arguments of its own. Progress is printed to stderr, phase by phase, ending with Done. The command always exits 0, even if nothing was found to stop, so scripts can't use the exit code to tell whether anything actually happened.
The built-in --help text undersells this command as just "stop the running memtrace start process." In reality it unloads OS-level supervisors first, sweeps the dashboard port, and force-reaps detached sidecar processes — see the phases below.
What it stops, in order#
Stop runs four phases in a fixed order, each designed to catch what the previous one could miss:
Phase 0 — unload OS supervisors. Before killing anything, stop unloads every Memtrace OS supervisor so nothing can respawn the daemon mid-shutdown: on macOS, launchctl bootout (falling back to legacy launchctl unload) for every *memtrace*.plist in ~/Library/LaunchAgents; on Linux, systemctl --user stop for every *memtrace*.service; on Windows, sc.exe stop for any service whose name contains "memtrace".
Phase 1 — kill the PID-file daemon. Stop picks a PID file — the workspace-scoped <memdb_data_dir>/daemon.pid if it exists, otherwise the legacy ~/.memtrace/memtrace.pid — and re-verifies the PID before touching it. It refuses to kill (and just deletes the stale file with a warning) if the PID is an ancestor of the stop process itself, its process name is on an editor/shell skiplist (code, cursor, claude, zed, vim, node, iterm2, zsh, bash, launchd, ...), or its basename isn't exactly memtrace/memtrace.exe. This guards against a stale PID file whose number got recycled by an unrelated program. If the PID passes, stop sends SIGTERM (Unix) or taskkill /F (Windows) and polls every 250ms — up to MEMTRACE_STOP_WAIT_SECS (default 60s) — for the process to actually exit, so the memcore sidecar's WAL/index flush has time to finish before stop returns.
Phase 2 — sweep the UI port. Stop always scans the dashboard port (the owner's recorded port, else MEMTRACE_UI_PORT, default 3030) and kills any surviving listener whose basename is memtrace. This is what catches daemons started by an external launcher with no PID file, a stale PID file, or a process that outlived Phase 1.
Phase 3 — reap detached sidecars. When the daemon dies, its child sidecars' handles die with it in memory but the OS processes can outlive it. Stop reaps them directly: memcore-server listening on the MemDB loopback port (default 50051, so a second workspace on a different port is untouched), plus any Cortex process matched by command line — memcortex-daemon, memcortex-mcp, cortex-feed.sh, cortex-live.sh. Cortex sidecars are matched by name across all workspaces (machine-global), while memcore-server is port-scoped. Each target gets SIGTERM, a grace window (default 3s), then SIGKILL for anything still alive.
After cleanup, if Cursor has a memtrace MCP server registered in ~/.cursor/mcp.json, stop prints a note explaining that it only stopped the :3030 runtime — Cursor spawns a separate stdio memtrace mcp process on its own whenever an agent calls a tool, and quitting Cursor entirely is the only way to end those.
$ memtrace stop Stopping Memtrace background supervisors... Stopping memtrace server (PID 41213)... Scanning port 3030... Reaping sidecars (cortex + memdb)... Done.
Environment variables#
| Variable | Default | Description |
|---|---|---|
MEMTRACE_STOP_KILL_ANY | off | Set to 1/true/yes/on to override the editor-skiplist and memtrace-basename guards when terminating PIDs found via the PID file, port scan, or sidecar patterns. Ancestor processes of the stop command are still never killed. |
MEMTRACE_STOP_WAIT_SECS | 60 | How long Phase 1 waits for the daemon to exit after SIGTERM. Raise this for very large stores where the sidecar's WAL/index flush takes longer than 60s. |
MEMTRACE_STOP_SIDECAR_GRACE_SECS | 3 | Grace window between SIGTERM and the SIGKILL fallback when reaping Cortex sidecars in Phase 3. |
MEMTRACE_UI_PORT | 3030 | Dashboard/API port swept in Phase 2. If a workspace owner state file exists, its recorded port wins over this value. |
MEMTRACE_MEMDB_LOOPBACK_PORT | 50051 | Loopback gRPC port of the memcore-server sidecar; Phase 3 only kills memcore-server processes listening on this port. |
MEMTRACE_MEMDB_DATA_DIR | <workspace root>/.memdb | Overrides the workspace data directory stop looks in first for daemon.pid and daemon-state.json, before falling back to the legacy ~/.memtrace/memtrace.pid. |
Gotchas#
Stop uses the current working directory to resolve which workspace's daemon.pid to act on. Run memtrace stop from the same workspace you started it in (or set MEMTRACE_MEMDB_DATA_DIR), otherwise it falls back to the legacy global PID file and may miss your workspace's daemon entirely.
Stop does not stop IDE-spawned stdio memtrace mcp processes — Cursor and similar clients spawn those separately per tool call, and they only end when the IDE quits. Stop prints an explanatory note instead of trying to kill them.
The exit code is always 0. Stop's stderr output is ANSI-colored and not machine-readable — there is no --json mode. If you need to confirm nothing is running afterward, use memtrace status.
Stop relies on external system tools to find and kill processes: lsof, ps, kill, pgrep on Unix; Get-NetTCPConnection, netstat, tasklist, taskkill, sc.exe on Windows. On Windows there is no graceful SIGTERM — both the terminate and hard-kill paths use taskkill /F, and the .sh-suffixed Cortex sidecar patterns match nothing there since discovery filters by Windows image name instead.
Next: check the runtime is actually clean with memtrace status, or run memtrace doctor if stop leaves something behind.