Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Daemon and warm scans

The Unix daemon keeps one compiled scanner alive for repeated small scans. It is useful for editor saves, hooks, and other workflows where compiling the detector corpus costs more than scanning one file. It is not a second scanner implementation and it does not replace the full scan orchestrator.

Use it for repeated eligible stdin or single-file requests. Use the in-process orchestrator for repository-wide and multi-source work:

# Repeated small scans: start once, then allow the default daemon=auto policy.
keyhog daemon start
keyhog scan --stdin < changed-file.txt

# Large trees and multi-source scans always use the full in-process path.
keyhog scan --daemon=off /large/repository

A running daemon is passive until an eligible client connects. KeyHog never starts it implicitly. Conversely, a running daemon does not capture every scan: directories, multiple inputs, and policy the protocol cannot represent remain in process.

keyhog watch is separate: it is a foreground filesystem-event loop with its own compiled scanner, not a daemon client or daemon-managed process. Starting a watcher does not create the Unix socket, and daemon status does not report it. Use watch for continuous directory monitoring; use scan --daemon for an eligible stdin or single-file request sent to the separately started service.

Start, confirm readiness, and stop

keyhog daemon start
keyhog daemon status
keyhog daemon stop

daemon start first prints that compilation is in progress, then prints a separate readiness line after scanner initialization, backend validation, and socket binding complete. Only the readiness line means the daemon can accept requests. daemon status checks the existing process; it does not start one.

The default socket is $XDG_RUNTIME_DIR/keyhog.sock when XDG_RUNTIME_DIR is set. Otherwise KeyHog uses the OS user cache directory: ~/.cache/keyhog/server.sock on Linux or ~/Library/Caches/keyhog/server.sock on macOS. If the OS cache directory is unavailable (for example, a container without HOME), the fallback is the OS temporary directory plus keyhog/server.sock. Use matching daemon start --socket <PATH> and scan --daemon-socket <PATH> options for another location. The transport is a user-only Unix-domain socket. Windows has no daemon transport: it rejects daemon commands and explicit --daemon=auto|on, while an absent flag or --daemon=off uses the in-process scanner. On Unix, an absent flag has the documented auto behavior and can use a compatible daemon at the selected socket.

The service owns its startup configuration. daemon start --detectors <DIR> selects its detector corpus, --cache-dir <DIR> selects its compiled Hyperscan cache, and --backend auto|gpu|simd|cpu selects persisted autoroute or an explicit diagnostic backend for requests the service can accept. Client scan flags never rewrite those daemon-owned choices; the handshake rejects a corpus or build identity mismatch instead of silently mixing them.

An autorouted daemon initializes scanner regex state and runs a bounded real GPU warmup before announcing readiness when a physical GPU is eligible. If that GPU cannot complete the probe, startup fails loudly instead of substituting CPU/SIMD. Autoroute therefore treats a ready daemon as a persistent warm runtime: GPU decisions use calibrated warm trials. An in-process scan uses the same calibration record but includes the measured first-dispatch GPU cost. These are separate derived decisions, not one generic “GPU time.”

An explicit daemon backend is validated before the readiness line. For example, daemon start --backend gpu is rejected when this build/host has no eligible physical GPU, and --backend simd is rejected without a live Hyperscan prefilter; neither request is silently relabeled. Pre-readiness argument, configuration, or capability rejection exits 2. Operator-correctable socket path failures also exit 2, including a missing socket, permission denial, invalid path/data, connection refusal, or an already-bound socket. Other low-level operating-system I/O failures exit 3, and a selected GPU dispatch that fails during the real warmup exits 12. An explicit CPU or SIMD daemon does not warm or require the GPU; the GPU warmup is mandatory only for daemon autoroute or an explicit GPU daemon.

What --daemon means

keyhog scan has one tri-state daemon policy. On Unix, omitting the flag is the same as --daemon=auto:

PolicyCompatible daemon activeDaemon inactive, stale, or request incompatible
--daemon=auto (default on Unix)Send an eligible request to the daemon. If IPC or daemon execution fails, report it and retry the same eligible workflow in process.Stay or retry in process after reporting a connection/identity failure; an incompatible request is kept in process without sending it.
--daemon=on or bare --daemonRequire the daemon response.Fail with the specific availability, identity, or eligibility error. No in-process substitution occurs.
--daemon=offDo not connect; run in process.Run in process.

An automatic in-process retry is still an automatic scan: it uses the one-shot autoroute decision for its real workload. If that decision is missing or stale, the retry fails closed with autoroute calibration required; it does not pin a CPU backend to make the retry succeed.

Small requests the daemon can serve

The fast path accepts exactly one input: --stdin or one regular file. The client still applies the shared finding finalization needed by eligible scans, including inline suppression, allowlist/rule suppression, match resolution, and deduplication.

The in-process orchestrator is required for directories, multiple inputs, Git modes, remote/cloud/container/binary sources, baselines, Merkle skip state, live verification, explicit backend/GPU controls, calibration mode, and policy that the daemon cannot enforce exactly. Examples of incompatible policy include lockdown requirements, secret display, explicit confidence or severity floors, and custom detector/AWS-canary configuration.

In auto mode an incompatible request simply stays in process. In on mode it fails with the specific unsupported requirement. This is intentional: daemon availability must never change findings or weaken policy silently.

Repository and mass scans

Directory trees, multiple inputs, Git history, remote sources, and other high-volume workflows use the in-process orchestrator whether or not the daemon is active. That path can overlap source reads with fused scanner batches and can represent the complete source, verification, baseline, and reporting policy.

Autoroute decisions are looked up for the concrete batches produced by that workflow. A calibration entry covers one exact workload key, whose numeric dimensions are logarithmic ranges measured using a representative input; it is not proof for every byte length inside the range. If any required key is absent, KeyHog reports the missing key and fails closed instead of borrowing a neighbouring range or silently selecting CPU. Run keyhog calibrate-autoroute for the core file/tree ladder or the installer calibration for source-specific fixtures. See Autoroute calibration.

Every scan connection performs a versioned handshake that checks the daemon’s wire version, package version, Git hash, and detector-rules digest against the client. This rejects a daemon left alive across an upgrade and a same-version daemon started with a different --detectors corpus. daemon status and daemon stop intentionally tolerate an identity mismatch so the operator can inspect and terminate it; status prints the exact mismatch and the strict scan route refuses it. In --daemon=auto that refusal is visible on stderr before the identical request runs in process. In --daemon=on it is an error. Scan-result frames require suppression telemetry, dogfood telemetry, and source-coverage fields; missing fields are malformed protocol data, not permission to synthesize zeroes that could hide incomplete scanning.

Autoroute semantics

The daemon does not inherit a client process’s backend override. It loads the persisted fastest-correct decision table for its compiled detector/config/host identity and resolves each real workload bucket itself. Missing, stale, or incomplete evidence is an error, just as it is for a one-shot automatic scan.

Calibration records warm CPU/Hyperscan trials and one real GPU first dispatch followed by warm GPU trials. An in-process lookup compares the CPU/Hyperscan medians with the conservative cold-aware GPU representative. A ready daemon compares against the warm GPU median because accelerator state was initialized before requests were accepted. Scalar CPU, Hyperscan/SIMD, and the acquired GPU runtime remain peer execution classes; daemon mode is not permission to prefer GPU. GPU driver implementations are not independent autoroute candidates: the scanner exposes the single GPU runtime it acquired as the GPU class.

keyhog backend --autoroute renders separate one-shot and daemon rows for every calibrated workload. Its JSON form names the persisted cold-aware route as backend and the warm route as daemon_backend, with separate confidence, basis, and margin fields for each. Neither runtime silently executes a different backend after selection: unavailable SIMD fails, and unavailable or failed GPU execution fails with the GPU error status.

Timeouts and status

daemon start --request-timeout-secs <N> bounds the time a client may take to finish a request frame (default 300). daemon status reports uptime, scans served, active scans, detector count, and any build/corpus identity mismatch. A stale socket is removed only after ownership and directory trust checks pass.

The status counter labeled scans served currently counts completed scan attempts, including attempts that returned a daemon error. Use it as activity telemetry, not as a success counter.