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
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_huntscapability) 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 pastosqueryd’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:
- Incident response. A threat feed flagged a fresh hash; sweep the fleet for it before the attacker has time to spread.
- 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.
- 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
- Open the Hunts page.
- Paste the value into the input. Wait a beat for the “Detected: …” badge to confirm Mimir read the type correctly.
- Click Hunt Fleet. The active hunt detail panel opens immediately and starts polling for results every 3 seconds.
Advanced Hunt
- Open the Hunts page and click ▼ Advanced Hunt.
- Tick the indicators you want included. The count next to the heading updates as you select.
- Pick All connected hosts or Selected hosts. (Selected-host picker is on the Indicators page; this panel only flips the mode.)
- Tune Live window and Hosts seen within if the defaults don’t fit.
- 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:
- The hunt status is
partial— some hosts timed out. Wait or re-launch with a wider live window. - 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. - 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.