StairQL
A compact prefix search language for Stairwell. Terms like p:true fs:today asset:DESKTOP-123 combine with an implicit AND, so a hunt fits on one line.
StairQL is a compact, prefix-driven search language for Stairwell. A query is a sequence of prefix:value terms joined by an implicit AND, which makes it quick to type, short enough to paste into a ticket, and readable a month later in a runbook. It reaches the same data as CEL, and it is the one to reach for when you are hunting by hand.
StairQL is in beta and the syntax is still evolving, so expect prefixes to be added and details to shift. This page teaches the language. The StairQL reference lists every prefix and accepted value.
What does a StairQL query look like?
Three terms, side by side, with no operators between them:
p:true fs:today asset:DESKTOP-123
That reads as "malicious files first seen today on asset DESKTOP-123." Each term is a prefix, a colon, and a value. Space-separated terms are combined with AND, so every term you add narrows the result.
That is the whole shape of the language. Everything else is a matter of which prefixes exist, and four operators for when AND is not what you meant: parentheses to group, OR for alternatives, ! to negate, and [a,b,c] when one field should accept several values.
Most prefixes have a short form and a spelled-out form, and they behave identically. fs: and firstseen: are the same prefix, so use whichever reads better in the place you are writing it.
How do I run a StairQL query?
Pick StairQL from the language selector at the left edge of the search bar, then type the query and press enter. The selector also offers CEL, which is the default, and plain English, which is translated into StairQL before it runs. A query that starts with > is treated as StairQL wherever it appears, and the search bar adds that marker for you.
Which prefixes do I need first?
These ten cover most hunting. The reference has the rest, including PE metadata, tags, network indicators, and threat reports.
| Prefix | Matches | Example |
|---|---|---|
| (bare hash) | MD5, SHA1, or SHA256, detected by length | 0123...def |
hash: | A hash, one or a list | hash:[aaa,bbb,ccc] |
p: | The automated verdict | p:true |
fs: | First seen anywhere Stairwell can see | fs:7d+ |
envfs: | First seen in your environment | envfs:today |
asset: | Asset name or ID | asset:DESKTOP-123 |
fc: / pc: | Filename or path contains | pc:Windows |
magic: | File type | magic:EXE |
oac: | How many of your assets hold the file | oac:5+ |
wk | Well-known files, usually as !wk | !wk |
How do I narrow a query that returns too much?
Add a term. Every space-separated term is another AND, so a broad query gets specific one word at a time.
magic:EXE # every executable, which is far too many
magic:EXE envfs:7d+ # new to your environment in the last week
magic:EXE envfs:7d+ !wk # and not something Stairwell already knows
magic:EXE envfs:7d+ !wk oac:5- # and on no more than 5 of your assets
That last query is a rare-and-recent hunt, which is the shape most hunts end up in. The ! on wk is negation, and it works on any term: !fc:safe for a filename that does not contain "safe", !an:[A1,B2,C3] for anything not on those three assets.
How do I write a time window?
Use fs: for the first time a file was seen anywhere Stairwell can see, and envfs: for the first time it appeared in your own environment. A trailing + means at or after, a trailing - means at or before.
fs:today # first seen anywhere today
fs:7d+ # in the last 7 days. Also 24h+, 30m+, 2w+
fs:2026-01-01+ # on or after that day
fs:7d+ fs:5d- # between 5 and 7 days ago, as two terms
envfs:7d+ # reached your environment in the last 7 days
The distinction between the two matters more than it looks. fs:7d+ finds files that are new to the world. envfs:7d+ finds files that are new to you, which includes the decade-old tool an attacker brought with them. Dates are read as UTC.
How do I search by filename or path?
fc: and pc: are substring matches on the filename and the path. fr: and pr: are the same two fields matched against a regular expression, and fsw:, few:, psw:, and pew: anchor to the start or end.
fc:invoice # filename contains "invoice"
fc:"evil file" # quote a multi-word value
pc:`C:\Windows\System32` # backticks take the value exactly as typed
few:\.dll # filename ends with ".dll"
fr:svc.*\.exe # regular expression on the filename
Two traps live here. The contains prefixes treat regular expression characters as literal text, so reach for fr: or pr: when you want a pattern. And because the ends-with prefixes are patterns, a bare dot is a wildcard: few:.exe matches aexe as well as .exe, so escape it as few:\.exe.
When a value contains a backslash, put it in backticks. Everything between them is taken exactly as typed, which is what makes Windows paths and patterns paste in cleanly.
How do I combine alternatives?
Use OR between the alternatives, or a list when they are values of the same field.
tr:APT1 OR yara:APT1_Loader # in that report, or matching that rule
hash:[aaa,bbb,ccc] # any of these three hashes
yara:[Emotet,Trickbot] # either rule
(yara:APT1 AND fs:today) OR p:true # parentheses group
Three things to know:
- A list is the compact way to write same-field OR, and it is exact-match only. The contains and pattern prefixes reject
[a,b], so writefc:`.sys` OR fc:`.exe`instead. ANDbinds tighter thanOR, the same as SQL, soa OR b AND cparses asa OR (b AND c). Parenthesize when you meant the other one.- Operators inside a quoted value are not operators, so
fc:"foo OR bar"is a single substring match.
Which queries are worth starting from?
Each of these is one line, and each answers a question someone asks in a real investigation.
pc:`Windows\System32` few:\.exe !wk # unusual executables in a system folder
yara:Emotet oac:5+ # a rule match, on 5 or more of your assets
hash:[aaa,bbb,ccc] fs:5d+ # any of these hashes, last 5 days
tr:APT123 # everything tied to one threat report
fsw:sys pc:Windows !wk # files posing as Windows system files
!an:[A1,B2,C3] fs:today # today, minus three known-good assets
p:true envfs:7d+ ar:^WKS- # new malicious files on WKS- hosts
magic:EXE impdll:kernel32.dll impfunc:CreateRemoteThread !signed:VALID
# unsigned PEs that can inject code
The last one is worth reading twice, because it is the pattern most worth copying: no single term in it is suspicious. Plenty of legitimate software imports CreateRemoteThread, and plenty is unsigned. The four terms together describe something much narrower than any one of them does, and that is what StairQL is short enough to let you try.
What should I know before trusting a result?
A YARA rule match tells you a file matched a rule someone wrote. It is evidence worth looking at, not a verdict, and a hunting rule in particular is written to be broad. Read the match alongside the verdict, how rare the file is, and where it landed.
Two more edges to keep in mind. p:true is the automated verdict only, so it does not include the opinions your team has set; write p:true OR opinion:MALICIOUS when you want both. And tag matching is case-sensitive today, so at:critical does not find an asset you tagged Critical.
What is not supported yet?
Environment-scoped rule queries ("any rule in environment X"), which CEL can express with rule.environment_id. Negated patterns, so !fr: and !pr: are rejected. Case-insensitive tag matching. And last-seen time: StairQL filters on first-seen only.
StairQL is in beta, and which of those gets closed first depends on which one is costing someone real work. If one of them is blocking your team, tell us at [email protected].
What should I read next?
- StairQL Reference, for every prefix, operator, and accepted value.
- CEL Query Language, the explicit form, and the fields StairQL has no prefix for.
- Example Queries, for more starting points.
- Hunting and Search, for the rest of what the search bar does.
Updated 24 days ago