Daemon (sqryd)
Overview
sqryd is sqry’s background process. It keeps one or more workspace graphs loaded in memory for daemon-backed MCP and LSP shims, and watches loaded workspaces so it can refresh graphs after file changes. The CLI subcommand family sqry daemon and the standalone sqryd binary are two views of the same process — sqry daemon is the convenient parent-CLI surface; sqryd is the binary that actually runs.
Use sqryd when:
- You run many MCP or LSP queries per session against the same workspace.
- Your repository is large enough that the cold-start index build is noticeable.
- You want a single daemon-managed graph instance shared across an editor (LSP) and an AI assistant (MCP).
Skip sqryd for one-shot CLI invocations on small repositories — the cold-start cost is already low and the daemon adds no value at that size.
Quick start
sqry daemon start
sqry daemon load .
sqry-mcp --daemon # MCP shim — auto-starts the daemon if not running
sqry lsp --daemon # LSP shim — auto-starts the daemon if not running
sqry daemon start execs sqryd start --detach, then polls the IPC socket until the daemon signals readiness. sqry daemon load <path> registers a workspace and builds its graph in the background; daemon-backed MCP/LSP requests against that workspace then use the loaded graph.
If a shim (sqry-mcp --daemon / sqry lsp --daemon) cannot reach a daemon, it auto-starts one. Set SQRY_DAEMON_NO_AUTO_START=1 to disable the auto-start fallback.
CLI reference
| Subcommand | Description |
|---|---|
sqry daemon start | Launch sqryd start --detach and wait for readiness. |
sqry daemon stop | Send daemon/stop over IPC and wait for graceful shutdown. |
sqry daemon status [--json] | Show daemon version, uptime, memory, peak, workspace count. |
sqry daemon logs [--lines N] [--follow] | Tail the configured log file (requires log_file to be set). |
sqry daemon load <path> | Register a workspace and warm its graph. |
sqry daemon rebuild <path> | Trigger an in-place rebuild for a loaded workspace. |
sqry daemon reset <path> [--force] | Drop a workspace’s in-memory graph back to Unloaded without touching .sqry/ on disk. Used to recover from Failed/Evicted states cheaply. |
sqry daemon status --json emits a ResponseEnvelope<DaemonStatus> — a two-field object { "result": <DaemonStatus>, "meta": <ResponseMeta> }. Scripts should index through .result to reach the status fields.
sqryd binary
The binary itself exposes a small set of admin verbs:
| Verb | Description |
|---|---|
sqryd start [--detach] | Foreground or detached start. --detach is a no-op on Windows. |
sqryd foreground | Run in the foreground (alias for start without --detach). |
sqryd stop [--timeout-secs N] | Send daemon/stop and wait until the socket is unreachable. |
sqryd status [--json] | Print daemon status (human-readable or JSON). |
sqryd print-config | Print the effective daemon configuration as canonical TOML. |
sqryd install-systemd-user | (Linux) Emit a systemd user-service unit to stdout. |
sqryd install-systemd-system [--user NAME] | (Linux) Emit a templated sqryd@.service system unit to stdout. |
sqryd install-launchd | (macOS) Emit a launchd user-agent plist to stdout. |
sqryd install-windows | (Windows) Emit sc.exe create + Task Scheduler XML to stdout. |
The sqryd binary itself does not expose logs, load, rebuild, or reset — those live under the parent sqry daemon … CLI only. The platform-specific install-* verbs are gated by #[cfg(target_os = …)], so only the verb matching the current OS will show up in sqryd --help.
Configuration
Daemon settings live in a TOML file. Path resolution order:
- Linux:
$XDG_CONFIG_HOME/sqry/daemon.toml(defaults to~/.config/sqry/daemon.toml) - macOS:
~/Library/Application Support/sqry/daemon.toml - Windows:
%APPDATA%\sqry\daemon.toml
Override via SQRY_DAEMON_CONFIG=/path/to/daemon.toml.
| Field | Default | Description |
|---|---|---|
memory_limit_mb | 2048 | Maximum resident graph memory across all workspaces. LRU eviction kicks in above this. |
idle_timeout_minutes | 30 | Idle period before the daemon may shut itself down. |
debounce_ms | 2000 | File-system event debounce window. |
max_shim_connections | 256 | Concurrent MCP/LSP shim connection cap. |
log_max_size_mb | 50 | Log rotation size threshold. |
log_keep_rotations | 5 | Rotated log segments to retain. |
socket.path | platform default | Unix domain socket path. |
socket.pipe_name | sqry | Windows named-pipe leaf name (resolves to \\.\pipe\sqry). |
log_file | unset (stderr) | Required for sqry daemon logs. |
Changes require a daemon restart (sqry daemon stop && sqry daemon start).
Environment variables
Every key field has an environment-variable override. Env wins over the config file.
| Variable | Effect |
|---|---|
SQRY_DAEMON_CONFIG | Override the config-file path. |
SQRY_DAEMON_MEMORY_MB | Override memory_limit_mb. |
SQRY_DAEMON_SOCKET | Override the Unix socket path. |
SQRY_DAEMON_PIPE | Override the Windows pipe name. |
SQRY_DAEMON_LOG_FILE | Set the log-file path. |
SQRY_DAEMON_LOG_LEVEL | error, warn, info (default), debug, trace. |
SQRY_DAEMON_LOG_KEEP_ROTATIONS | Override log_keep_rotations. |
SQRY_DAEMON_TOOL_TIMEOUT_SECS | Cap per-tool execution time inside MCP host. |
SQRY_DAEMON_MAX_SHIM_CONNECTIONS | Override max_shim_connections. |
SQRY_DAEMON_STALE_MAX_AGE_HOURS | Reject queries against workspaces last refreshed beyond this age. |
SQRY_DAEMON_AUTO_START_READY_TIMEOUT_SECS | Shim auto-start readiness timeout. |
SQRY_DAEMON_NO_AUTO_START | Disable shim auto-start-on-miss when set to 1. |
IPC transport
Shims and CLI clients talk to the daemon over a length-prefixed JSON-RPC channel.
- Unix: Unix domain socket at
$XDG_RUNTIME_DIR/sqry/sqryd.sock(XDG) or$TMPDIR/sqry-<uid>/sqryd.sock(generic Unix fallback;/tmp/sqry-<uid>/sqryd.sockifTMPDIRis unset). - Windows: Named Pipe at
\\.\pipe\<socket.pipe_name>(default\\.\pipe\sqry).
Frames are 4-byte little-endian length prefixes followed by JSON. The handshake (DaemonHello / DaemonHelloResponse, ShimRegister / ShimRegisterAck) negotiates the protocol envelope version. Tool calls follow standard JSON-RPC 2.0 framing.
The shim registry enforces max_shim_connections (default 256) — admission-controlled with a ShimRegisterAck { accepted: false } and reason string "shim registry full ({current} / {cap})" when the cap is exceeded.
Workspace lifecycle
Each registered workspace transitions through a small state machine:
| State | Meaning |
|---|---|
Unloaded | Registered but no graph in memory. |
Loading | Cold or rebuild in progress. |
Loaded | Graph resident; queries serve from cache. |
Rebuilding | A file-system change triggered an incremental rebuild. |
Evicted | LRU evicted under memory pressure; the next query must reload. |
Failed | Build error; see the daemon log. |
When memory_limit_mb is exceeded, the least-recently-used workspace is evicted. Read-only graph-backed MCP tools (semantic search, callers/callees, references, definitions, cycles, etc.) transparently reload the workspace from its persisted snapshot via the shared graph acquisition layer, so WorkspaceEvicted (IPC code -32004) is no longer surfaced to those callers. The error still bubbles up if the snapshot reload itself fails (missing file, corrupt snapshot, plugin-incompatible graph) and for explicit mutating paths such as rebuild_index, where re-running the operation is the right user action.
The file-system watcher debounces editor saves over debounce_ms (default 2000 ms) and cancels in-flight rebuilds when a newer event arrives.
Observability
sqry daemon status [--json] reports daemon version, uptime, total resident memory, per-workspace high-water mark, and counts. Combine with sqry daemon logs --follow (after setting log_file) for live tracing.
The status payload’s meta field carries daemon_version and staleness flags so a remote client can detect a daemon mismatch without parsing the result body.
Lifecycle and singleton enforcement
- Pidfile + lockfile:
sqryd.pidandsqryd.locklive under$XDG_RUNTIME_DIR/sqry/(Unix XDG), the Unix tmp fallback, or%LOCALAPPDATA%\sqry\(Windows). The advisorysqryd.lockenforces single-instance ownership;sqryd.pidrecords the daemon PID and is unlinked on clean exit. - Graceful shutdown:
daemon/stop,SIGTERM, andSIGINTcancel all in-flight rebuilds before closing the socket and exiting. sqryd start --detach(Unix): the parent execscurrent_exe()with--spawned-by-clientandpre_execcallingsetsid()to detach from the controlling terminal. The detached child adopts the inherited lock FD and signals READY on a pipe; the parent waits for READY before exiting. On Windows,--detachlogs a WARN and falls back to foreground; use a Windows service wrapper for background operation.- Log rotation: rolling-file appender with
log_max_size_mb(default 50 MiB) andlog_keep_rotations(default 5). The oldest segment is deleted when the rotation count is exceeded.
Service installation
For long-running deployments, install the daemon as an OS-managed service. Each install-* verb emits the unit / plist / Windows-service definition on stdout — pipe it to the right location yourself:
# Linux (per-user, no sudo required)
sqryd install-systemd-user > ~/.config/systemd/user/sqryd.service
systemctl --user daemon-reload
systemctl --user enable --now sqryd.service
# Linux (system-wide; --user templates the %i instance)
sqryd install-systemd-system --user "$USER" | sudo tee /etc/systemd/system/sqryd@.service
sudo systemctl enable --now sqryd@$USER.service
# macOS
sqryd install-launchd > ~/Library/LaunchAgents/ai.verivus.sqry.sqryd.plist
launchctl load -w ~/Library/LaunchAgents/ai.verivus.sqry.sqryd.plist
# Windows (emits sc.exe create + Task Scheduler XML)
sqryd install-windows
The install-systemd-system subcommand uses systemd templating, so the system unit is sqryd@<username>.service, not a plain sqryd.service.
Using the daemon with MCP and LSP
# MCP shim (Claude Code, Codex, Gemini, Cursor, Windsurf)
sqry-mcp --daemon
# LSP shim (any LSP 3.17 client)
sqry lsp --daemon
The shims connect to the daemon over IPC and proxy MCP / LSP requests to the warm graph. sqry-mcp --daemon and sqry lsp --daemon both auto-start the daemon if it is not already running, unless SQRY_DAEMON_NO_AUTO_START=1.
To wire an MCP client (e.g. Claude Code) to the daemon-backed shim, add "args": ["--daemon"] to its sqry-mcp server entry — see the MCP Integration page.
Troubleshooting
- “daemon not reachable”: Run
sqry daemon status; if absent,sqry daemon start. If start fails, check the log file (setSQRY_DAEMON_LOG_FILE) and runsqryd foregroundto see startup errors directly. sqry daemon logserrors: Logging defaults to stderr. Setlog_fileindaemon.toml(orSQRY_DAEMON_LOG_FILE) before usingsqry daemon logs.WorkspaceEvicted(IPC -32004): Memory budget tripped LRU eviction and the snapshot reload also failed (or the call was a mutating one likerebuild_index). Reload the workspace withsqry daemon load <path>, raisememory_limit_mb, or runsqry index --force <path>if the on-disk snapshot is the underlying problem.- “shim registry full”: Too many concurrent shim connections. Raise
max_shim_connections(orSQRY_DAEMON_MAX_SHIM_CONNECTIONS). - Stale results after a major sqry upgrade: Rebuild the graph with
sqry daemon rebuild <path>(orrm -rf .sqry/graph && sqry index .and reload). - Stuck pidfile after a crash: If
sqryd.lockorsqryd.pidblocks startup after an unclean exit, remove the stale files in$XDG_RUNTIME_DIR/sqry/(Unix) or%LOCALAPPDATA%\sqry\(Windows) and start again. - Windows
--detachno-op: Expected — usesqryd install-windowsand run as a service for unattended operation.