Skip to content

Run a query as a campaign

Run a query as a campaign

The default Run a query flow is live-only: a laptop that closed its lid mid-query contributes nothing. Launch as campaign sends the same osquery statement through the campaigns pipeline instead, so offline hosts catch up when they reconnect, and the run leaves a durable record you can come back to later. It’s the right tool when the SQL isn’t IOC-shaped but you still want offline tolerance.

What it is

A campaign-mode query is raw operator-written osquery, dispatched to every targeted host (online or not), with the full offline-tolerant lifecycle: assignment ledger, reconnect catch-up, coverage card, pending hosts, late responses, Stop button, audit trail. The data that comes back is the same rows you’d get from a live run — there’s no IOC matching, no alerts, just osquery results stamped with the host that produced them.

The campaign shows up on the Campaigns page mixed in with IOC hunt campaigns; the Kind column distinguishes them (osquery vs ioc_hunt). The detail view is shaped to match the data — instead of a Frozen IOCs card and a Late Matches card, it shows a Frozen SQL card (the literal query text it was launched with) and a Late Responses card (the hosts that answered after the live window closed).

When to use it

Three patterns:

  1. Incident response on an intermittently-connected fleet. “Every host that has ever loaded the file C:\evil.dll, get back to me even if you’re a remote laptop that won’t reconnect until tomorrow.” The live-only path under-reports because the host you care about is offline at dispatch. Campaign mode picks them up on reconnect.
  2. Compliance evidence trails. “On day Y we asked every host about X.” A campaign is a durable record of that question and every answer that came back — useful for auditors who want to see proof of completion, not just a point-in-time snapshot.
  3. Long-running fleet sweeps. A query that doesn’t need to finish in the SSE window. Launch it, walk away, come back to the campaign detail page later to see the late responses.

For a one-shot spot-check (“listening ports on host X right now”), the default live-only Run a query flow is still the right tool. Campaign mode adds overhead — an assignment row per host, a durable record — that’s wasted on a query you don’t care about ten minutes from now.

How to use it

On the Queries page, near the Run query button, tick Launch as campaign (offline-tolerant). The toggle reveals a Live window (minutes) input (default 10, range 1–1440) and relabels the button to Launch as campaign.

  1. Type or paste your SQL.
  2. Pick a target (Fleet / Single host / By OS) as usual.
  3. Tick Launch as campaign.
  4. Adjust the Live window if 10 minutes isn’t right. The live window is the period during which the campaign counts against the MaxConcurrentHunts concurrency cap — short windows free the slot sooner for the next launch.
  5. Click Launch as campaign. A toast confirms how many hosts the campaign was dispatched to, and the page navigates to the campaign’s detail view at /campaigns/<id>.

From there, watch the coverage card fill in, drill into pending hosts, read late responses, or stop the campaign. The full campaign-detail UX is documented in Reading a campaign; the osquery-specific cards are called out below.

Launching from a saved query

The Saved queries rail’s per-row action menu has a Launch as campaign entry. The modal pre-fills the SQL from the saved query and the target from the current Queries-page target picker — by default the editor’s target wins, so you can re-target a saved query without rewriting it. Tick Use saved query’s target to have the saved query’s stored scope override instead.

If the saved query’s stored scope is OS-filtered (os_in), launching as a campaign is rejected up front with the modal error:

Campaigns don’t yet support OS-filtered targets — pick Fleet or specific hosts instead.

See Limits below for the rationale.

How it differs from a hunt

Both paths use the campaign pipeline. The difference is what the campaign carries and what it produces:

Hunt (IOC-driven)Run as campaign (osquery)
SourceA frozen IOC set; the IOC compiler turns it into SQLOperator-written SQL (free-form or saved query)
Per-row outputMatches against the frozen IOC snapshotRaw osquery rows
Alerts producedYes — every match creates a source='campaign' IOC alertNo — there’s nothing to match against, just data
Detail page result cardLate Matches (IOC type, value, host, time)Late Responses (host, row count, time, “view rows”)
Frozen cardFrozen IOCs (the snapshot)Frozen SQL (the literal query text)

For indicator-driven sweeps — “every host that touched this SHA256” — the hunt path is purpose-built and ranks matches for triage. Use this page’s flow for the investigations that aren’t IOC-shaped.

Per-host row cap and the truncated badge

A live-only query has one server-side row cap (default 50,000) that applies to the entire result set. A campaign query that fans out across a fleet would silently lose late-arriving hosts under that shared cap, so the cap is per host instead: each host’s rows are preserved up to the cap independently, and a host that hits the cap gets a rows truncated badge in the Late Responses card.

If you see truncated badges, the query is producing more rows per host than the cap allows — narrow the query or split the work.

Aggregate cap auto-stop

To bound worst-case storage growth (4,000 hosts × very large queries can run away), each campaign carries a per-campaign aggregate row counter. When the aggregate cap fires (default 10,000,000 rows), the campaign auto-stops with reason auto_stopped_aggregate_row_cap and the detail page shows an amber banner explaining what tripped. Rows already collected are kept; new responses are cancelled. The default is the panic cutoff, not the steady-state — ask your operator to tune it from production data if it fires routinely.

Stop semantics

Same as IOC-hunt campaigns. Admins see a red Stop Campaign button on the detail page; clicking it opens the stop modal.

  • Reason — free-text up to 1,000 characters, shown alongside the stopped-at timestamp in the header strip.
  • Discard in-flight results — by default Mimir cancels pending assignments but accepts responses already on the wire. Tick this to also reject responses that arrive after stop.

A stopped campaign keeps its row, its frozen SQL, and the responses already collected. Nothing is deleted.

The full stop flow is documented in Reading a campaign.

Promoting a campaign query to a saved query

A campaign launched from free-form SQL can be saved for re-launch later. Open the campaign detail view, copy the SQL from the Frozen SQL card, paste it into the editor on the Queries page, and follow the regular Save flow. A saved query created this way can be re-launched as a campaign from the saved queries rail’s per-row action menu, closing the loop.

Limits

Three deliberate v1 restrictions:

  1. OS-filtered targets aren’t supported. Campaigns target Fleet or a specific host list. A saved query whose stored scope is os_in (e.g., “Windows only”) is rejected when you try to launch it as a campaign — switch the scope to Fleet or the equivalent host list instead. This is a known gap; OS-filtered campaigns are tracked as a follow-up.
  2. No result-set alerts. A campaign osquery query produces data, not alerts. If you need an alert when a result set matches some shape (“any host returned > 1000 rows”), promote the query to a scheduled pack instead, or use a hunt with an IOC.
  3. Retention is operator-managed. A stopped campaign holds its raw rows until an operator drops them. The per-host and aggregate row caps bound worst-case growth, but there’s no automatic cleanup of stopped campaigns in v1.

Permissions

Launching as a campaign requires the admin role — the same gate as the live-only ad-hoc queries endpoint. Fleet-wide campaigns (target_type='all') additionally require either the admin role or the can_launch_fleet_hunts capability, matching the hunt path’s fleet gate.

Like all campaigns, the Launch as campaign toggle is gated by the runtime feature flag offline_campaigns_enabled. When the flag is off, the toggle is disabled with the tooltip:

Enable Campaigns on the Campaigns page to make queries offline-tolerant.

See Launching a campaign for the flag toggle and audit attribution.

Troubleshooting

The toggle is disabled and the tooltip says “Enable Campaigns…”. The offline_campaigns_enabled runtime flag is off. Open the Campaigns page and click Enable Campaigns; subsequent launches will be allowed. Flipping the flag requires the admin role — non-admins will see the button but the click returns 403. Ask an admin to enable it instead.

The launch returns max_concurrent_hunts_reached. Too many hunts and campaigns are active in their live windows. The cap is fleet-wide (or per-user, depending on the PerUserHuntIsolation setting). Wait for one to finish its live window, or stop one early.

The launch returns blocked_sql. The SQL touched a deny-listed osquery table at a multi-host scope. The deny-list applies at three surfaces — launch, live-drain, and reconnect — so the same SQL that runs fine against a single host is rejected against the fleet. Narrow the target or rewrite the query.

The launch returns capability_required. You tried to launch a fleet-wide campaign without the admin role or the can_launch_fleet_hunts capability. Pick a specific host list, or ask an admin to grant the capability.

A host’s row in Late Responses shows the truncated badge. That host produced more rows than the per-host cap. The rows you have are good; there were more rows on that host that didn’t make it into the campaign. Narrow the query or split the scope.

An amber banner says the campaign auto-stopped at the aggregate cap. The campaign produced more total rows across the fleet than the aggregate cap allows. Rows already collected are kept; new responses are cancelled. Re-launch with a narrower query.

Where to next

  • Run a query — the default live-only flow, the editor, and the target picker.
  • Saved queries — save and share queries; launch them as campaigns from the per-row action menu.
  • Launching a campaign — the campaign pipeline as a whole; the feature flag toggle and audit attribution.
  • Reading a campaign — the detail view, the Frozen SQL and Late Responses cards, the stop flow.
  • Launch a hunt — the IOC-driven campaign path, for the investigations that are IOC-shaped.