Where the daemon keeps its socket, its log and its decisions. The mod computes the same socket path from the same two variables (bridge.ts).

4use std::path::PathBuf;

$XDG_STATE_HOME/jevhooks, or ~/.local/state/jevhooks.

7pub fn state_dir() -> PathBuf {
8    let base = std::env::var_os("XDG_STATE_HOME")
9        .filter(|v| !v.is_empty())
10        .map(PathBuf::from)
11        .or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".local/state")))
12        .unwrap_or_else(|| PathBuf::from("."));
13    base.join("jevhooks")
14}

The one daemon's socket; every session's mod and MCP server talk to it.

17pub fn socket() -> PathBuf {
18    state_dir().join("daemon.sock")
19}

Held for as long as a daemon runs, so two cannot serve at once.

22pub fn lock() -> PathBuf {
23    state_dir().join("daemon.lock")
24}

The daemon's stderr, since nothing else is attached to it.

27pub fn daemon_log() -> PathBuf {
28    state_dir().join("daemon.log")
29}

Every decision and what became of it, one JSON object per line.

32pub fn decisions() -> PathBuf {
33    state_dir().join("decisions.jsonl")
34}

Which build this process is: the executable's device, inode, size and modification time. A rebuilt binary is a different identity, which is how a new session's MCP server knows the running daemon is stale.

39pub fn identity() -> String {
40    use std::os::unix::fs::MetadataExt;
41    match std::env::current_exe().and_then(std::fs::metadata) {
42        Ok(meta) => format!("{}:{}:{}:{}", meta.dev(), meta.ino(), meta.len(), meta.mtime_nsec() as i128 + meta.mtime() as i128 * 1_000_000_000),
43        Err(_) => "unknown".to_owned(),
44    }
45}