Skip to content

Hunt results

Hunt results

Clicking any hunt — whether it just launched or finished a week ago — opens the active hunt detail panel. It’s the same panel for live and historical hunts: the live ones poll for updates every few seconds; the finished ones render their final state and stop. The page is built around three regions: the coverage strip at the top, optional status banners, and the results table.

What you get

Header strip — hunt name, current status (pending / running / completed / partial / failed / cancelled), target type (“Fleet-wide” or “N selected hosts”), and a blue “Part of campaign: view →” badge if this hunt was launched via the campaigns subsystem. The campaign link opens the campaign detail page where you can see frozen IOC snapshot, pending host roster, and late-match provenance.

Coverage stripresponded_hosts / total_hosts hosts responded on the left, the matched-host counter on the right. The matched counter turns red when non-zero; on completion it animates from 0 to the final value in four steps so the result lands with a beat. A progress bar underneath shows responded-vs-total fill, colored green if no matches, red if any match has come in.

Status banners — context-dependent:

  • Streaming results… (running) — soft grey, no urgency.
  • ✓ No matches found across N hosts. Fleet is clean. (completed, zero matches) — green, the all-clear.
  • ⚠ Hunt timed out — N hosts did not respond (offline). (partial) — yellow, indicates the hunt finished with incomplete coverage.

Results table — one row per (host, IOC) match. Columns:

  • Host — the hostname that reported the hit.
  • IOC — a small badge with the indicator type (SHA256, IPV4, REGISTRY, etc.) plus the value, truncated to 12 chars with an ellipsis for hashes.
  • Evidence — the first 80 characters of the JSON osquery row that matched. The full row is available via GET /api/v1/hunts/{id}/results.
  • Detected — when the agent reported the match, with the server-side ingestion timestamp shown as a tooltip if the two differ (clock-skew aware).

How polling works

The detail panel polls every 3 seconds while the hunt is in pending or running. As soon as the status flips to a terminal value (completed, partial, failed, cancelled), polling stops.

If the server returns HTTP 429 (rate limit), the poller backs off to 6 seconds for the next request and then resumes the 3-second cadence once it gets a clean response. The backoff is per-tab — opening the same hunt in two windows doubles the request rate.

You can leave the detail panel open across a long-running hunt; nothing bad happens. The 3-second cadence is light: two API calls per tick (hunt summary + results page). For a 1-hour hunt that’s ~2,400 round trips total, well within any sensible rate budget.

Why use it

Live drilldown is where the actual triage happens:

  • Watch the coverage bar grow during a fleet sweep so you know when you’ve reached enough confidence to act.
  • Spot the host that’s matching unexpectedly — the IOC badge tells you what matched, the Evidence column tells you how (which process, which file path, which DNS lookup).
  • Confirm a “no matches” result before declaring the fleet clean. The green status banner with the responded-host count is what you paste into the incident report.

How to use it

  1. Open the Hunts page.
  2. Click any row in the Hunt History table — or click View in the rightmost column. The detail panel opens above the history, pinned to that hunt.
  3. While the hunt is running, watch the coverage strip and result rows stream in. Hover any Detected timestamp for the skew tooltip if you suspect agent-server clock drift.
  4. To close the panel, click the button in the top-right of the header. Closing the panel does NOT stop the hunt — it’s just a UI dismissal. Use Stop (red button, only visible while the hunt is running) to cancel.
  5. To export the full result set, hit the API:
    Terminal window
    curl -H "Authorization: Bearer <token>" \
    https://mimir.example.com/api/v1/hunts/<id>/results

Late matches and campaign provenance

If the hunt was launched via the campaigns subsystem (offline_campaigns_enabled runtime flag), result rows can include late-match provenance — a flag indicating the match arrived after the hunt’s live window expired. The agent had been offline during the active dispatch and replied on reconnect via the campaign assignment lease.

Late matches still create ioc_alerts rows in the alert feed, with a late_match: true annotation in security_events. They’re indistinguishable from real-time matches in the Hunts result table — go to the campaign detail page (linked via the blue badge in the header) to see which matches were late vs. live.

Troubleshooting

The result rows render but the coverage bar is stuck at 0/0. The hunt was rejected with no_hosts_targeted before any dispatch happened. The status will be failed with a reason. Re-launch with a wider “Hosts seen within” window or wait for agents to reconnect.

Status flipped to partial and there are no matches. Some agents responded clean before the timeout; the rest never replied. “Partial” means coverage was incomplete, not that anything matched. Re-run with a wider live window if you need full coverage.

Status is failed with no result rows. The dispatch itself errored — usually a connectivity blip with the agent fleet or a database failure. Check the server logs for the hunt id; the in-memory watchHuntTimeout goroutine logs a structured event with the failure reason.

I see fewer matches in the table than the matched-host counter claims. The counter is matched_hosts (distinct hosts), the table is (host, indicator) pairs. A host that matched three indicators shows once in the counter, three times in the table.

The results table is empty during the hunt, then suddenly fills on completion. That’s possible if the agents are slow to reply (network latency, or osquery is busy on the box). Result rows appear as they arrive — there’s no batching server-side. If your fleet is consistently this quiet, raise an issue with timing data.

Permission model

The hunt detail and results endpoints (GET /api/v1/hunts/{id} and GET /api/v1/hunts/{id}/results) are withAnyAuth. Under MIMIR_PER_USER_HUNT_ISOLATION=true, non-admin users can only see hunts they launched; cross-user access returns HTTP 403 forbidden. Admins see every hunt regardless of who launched it.