Daemon (sqryd)
Overview
sqryd is sqry’s background process. It keeps one or more workspace graphs loaded in memory and serves CLI, LSP, and MCP queries from that warm state instead of rebuilding the index on every invocation. A file-system watcher debounces edits and rebuilds incrementally in the background. 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 graph instance shared across an editor (LSP), an AI assistant (MCP), and ad-hoc CLI queries.
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 --stdio --daemon # LSP shim
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; subsequent queries against that workspace are served instantly.
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 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 (logs to stderr unless log_file is set). |
sqryd stop | Equivalent to sqry daemon stop. |
sqryd status | Equivalent to sqry daemon status. |
sqryd print-config | Dump the resolved daemon config (file + env overrides). |
sqryd install-systemd-user | Generate a systemd user-service unit. |
sqryd install-systemd-system | Generate a systemd system-service unit. |
sqryd install-launchd | Generate a macOS launchd plist. |
sqryd install-windows | Generate a Windows service wrapper config. |
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. Queries against an evicted workspace return WorkspaceEvicted (IPC code -32004); shims surface this so the caller can reload the workspace explicitly.
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:
# Linux (per-user, no sudo required)
sqryd install-systemd-user
systemctl --user enable --now sqryd.service
# Linux (system-wide)
sudo sqryd install-systemd-system
sudo systemctl enable --now sqryd.service
# macOS
sqryd install-launchd
launchctl load ~/Library/LaunchAgents/dev.sqry.sqryd.plist
# Windows (service wrapper)
sqryd install-windows
Each install-* verb writes the unit/plist/wrapper and prints the next-step command for the OS service manager.
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 --stdio --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. Reload the workspace withsqry daemon load <path>, or raisememory_limit_mb.- “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.