Where the daemon keeps its socket, its log and its decisions. The socket's path is jevhooks_events::socket_path, which the mod calls too.

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. An error when no path is short enough to be a socket's.

18pub fn socket() -> Result<PathBuf, String> {
19    let var = |name: &str| std::env::var(name).ok();
20    jevhooks_events::socket_path(var("XDG_STATE_HOME").as_deref(), var("HOME").as_deref(), var("XDG_RUNTIME_DIR").as_deref())
21        .map(PathBuf::from)
22}

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

25pub fn lock() -> PathBuf {
26    state_dir().join("daemon.lock")
27}

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

30pub fn daemon_log() -> PathBuf {
31    state_dir().join("daemon.log")
32}

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

35pub fn decisions() -> PathBuf {
36    state_dir().join("decisions.jsonl")
37}

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.

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