Skip to content

hermit-stop and hermit-watchdog can act on a session they don't own #696

Description

@gtapps

Every lifecycle script answers "is this session mine?" by deriving a name rather than checking an identity.

getSessionName(config) returns hermit-{basename(cwd)} (plugins/claude-code-hermit/scripts/lib/tmux.ts:94-97). Two projects whose directory basenames collide produce the same session name.

Impact

hermit-stop derives the session name from config alone (scripts/hermit-stop.ts:127) and --force kills whatever answers, with no ownership check:

const tree = collectTree(paneRootPids(sessionName));
tmux(["kill-session", "-t", sessionName]);

(scripts/hermit-stop.ts:182-184)

So hermit-stop --force run in one project can terminate a different project's live hermit — its claude process and its heartbeat/routine monitor subprocesses included. hermit-watchdog has the same exposure on its restart/kill paths.

A related case: when a project directory is deleted and recreated while its session is still running, the surviving session is bound to the old inode. It answers to the same name but is factually a different project generation, and nothing can tell the two apart.

Proposed fix

Stamp a session identity descriptor at creation and gate destructive operations on it, instead of inferring ownership:

  • canonical project root
  • the project directory's dev and ino (distinguishes "same path, recreated directory" from "same directory" — a path string cannot)
  • runtime mode (tmux / docker)
  • a marker plus schema version

Carry it in the tmux session environment via tmux new-session -e, read back with tmux show-environment -t <session>. Verified the round trip works: a JSON descriptor survives new-session -eshow-environment intact on tmux 3.6b.

Classify into owned-exactly / same-path-replaced-inode / foreign-path / legacy-unmarked / unreadable / missing, and refuse destructive operations on anything not exactly owned.

Blockers to settle first

  • No declared tmux version floor. plugins/claude-code-hermit/docs/always-on-ops.md:9 lists tmux with no minimum. The version that introduced new-session -e needs checking against the tmux changelog before this can be relied on, plus a capability probe and a doctor check.
  • Migration window. Sessions running today carry no descriptor, so they classify as legacy-unmarked and must degrade to current behavior until they restart. The guarantee only becomes real after a full fleet cycle.

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