Table of Contents
Investigate
stormpulse investigate runs one-shot diagnostics that end in a verdict,
not a log dump. Each investigation checks a fixed set of suspects and
prints a case file: what was ruled out, what the evidence points at,
and what could not be checked - with the exact command that would supply
the missing evidence.
$ stormpulse investigate
Investigations (run: stormpulse investigate <name>):
flaps agent websocket reconnect churn
box host starvation, reboots, upgrades, kernel faults
logs-pipeline per-group shipping health and parser drift
garage health garage daemon restarts and maintenance load (run: stormpulse garage investigate health)
Reading a case file
CASE FILE: flaps - agent websocket reconnect churn
Window: 2026-07-19 06:00 → now
Receipt: earned 2026-07-19: the alpha flap storm ...
VERDICTS
IMPLICATED reconnect churn
14 drops: 8 x keepalive timeout (pings unanswered 20s), ...
CLEARED refresh storm
0 garage_refresh results in window (2 commands total).
IMPLICATED process/box freeze
5 log lines reached journald >= 15s late; worst 48s at 2026-07-19T10:19:24.
INCONCLUSIVE kernel faults
System journal not readable as this user.
run: sudo journalctl -k --since "2026-07-19 06:00" ...
Three verdicts, always:
- CLEARED - checked and ruled out, with the measurement that rules it out. A cleared suspect is a finding, not the absence of one.
- IMPLICATED - the evidence points here. One check rarely proves a root cause; it tells you where to dig next.
- INCONCLUSIVE - the check could not see. The
run:line is the command that would supply the evidence. An investigation never escalates privileges itself, and an unwitnessed window is never reported as healthy.
After the verdicts: next moves (what to run next, in order) and open questions (things only you can answer, like "was that reboot yours?").
Windows
All investigations accept a time window:
stormpulse investigate flaps --since 6h
stormpulse investigate box --since "2026-07-19 06:00" --until "2026-07-19 07:10"
--since/--until take 90m / 24h / 7d or YYYY-MM-DD [HH:MM].
Default: the last 24 hours.
The investigations
flaps - why the agent keeps reconnecting. Tallies drops by error family (keepalive timeout = the peer, or this process's own starved event loop, stopped answering pings for 20s; handshake timeout = the backend couldn't accept within 10s; abrupt TCP drop = a process died or restarted). Rules the refresh path and log shipping in or out. Its sharpest check compares each log line's own timestamp against journald's receipt time: a large gap means the process or the whole box was frozen, a signature that shows up even when nothing else errors.
box - what starved the host. Point-in-time CPU steal and iowait, reboots in the window checked against unattended-upgrades' scheduled reboots (a boot at the scheduled time is routine, not an anomaly), package-manager activity correlated to the window, and kernel rcu/stall/hung/oom lines. A clean kernel log does not acquit the hypervisor: a full host-side pause leaves no in-guest trace except the freeze signature above.
logs-pipeline - per log group: is shipping healthy, and when a group
ships nothing but drops everything, is that a source whose format
drifted or noise the parser suppresses on purpose? It decides by feeding
a raw sample from the source through the group's own parser. Two numbers
that mislead if read naively: a duration_ms pinned near
ship_interval x 0.9 is the drain window working as designed, and
dropped includes deliberately suppressed lines, not just unparseable
ones.
garage health - declared by the Garage integration. Detects daemon shutdown waves (many workers exiting within seconds = Garage went down; someone sent SIGTERM) and maintenance load (scrub, resync, snapshot, compaction) inside the window.
For integration authors
An integration declares investigations on its contract
(investigations=(InvestigationSpec(name, title, run),)). The runner
receives the parsed integration config and the resolved window, and
returns a CaseFile built from stormpulse.sdk.investigate types; the
CLI owns rendering. Investigations are read-only by contract: observe
and report, never mutate. See the Architecture page and ADR CORE-005
(decision 14) in the repo.