Skip to content

Launch a hunt

Launch a hunt

A hunt is a fleet-wide sweep for one or more indicators of compromise. Mimir compiles your indicators into osquery SQL, dispatches it to every targeted agent, streams back the matches, and ranks the results so you can triage. Hunts are the operator’s primary reactive control: “we just got an alert about , find me every machine that has it.”

The Hunts page offers two launch surfaces — Quick Hunt for the single-value case, and the collapsible Advanced Hunt panel for everything else.

What you get

Quick Hunt is one input box and a button:

  • Paste a hash, filename, file path, or registry key
  • Mimir auto-detects the indicator type as you type (SHA256, SHA1, MD5, Registry Key, File Path, or Filename — visible in the right-edge “Detected: …” badge)
  • Click Hunt Fleet and Mimir creates the IOC (if it doesn’t already exist), launches a fleet-wide sweep, and immediately opens the active hunt detail panel for live streaming

Advanced Hunt is the same idea, parameterized:

  • Indicator picker. A scrollable list of every active IOC in your workspace, with severity badges and value previews. Check the ones you want included in this sweep.
  • Target type. “All connected hosts” (requires can_launch_fleet_hunts capability) or “Selected hosts” (subject to the non-admin host cap, see below). Mimir will compile separate osquery queries per indicator type and dispatch them in chunks of 1,000 values per query so large indicator sets don’t blow past osqueryd’s 128 KiB argv cap or gRPC’s 4 MiB recv cap.
  • Live window (minutes). How long the hunt runs before timing out any agents that haven’t responded. Default 10 minutes. Range 1–1440.
  • Hosts seen within (days). Excludes “ghost” hosts that haven’t reported in this many days. Default 90. Stops a fleet hunt from permanently stalling on a long-decommissioned VM.

Both flows land in the same backend (POST /api/v1/hunts) and produce the same Hunt row; the Quick Hunt path also runs the POST /api/v1/hunts/quick shorthand that creates the IOC if it doesn’t already exist.

Why use it

Three patterns:

  1. Incident response. A threat feed flagged a fresh hash; sweep the fleet for it before the attacker has time to spread.
  2. IOC validation. You’re considering adding an indicator to a real-time watchlist. Run a single fleet hunt first to see whether the hit rate is sane and the matches look legitimate.
  3. Variant hunting. An alert fired on evil.exe; you suspect a pack is dropping different filenames per host. Run an advanced hunt on the binary’s SHA256 across the fleet to find every copy regardless of filename.

How to use it

Quick Hunt

  1. Open the Hunts page.
  2. Paste the value into the input. Wait a beat for the “Detected: …” badge to confirm Mimir read the type correctly.
  3. Click Hunt Fleet. The active hunt detail panel opens immediately and starts polling for results every 3 seconds.

Advanced Hunt

  1. Open the Hunts page and click ▼ Advanced Hunt.
  2. Tick the indicators you want included. The count next to the heading updates as you select.
  3. Pick All connected hosts or Selected hosts. (Selected-host picker is on the Indicators page; this panel only flips the mode.)
  4. Tune Live window and Hosts seen within if the defaults don’t fit.
  5. Click Launch Hunt (N IOCs). The detail panel opens and starts streaming.

Limits and gates

Fleet-wide hunts require admin OR capability. The server admits a fleet-wide hunt when the requester has the admin role OR the can_launch_fleet_hunts capability. Non-admin operators without the capability see the “All connected hosts” radio disabled and the Hunt Fleet / Launch Hunt buttons disabled with the tooltip “Requires the can_launch_fleet_hunts capability. Ask an admin.” Admins without the explicit capability still see the buttons enabled — the UI checks is_admin || capability, mirroring the server’s !sess.IsAdmin && !caps["can_launch_fleet_hunts"] check. The capability is granted per-user via the admin Users page.

Non-admin selected-host hunts are capped. The cap is the runtime nonadmin_hunt_host_cap setting (admin-mutable via PUT /api/v1/admin/settings/nonadmin_hunt_host_cap). Exceeding it returns HTTP 400 with non-capability hunts may target at most N hosts. Admins are uncapped.

Concurrent-hunt cap. The server enforces a global cap of 5 hunts in pending or running status (the DefaultMaxConcurrentHunts constant). Hitting it returns HTTP 429 with max_concurrent_hunts_reached. Increase the cap via MIMIR_MAX_CONCURRENT_HUNTS if your fleet is small enough to absorb the additional load (each fleet-wide hunt dispatches a query to every connected agent).

Per-user isolation. When MIMIR_PER_USER_HUNT_ISOLATION=true (default false in production, true in the demo binary), each user gets their own active-hunt slot — a global hunt doesn’t block another operator from launching their own. List/get/results endpoints scope to the requesting user’s hunts.

0-host rejection. If every agent matching your target filter is offline (or no host has reported within the seen_within window), the launch returns HTTP 400 with no_hosts_targeted instead of stalling in running forever. Re-launch with a wider seen_within or wait for hosts to reconnect.

Troubleshooting

“Hunt Fleet” button is disabled even though I’m an admin. Your input is empty or the trim removed all content. Type at least one character.

“Detected: Filename” but I pasted a hash. Mimir’s type sniffer is literal: /^[0-9a-f]{32,64}$/ for hashes. A leading or trailing space, a Unicode hyphen instead of ASCII, or hex digits outside 0-9a-f (uppercase is fine; the value is lowercased before checking) all push into the “Filename” fallback. Re-paste cleanly.

“hunt_already_running” / HTTP 409. Under per-user isolation, you already have an active hunt. Open it from the Hunt History table and either stop it (see the Stop button on the detail panel) or wait for it to complete.

“max_concurrent_hunts_reached” / HTTP 429. The global cap is hit. Wait for an active hunt to finish or stop one. If you’re consistently saturating the cap with legitimate work, ask an admin to raise MIMIR_MAX_CONCURRENT_HUNTS.

“capability_required” / HTTP 403. Your account doesn’t have can_launch_fleet_hunts. Either switch to a selected-host hunt (picker on the Indicators page) or ask an admin to grant the capability.

No matches but I expected some. Three things to check:

  1. The hunt status is partial — some hosts timed out. Wait or re-launch with a wider live window.
  2. The hash is on a binary that’s deleted but was running in memory; osquery’s file/hash table doesn’t see in-memory-only artifacts. Use a processes-based query (advanced custom queries page, not this hunt path) to catch those.
  3. The “Hosts seen within” filter excluded the host you cared about. Bump it up.

Under the hood

Every hunt commits as an INSERT INTO hunts row with status='pending' inside a pg_advisory_xact_lock transaction so concurrent launches serialize cleanly. The dispatch loop compiles your IOCs into per-type osquery SQL (sha256, sha1, md5, filename, filepath, registry, ipv4, domain), chunks any IN-clause longer than 1,000 values, and streams the resulting queries over gRPC to every targeted agent.

If the offline_campaigns_enabled feature flag is on, the hunt is launched via the campaigns subsystem so agents that are offline at launch time can answer when they reconnect (subject to the campaign assignment lease window, default 30 minutes). Without the flag, the hunt is a pure live dispatch — offline hosts contribute zero to the result.