Claude-skill-registry lcql-query-builder
Activate when the user needs help writing, optimizing, or understanding LimaCharlie Query Language (LCQL) queries for searching telemetry.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/lcql-query-builder" ~/.claude/skills/majiayu000-claude-skill-registry-lcql-query-builder && rm -rf "$T"
skills/data/lcql-query-builder/SKILL.mdLCQL Query Builder
You are an expert in LimaCharlie Query Language (LCQL), a powerful query language for searching and analyzing security telemetry. Help users write, optimize, and understand LCQL queries.
What is LCQL?
LCQL (LimaCharlie Query Language) is a structured query language for searching historical telemetry across sensors and events in LimaCharlie. It enables threat hunting, investigation, and analysis of security data.
Query Structure
LCQL queries follow a pipeline structure with four main components:
TIME_RANGE | SENSOR_SELECTOR | EVENT_TYPE | FILTER | PROJECTION
1. Time Range (Required)
Specify the time window using ParseDuration format:
Syntax:
where duration uses these units:-<duration>
= hours (e.g.,h
= last 3 hours)-3h
= minutes (e.g.,m
= last 30 minutes)-30m
= days (e.g.,d
= last 7 days)-7d
Examples:
- Last 1 hour-1h
- Last 24 hours-24h
- Last 10 minutes-10m
- Last 12 hours-12h
2. Sensor Selector (Required)
Define which sensors to query using selector expressions.
Available Fields:
- Sensor IDsid
- Organization IDoid
- Installation Key IDiid
- Platform (windows, linux, macos, ios, android, chrome, github, office365, 1password, etc.)plat
- Extended Platformext_plat
- Architecturearch
- Host namehostname
- MAC addressmac_addr
- Last connection time (epoch)alive
- External IPext_ip
- Internal IPint_ip
- Network isolation status (boolean)isolated
- Kernel visibility enabled (boolean)kernel
- Device IDdid
- List of sensor tagstags
Operators:
- Equals==
- Not equal!=
- Element in list or substring in stringin
- Element not in list or substring not in stringnot in
- Regex match (use backticks for regex:matches
)`^10\.3\..*`
- Regex does not matchnot matches
- String contained within elementcontains
Examples:
- All Windows sensorsplat == windows
- Sensors with "test" tagtest in tags
^10.3..*`` - Windows with IP 10.3.x.xplat == windows and int_ip matches
- All Azure-related platformsplat contains "azure"
- Linux OR (isolated OR tagged "evil")plat == linux or (isolated == true or evil in tags)
Note: Platform names starting with numbers need backticks:
plat == `1password`
3. Event Type (Required)
Specify which event types to search. Use
* for all events or space-separated event types.
Common Event Types:
- Process creationNEW_PROCESS
- Existing processesEXISTING_PROCESS
- DNS queriesDNS_REQUEST
- Network connectionsNETWORK_CONNECTIONS
- Code signing informationCODE_IDENTITY
- Windows Event LogsWEL
- File accessFILE_TYPE_ACCESSED
- All event types*
Examples:
- Only new process eventsNEW_PROCESS
- Both new and existing processesNEW_PROCESS EXISTING_PROCESS
- DNS requests onlyDNS_REQUEST
- All events*
4. Filter (Optional)
Filter events based on field values using boolean logic.
Field Paths:
- Access event fieldsevent/FIELD_NAME
- Access routing metadata (sid, hostname, etc.)routing/FIELD_NAME- Nested fields use
:/event/PARENT/FILE_PATH - Wildcard matching:
matches any fieldevent/*
Operators:
- Equals==
- Not equals!=
- Substring matchcontains
- Substring not presentnot contains
- Prefix matchstarts with
- Suffix matchends with
- Equality check (alternative to ==)is
- Inequality checkis not
- Logical ANDand
- Logical ORor
Examples:
- Command line contains "psexec"event/COMMAND_LINE contains "psexec"
- Unsigned filesevent/SIGNATURE/FILE_IS_SIGNED != 1
- Domains with "google"event/DOMAIN_NAME contains 'google'
- Windows Event ID 4624event/EVENT/System/EventID == "4624"
- File path doesn't contain "powershell"event/FILE_PATH not contains "powershell"
- Any field contains "suspicious"event/* contains 'suspicious'
Complex Filters:
event/COMMAND_LINE contains "powershell" and event/FILE_PATH not contains "system32"
event/public_repo is false and event/actor_location/country_code is not "us"
5. Projection (Optional)
Control output format, extract fields, aggregate data, and sort results.
Field Extraction:
event/DOMAIN_NAME as domain event/FILE_PATH as path routing/hostname as host
Aggregation Functions:
- Count eventsCOUNT(event)
- Count unique valuesCOUNT_UNIQUE(field)
- Group by fieldsGROUP BY(field1 field2)
Sorting:
- Sort by fieldORDER BY(field)
Examples:
Simple field extraction:
event/FILE_PATH as path event/COMMAND_LINE as cli routing/hostname as host
Count with grouping:
event/DOMAIN_NAME as domain COUNT(event) as count GROUP BY(domain)
Unique count (prevalence):
event/DOMAIN_NAME as domain COUNT_UNIQUE(routing/sid) as sensors GROUP BY(domain)
Multiple grouping fields:
event/repo as repo event/actor as actor COUNT(event) as count GROUP BY(repo actor)
Complete Query Examples
Basic Search
Search for psexec across all Windows systems:
-24h | plat == windows | * | event/* contains 'psexec'
DNS Analysis
Domain resolution count:
-10m | plat == windows | DNS_REQUEST | event/DOMAIN_NAME contains 'google' | event/DOMAIN_NAME as domain COUNT(event) as count GROUP BY(domain)
Domain prevalence (unique sensors):
-10m | plat == windows | DNS_REQUEST | event/DOMAIN_NAME contains 'google' | event/DOMAIN_NAME as domain COUNT_UNIQUE(routing/sid) as count GROUP BY(domain)
Process Monitoring
Process command line search:
-1h | plat == windows | NEW_PROCESS EXISTING_PROCESS | event/COMMAND_LINE contains "psexec" | event/FILE_PATH as path event/COMMAND_LINE as cli routing/hostname as host
Unsigned binaries grouped:
-24h | plat == windows | CODE_IDENTITY | event/SIGNATURE/FILE_IS_SIGNED != 1 | event/FILE_PATH as Path event/HASH as Hash event/ORIGINAL_FILE_NAME as OriginalFileName COUNT_UNIQUE(Hash) as Count GROUP BY(Path Hash OriginalFileName)
Stack children by parent process:
-12h | plat == windows | NEW_PROCESS | event/PARENT/FILE_PATH contains "cmd.exe" | event/PARENT/FILE_PATH as parent event/FILE_PATH as child COUNT_UNIQUE(event) as count GROUP BY(parent child)
Windows Event Log (WEL)
Specific Event ID:
-24h | plat == windows | WEL | event/EVENT/System/EventID == "4624" AND event/EVENT/EventData/LogonType == "10"
Stack logon types by user:
-24h | plat == windows | WEL | event/EVENT/System/EventID == "4624" | event/EVENT/EventData/LogonType AS LogonType event/EVENT/EventData/TargetUserName as UserName COUNT_UNIQUE(event) as Count GROUP BY(UserName LogonType)
Failed logons:
-1h | plat==windows | WEL | event/EVENT/System/EventID == "4625" | event/EVENT/EventData/IpAddress as SrcIP event/EVENT/EventData/LogonType as LogonType event/EVENT/EventData/TargetUserName as Username event/EVENT/EventData/WorkstationName as SrcHostname
Overpass-the-Hash detection:
-12h | plat == windows | WEL | event/EVENT/System/EventID == "4624" and event/EVENT/EventData/LogonType == "9" and event/EVENT/EventData/AuthenticationPackageName == "Negotiate" and event/EVENT/EventData/LogonProcess == "seclogo"
GitHub Telemetry
Protected branch override from outside US:
-12h | plat == github | protected_branch.policy_override | event/public_repo is false and event/actor_location/country_code is not "us" | event/repo as repo event/actor as actor COUNT(event) as count GROUP BY(repo actor)
Query Execution Modes
CLI Usage
When using the Python CLI (
limacharlie query), set context first:
set_time -3h # Set time range set_sensors plat == windows # Set sensor selector set_events NEW_PROCESS DNS_REQUEST # Set event types (space-separated)
Then run queries:
Paged mode (recommended):
q event/DOMAIN_NAME contains 'google' | event/DOMAIN_NAME as domain COUNT_UNIQUE(routing/sid) as count GROUP BY(domain)
Use
n command to fetch next page.
Full query (all results):
qa event/DOMAIN_NAME contains 'google' | event/DOMAIN_NAME as domain COUNT_UNIQUE(routing/sid) as count GROUP BY(domain)
Dry run (cost estimation):
dryrun event/COMMAND_LINE contains "powershell" and event/FILE_PATH not contains "powershell"
Additional commands:
- Limit events scannedset_limit_event <number>
- Mirror queries/results to fileset_output <file>
- Set output formatset_format json|table
- Show total query costsstats
UI Usage
In the web console:
- Select data source (Events, Detections, or Platform Audit)
- Enter LCQL query in editor
- Set time period:
- Last [time period]:
,-3h-7d - Around [timestamp] +- [duration]:
2025-01-16 08:52:54 +- 15 minutes - Absolute:
From 10am to 1:30pm
- Last [time period]:
- View query cost estimation
- Run query and view results in Timeline or Table view
Query Optimization
Performance Tips
-
Be specific with sensor selection
- Use
instead of no filterplat == windows - Filter by tags, IPs, or hostnames when possible
- More specific = faster and cheaper
- Use
-
Limit event types
- Use specific event types instead of
* - Example:
vsDNS_REQUEST*
- Use specific event types instead of
-
Use narrow time windows
is cheaper than-1h-7d- Only query the time range needed
-
Filter early
- Put restrictive filters first
- Filter before projection/aggregation
-
Limit event scanning
- Use
in CLI to cap scanned eventsset_limit_event
- Use
Cost Management
Queries are charged by events evaluated (per million events)
- Cost estimation shown before running query
- "At most" cost for full time range
- Only retrieved data is billed
- Better targeting = lower cost
Use
to estimate costs before running expensive queriesdryrun
Common Use Cases
Threat Hunting
Search for suspicious command lines:
-24h | plat == windows | NEW_PROCESS | event/COMMAND_LINE contains "powershell" and event/COMMAND_LINE contains "bypass" | event/COMMAND_LINE as cli event/FILE_PATH as path routing/hostname as host
Hunt for lateral movement (psexec):
-12h | plat == windows | NEW_PROCESS | event/COMMAND_LINE contains "psexec" | event/FILE_PATH as path event/COMMAND_LINE as cli routing/hostname as host
Investigation
Find all activity from specific host:
-24h | hostname == "WORKSTATION-01" | * | routing/hostname as host event/* as data
Trace process ancestry:
-6h | plat == windows | NEW_PROCESS | event/PARENT/FILE_PATH contains "cmd.exe" | event/PARENT/FILE_PATH as parent event/FILE_PATH as child COUNT_UNIQUE(event) as count GROUP BY(parent child)
Baseline Analysis
Count logon types:
-7d | plat == windows | WEL | event/EVENT/System/EventID == "4624" | event/EVENT/EventData/LogonType AS LogonType COUNT(event) as Count GROUP BY(LogonType)
Network connection frequency:
-24h | plat == windows | NETWORK_CONNECTIONS | event/NETWORK_ACTIVITY/DESTINATION/IP_ADDRESS as ip COUNT(event) as count GROUP BY(ip)
Data Structure Reference
LCQL can query three primary data streams in LimaCharlie, each with a different structure. Understanding these structures is essential for writing effective queries.
Queryable Streams
LCQL can query three different data streams by selecting the appropriate source in the Query Console interface:
| Stream | Purpose | Structure Type |
|---|---|---|
| Real-time telemetry from sensors/adapters | Event structure |
| D&R rule alerts and detections | Detection structure |
| Platform management actions | Audit structure |
Note: The stream is selected via the Query Console UI (Events/Detections/Audit dropdown), not via LCQL syntax.
Event Stream Structure
Events are telemetry from sensors and adapters. They have two top-level objects:
{ "routing": { "sid": "bb4b30af-...", "hostname": "workstation-01", "event_type": "NEW_PROCESS", "event_time": 1656959942437, "event_id": "8cec565d-...", "oid": "8cbe27f4-...", "plat": 268435456, "this": "a443f9c4...", "parent": "42217cb0..." }, "event": { "FILE_PATH": "C:\\Windows\\System32\\cmd.exe", "COMMAND_LINE": "cmd.exe /c whoami", "PROCESS_ID": 4812 } }
routing/ fields (consistent across all events):
- Sensor ID (UUID)routing/sid
- Hostnamerouting/hostname
- Event type (NEW_PROCESS, DNS_REQUEST, etc.)routing/event_type
- Unix timestamp in millisecondsrouting/event_time
- Organization IDrouting/oid
- Platform (Windows=268435456, Linux, macOS)routing/plat
- Current process/object hashrouting/this
- Parent process hashrouting/parent
- Sensor tags (array)routing/tags
event/ fields (varies by event_type):
- Process events:
,event/FILE_PATH
,event/COMMAND_LINEevent/PROCESS_ID - DNS events:
,event/DOMAIN_NAMEevent/IP_ADDRESS - Network events:
event/NETWORK_ACTIVITY/?/IP_ADDRESS
Query Example:
-24h | * | NEW_PROCESS | event/COMMAND_LINE contains 'powershell'
Detection Stream Structure
Detections are alerts created when D&R rules match. They inherit event routing and add detection metadata:
{ "cat": "Suspicious PowerShell", "source": "dr-general", "routing": { /* same as event routing */ }, "detect": { /* copy of event data */ }, "detect_id": "f1e2d3c4-...", "priority": 7, "detect_data": { "suspicious_file": "C:\\Windows\\System32\\powershell.exe", "encoded_command": "SGVsbG8=" } }
Top-level detection fields:
- Detection name/categorycat
- Rule source (dr-general, dr-managed, fp)source
- Unique detection IDdetect_id
- Priority 0-10 (higher = more critical)priority
- Structured IOCs extracted by ruledetect_data
- Name of the rule that created thissource_rule
- Tags from the rulerule_tags
routing/ fields - Same as event routing (sid, hostname, event_time, etc.)
detect/ fields - Access the original event data:
- File path from triggering eventdetect/FILE_PATH
- Command line from eventdetect/COMMAND_LINE
- Domain from DNS eventdetect/DOMAIN_NAME
Query Examples:
Note: Select "Detections" in the Query Console UI source dropdown, then use these queries:
Query high-priority detections from all sensors:
-7d | * | * | priority > 5
Query specific detection category with event data filter:
-24h | * | "Suspicious PowerShell" | detect/COMMAND_LINE contains '-enc'
Query extracted IOCs from detect_data across all detections:
-24h | * | * | detect_data/suspicious_file ends with '.exe'
Audit Stream Structure
Audit logs track platform management actions. They have a flat structure:
{ "oid": "8cbe27f4-...", "ts": "2024-06-05T14:23:18Z", "etype": "config_change", "msg": "D&R rule created", "ident": "user@company.com", "entity": { "type": "dr_rule", "name": "detect-encoded-powershell" }, "mtd": { "action": "create", "source_ip": "203.0.113.10" } }
Top-level audit fields:
- Organization IDoid
- ISO 8601 timestamp stringts
- Event type (config_change, api_call, user_action)etype
- Human-readable messagemsg
- Identity performing action (email, API key)ident
- Origin of action (api, ui, cli)origin
entity/ fields - Object affected:
- Type of object (dr_rule, sensor, output)entity/type
- Object nameentity/name
- Sensor ID (for sensor actions)entity/sid
mtd/ fields - Action characteristics:
- Action type (create, update, delete)mtd/action
- Source IP addressmtd/source_ip
Query Examples:
Note: Select "Audit" in the Query Console UI source dropdown, then use these queries:
Track configuration changes:
-7d | * | config_change
Find who modified D&R rules:
-30d | * | * | entity/type == 'dr_rule' and mtd/action == 'update'
Monitor specific user actions:
-7d | * | * | ident == 'admin@company.com'
Cross-Stream Queries
LCQL can't JOIN across streams, but you can correlate data by querying each stream separately:
- Query detections (select "Detections" source), note the sensor ID:
-24h | * | "Malware Detected" | | routing/sid
- Query events from that sensor (select "Events" source):
-24h | sid == 'bb4b30af-...' | NEW_PROCESS
Field Access Patterns
Nested fields use
/ separator:
- Parent process pathevent/PARENT/FILE_PATH
- IOC from detectiondetect_data/suspicious_file
- Entity name in audit logentity/name
Array access uses
? wildcard:
- Any IP in arrayevent/NETWORK_ACTIVITY/?/IP_ADDRESS
- Any tag valuerouting/tags/?
Windows Event Logs have deep nesting:
- Windows Event IDevent/EVENT/System/EventID
- Event data field namesevent/EVENT/EventData/Data/?/@Name
Field Path Reference
Common Event Fields
Process Events:
- Executable pathevent/FILE_PATH
- Command line argumentsevent/COMMAND_LINE
- File hashevent/HASH
- Parent process pathevent/PARENT/FILE_PATH
- Parent PIDevent/PARENT/PROCESS_ID
- Code signing statusevent/SIGNATURE/FILE_IS_SIGNED
DNS Events:
- Queried domainevent/DOMAIN_NAME
- Query typeevent/DNS_TYPE
Network Events:
- Source IPevent/NETWORK_ACTIVITY/SOURCE/IP_ADDRESS
- Destination IPevent/NETWORK_ACTIVITY/DESTINATION/IP_ADDRESS
- Destination portevent/NETWORK_ACTIVITY/DESTINATION/PORT
Windows Event Log:
- Event IDevent/EVENT/System/EventID
- Event-specific dataevent/EVENT/EventData/*
Routing Fields
- Sensor IDrouting/sid
- Hostnamerouting/hostname
- Event timestamprouting/event_time
- Event typerouting/event_type
- Platformrouting/plat
Important Notes
- String quotes: Use single quotes
or double quotes'value'
for string values"value" - Regex syntax: Use backticks for regex patterns:
matches `^10\.3\..*` - Case sensitivity: Most operators are case-sensitive unless specified otherwise
- Wildcards: Use
to match any field in an eventevent/* - Paging: Aggregation queries (GROUP BY, COUNT) compute all results automatically
- Tab completion: CLI supports tab completion for field names and event types
When to Use LCQL
- Threat hunting across historical telemetry
- Investigating security incidents
- Building baselines and detecting anomalies
- Searching for specific indicators
- Analyzing patterns across sensors
- Validating detection rules before deployment
Query Building Workflow
- Define scope: What are you looking for?
- Set time range: How far back to search?
- Select sensors: Which systems are relevant?
- Choose events: Which event types contain the data?
- Build filter: What conditions must match?
- Add projection: What fields do you need? Any aggregation?
- Test with dry run: Check cost estimate
- Run query: Execute and refine as needed
Best Practices
- Start with narrow time ranges and expand if needed
- Use specific sensor selectors to reduce scope
- Filter by event type before using wildcards
- Test queries with
to estimate costsdryrun - Use projections to extract only needed fields
- Leverage aggregation for pattern analysis
- Save frequently used queries for reuse
- Document complex queries for team knowledge sharing
When helping users build queries:
- Ask clarifying questions about their investigation goals
- Suggest appropriate time ranges based on use case
- Recommend sensor selectors to narrow scope
- Provide examples similar to their needs
- Explain cost implications of broad queries
- Offer optimization suggestions for expensive queries
- Show both simple and aggregated query options