nano SIEM
Search Commands

Search & Query Language

Search & Query Language

nano uses nPL (nano Pipe Language), a piped query language for searching and analyzing security logs. If you've used other piped query languages, the syntax will be familiar.

Also available: Quick Reference, all 45 commands at a glance.

Understanding Fields

nano organizes log data into different field types, each optimized for specific search patterns. For detailed information, see Field Types & Search Performance.

Quick Overview:

  • Metadata Fields (timestamp, source_type, id) - Fastest, always present
  • Normalized Fields (src_ip, user, process_name) - Fast, heavily indexed
  • UDM Fields (525+ extended fields) - Good performance, broad coverage
  • Enriched Fields (enriched_src_country, enriched_dest_asn) - Fast, pre-computed
  • Prevalence Fields (prevalence_min, prevalence_process_hash, etc.) - Ingest-time rarity scores (0-100)
  • Extension Fields (additional UDM fields) - Stored as dynamic JSON, searchable by name

Query Basics

Search for keywords across all fields:

failed login

This searches for events containing both "failed" and "login" in any field.

Field Filters

Search specific fields using field=value syntax:

user=admin
status=401
src_ip=192.168.1.100

Comparison Operators

OperatorDescriptionExample
=Equalstatus=200
!=Not equalstatus!=200
>Greater thanbytes>1000
<Less thanresponse_time<100
>=Greater or equalseverity>=5
<=Less or equalport<=1024

Logical Operators

Combine conditions with AND, OR, NOT:

# AND (implicit or explicit)
user=admin status=401
user=admin AND status=401

# OR
status=401 OR status=403

# NOT
user=admin NOT status=200

# Grouping with parentheses
(status=401 OR status=403) AND user=admin

Wildcards

Use * for wildcard matching:

# Wildcard in field value
user=admin*
filename=*.exe

# Wildcard keyword search
*malware*

Regex Matching

Use /pattern/ for regex searches:

# Regex on specific field
filename=/cmd\.exe$/

# Case-insensitive regex
user=/admin|root/i

# Complex patterns
CommandLine=/powershell.*-enc.*[A-Za-z0-9+\/=]{50,}/

Advanced Search Operators

IN Lists

Check if a field matches any value in a list:

status IN (200, 201, 204)
user IN ("admin", "root", "administrator")
EventID NOT IN (4624, 4625)

IOC Observable Matching

Match an indicator of compromise across all observable columns at once with the ioc term — you never specify which field the indicator might appear in:

ioc=1.2.3.4
ioc=5fb90fd28458d16e61a7d6e08ae2ed28cb9b35e535aa6bc0e5a487f7c7702288
ioc in [1.2.3.4, evil.com, paste.ee/r/k9Xz]

ioc resolves the value against every observable field — IPs, file/process hashes, URLs and domains, DNS, identity, and detection context (CVE, rule, signature) — and is profile-aware (UDM or OCSF columns). It works on its own (ioc=1.2.3.4 | stats count by src_host) and pairs with | retro for a full-history, prevalence-weighted sweep. See retro for all forms, including threat-feed and lookup-table sources.

String Operators

OperatorDescriptionExample
LIKEPattern matching with % wildcardfilename LIKE "%.exe"
NOT LIKENegated pattern matchpath NOT LIKE "%temp%"
CONTAINSSubstring matchmessage CONTAINS "error"
STARTSWITHPrefix matchdomain STARTSWITH "evil"
ENDSWITHSuffix matchfilename ENDSWITH ".ps1"
# Examples
filename LIKE "%.dll"
CommandLine CONTAINS "powershell"
domain STARTSWITH "malicious"
path ENDSWITH "\\system32"

Function Filters

Use functions in search filters:

# Length check
len(password) < 8

# Lowercase comparison
lower(user) = "admin"

# Time-based filtering
timestamp > now() - INTERVAL 1 HOUR
age(timestamp) < 3600

Piped Commands

Transform and analyze results using piped commands with |. Results flow from left to right:

<search expression> | <command> | <command> | ...

Aggregation & Statistics

  • stats - Aggregate data with functions like count, sum, avg
  • chart - Create aggregated visualizations (alias for stats)
  • timechart - Time-based aggregations with automatic bucketing
  • streamstats - Calculate running statistics per event
  • eventstats - Add aggregate statistics to all events
  • bin - Bucket timestamps or numeric values

Filtering & Selection

  • where - Filter results based on conditions
  • head - Return first N results
  • tail - Return last N results
  • dedup - Remove duplicate events
  • top - Find most common values
  • rare - Find least common values
  • sample - Return random sample of events

Field Operations

  • eval - Create calculated fields
  • rename - Rename fields
  • fields - Include or exclude specific fields
  • table - Display specific fields
  • rex - Extract fields using regex
  • spath - Extract fields from JSON/XML
  • fillnull - Replace null values
  • mvexpand - Expand multi-value fields

Ordering & Formatting

  • sort - Order results by fields
  • reverse - Reverse order of events

Enrichment & Analysis

  • lookup - Enrich with lookup table data
  • inputlookup - Fetch data from external URLs for enrichment
  • prevalence - Filter or enrich based on artifact prevalence
  • resolve_identity - Resolve IP addresses to hostnames/users from EDR data
  • risk - Assign risk scores for risk-based alerting
  • anomaly - Detect statistical outliers
  • asset - Asset investigation view with identity resolution
  • cloud - Cloud investigation view with faceted summaries
  • baseline - What's new for a host/user/IP vs. its own recent history
  • retro - Full-history, prevalence-weighted IOC sweep (pairs with the ioc search term)

Pattern Detection

  • transaction - Group related events
  • sequence - Detect ordered event patterns
  • funnel - Analyze conversion through sequential steps
  • tree - Visualize hierarchical relationships (process trees, web flows)

Observability

Command-pages that open a curated observability surface directly from the search bar. Each must be the final command in the pipeline; they open a built page (no log scan) rather than returning rows.

  • services - Services overview with RED metrics (rate, errors, duration)
  • service - Single-service detail page (RED, endpoints, latency)
  • trace - Distributed-trace waterfall by trace id
  • metric - Metric time-series explorer

Subsearch Operations

  • append - Append subsearch results
  • join - Join with subsearch on common fields
  • format - Format subsearch results as string
  • return - Return field values from subsearch

Prevalence Filtering

nano provides two ways to filter by prevalence: ingest-time fields (recommended) and query-time enrichment.

Prevalence scores are computed at ingest time and stored directly in the logs. This is the fastest approach:

# Filter events with rare process hashes (score 0-25)
sourcetype = "sysmon" AND prevalence_process_hash <= 25

# Find events with any rare artifact
prevalence_min <= 10

# Combine with other filters
dest_ip != "" AND prevalence_dest_ip <= 15 | table timestamp, dest_ip, prevalence_dest_ip

Ingest-time prevalence fields:

FieldDescription
prevalence_file_hashScore (0-100) for file_hash. Lower = rarer.
prevalence_process_hashScore (0-100) for process_hash. Lower = rarer.
prevalence_dest_domainScore (0-100) for dest_host. Lower = rarer.
prevalence_dest_ipScore (0-100) for dest_ip (public IPs only).
prevalence_minMinimum of all scores (for filtering any rare artifact).

Score interpretation: 0-10 = Very rare, 11-25 = Rare, 26-50 = Uncommon, 51-100 = Common, 255 = N/A

Prevalence Slider

The search interface includes a Prevalence Filter slider next to the search button:

  • Drag left for rare artifacts only, right to show all
  • Quick presets: Very Rare (≤10), Rare (≤25), Uncommon (≤50), All
  • Automatically injects | where prevalence_min <= X before any pipeline commands, so it filters events prior to any stats/table/timechart aggregation
  • Value is saved in URL for shareable links

Query-Time Prevalence Enrichment

For dynamic prevalence lookups (slower but always current), use the prevalence command:

# Filter rare hashes (seen on < 5 hosts)
* | prevalence hash_prevalence < 5 window=24h

# Filter rare domains
* | prevalence domain_prevalence < 3 window=7d

Time Ranges

Time Picker

Use the UI time picker to set the search time range:

  • Last 15 minutes
  • Last hour
  • Last 24 hours
  • Last 7 days
  • Last 30 days
  • Custom range

Time Filters in Query

Filter by time in your query:

# Relative time
timestamp > now() - INTERVAL 1 HOUR
timestamp > date_sub(now(), INTERVAL 24 HOURS)

# Absolute time
timestamp > "2025-01-01 00:00:00"
timestamp BETWEEN "2025-01-01" AND "2025-01-31"

# Age-based
age(timestamp) < 3600  # Less than 1 hour old

Query Examples

Security Use Cases

# Brute force detection
event_type=login status=failure
  | bin span=5m
  | stats count() as attempts by time_bucket, src_ip
  | where attempts > 10

# Rare process execution
EventID=1
  | prevalence hash_prevalence < 5 window=24h
  | table timestamp, Image, CommandLine, hash_prevalence

# Suspicious PowerShell
Image=*powershell.exe CommandLine=*-enc*
  | eval cmd_entropy = entropy(CommandLine)
  | where cmd_entropy > 4.5

# Privilege escalation
EventID IN (4672, 4673, 4674)
  | stats count() by user, PrivilegeList
  | where count > 5

# Data exfiltration
bytes > 10485760 dest_port IN (80, 443)
  | stats sum(bytes) as total_bytes by src_ip, dest_ip
  | where total_bytes > 104857600  # 100 MB

Log Analysis

# Error rate by service
level=error
  | bin span=5m
  | stats count() by time_bucket, service

# Slow requests
response_time > 1000
  | stats avg(response_time), max(response_time), count() by endpoint

# Top talkers
* | stats sum(bytes) as total_bytes by src_ip
  | sort -total_bytes
  | head 10

# Failed authentication sources
status IN (401, 403)
  | stats count() as failures by src_ip, user_agent
  | where failures > 5

Threat Hunting

# Beaconing detection
* | bin span=1m
  | stats count() as connections by time_bucket, src_ip, dest_ip
  | stats stdev(connections) as conn_stdev by src_ip, dest_ip
  | where conn_stdev < 0.5  # Consistent timing = beaconing

# Lateral movement
EventID=4624 LogonType=3
  | stats dc(dest_host) as unique_targets by src_ip
  | where unique_targets > 10

# Suspicious domains
* | eval domain = extract_domain(url)
  | eval domain_entropy = entropy(domain)
  | where domain_entropy > 4.0
  | prevalence domain_prevalence < 3

# Command and control
* | where is_public_ip(dest_ip)
  | stats sum(bytes) as total, count() as connections by dest_ip
  | where connections > 100 AND total < 10000  # Many small connections

Search Queuing

When the system is busy, your search may be queued rather than running immediately. This happens when the global or per-user concurrency limits are reached.

While queued, you'll see your position in the queue and an estimated wait time. You can cancel a queued search at any time using the cancel button.

The Active Searches panel (above the results area) shows all your current and recent searches, including queued, running, completed, and failed jobs. Click a completed search to reload its results.

Detection rules always run immediately and are never affected by search queuing.

For configuration details, see Search Concurrency.

Performance Tips

  1. Filter early: apply time and field filters before piped commands
  2. Use specific fields: src_host="github.com" is much faster than bare github.com
  3. Limit results: use head to limit large result sets
  4. Avoid wildcards at start: *admin is slower than admin*
  5. Use stats wisely: group by low-cardinality fields when possible
  6. Time-based partitioning: always specify a time range
  7. Aggregate: stats is more efficient than returning raw events
  8. Sample for testing: use sample when developing queries

For a deep dive on all of these, see Search Best Practices.

Keyboard Shortcuts

  • Ctrl+Enter: Execute query
  • Ctrl+K: Open command palette
  • Ctrl+Space: Autocomplete
  • Ctrl+/: Comment/uncomment
  • Ctrl+F: Find in results

Next Steps

On this page

On this page