Sensor Tasking - Query and Command EDR Agents
This skill orchestrates sending tasks (commands) to EDR sensors and handling responses. It solves two key challenges of sensor tasking:
- Offline agents: Sensors may be offline when you want to task them
- Response collection: Some tasks generate data that needs to be collected
LimaCharlie Integration
Prerequisites: Run
/init-lcto initialize LimaCharlie context.
API Access Pattern
All LimaCharlie API calls go through the limacharlie-api-executor sub-agent:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: <function-name>
- Parameters: {<params>}
- Return: RAW | <extraction instructions>
- Script path: {skill_base_directory}/../../scripts/analyze-lc-result.sh"
)
Critical Rules
| Rule | Wrong | Right |
|---|---|---|
| MCP Access | Call mcp__* directly | Use limacharlie-api-executor sub-agent |
| LCQL Queries | Write query syntax manually | Use generate_lcql_query() first |
| Timestamps | Calculate epoch values | Use date +%s or date -d '7 days ago' +%s |
| OID | Use org name | Use UUID (call list_user_orgs if needed) |
When to Use
Use this skill when the user wants to:
- Live Response: Query running processes, network connections, registry keys, services
- Forensic Collection: Collect memory maps, file listings, autoruns, packages
- Fleet Operations: Execute commands across many sensors (by tag, platform, etc.)
- Incident Response: Isolate hosts, kill processes, gather evidence
- Data Collection at Scale: Get OS versions, installed software, users across fleet
Example requests:
- "Get running processes from sensor X"
- "List all files in C:\Windows\Temp on compromised hosts"
- "Get OS version from all Windows servers when they come online"
- "Run a memory collection on all hosts tagged 'incident-response'"
- "Execute netstat on all online Linux servers"
Core Concepts
Challenge 1: Offline Agents
Direct tasking (get_processes, dir_list, etc.) only works for online sensors. If a sensor is offline, the task fails immediately.
Reliable tasking queues tasks for delivery when sensors come online. Tasks persist for a configurable TTL (default: 1 week).
| Approach | Use When | Pros | Cons |
|---|---|---|---|
| Direct Task | ≤5 online sensors, need immediate response | Fast, response inline | Fails if offline |
| Reliable Task | >5 sensors, offline sensors, or can wait | Guaranteed delivery | Response via telemetry |
Challenge 2: Receiving Responses
| Method | Use When | How It Works |
|---|---|---|
| Inline Response | Direct tasking small numbers | Response returned directly from API |
| LCQL Query | Need to collect reliable task responses | Query for routing/investigation_id containing your context |
| D&R Rule | Need automated action on responses | Create rule matching investigation_id, with response actions |
Decision Tree
START: User wants to task sensors
|
v
Are all targets online AND ≤5 sensors?
|
|--YES--> Use Direct Tasking (inline response, parallel execution)
|
|--NO--> Use Reliable Tasking (>5 sensors OR any offline)
|
v
Do you need the response data?
|
|--NO (action-only like isolate)--> Create Reliable Task, done
|
|--YES--> Do you need automated handling?
|
|--NO--> Create Reliable Task, wait 2+ min, then LCQL query
|
|--YES--> 1. Create D&R rule with TTL FIRST
2. THEN create Reliable Task
(rule must exist before task to avoid missing responses)
How to Use
Step 1: Understand the Request
Parse the user's request to determine:
- Target scope: Single sensor, selector, all sensors?
- Task type: Data collection (needs response) or action (unidirectional)?
- Urgency: Need immediate response or can wait?
- Response handling: Inline, LCQL collection, or automated D&R?
Step 2: Get Organization and Sensors
If you don't have the OID, get it first:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: list_user_orgs
- Parameters: {}
- Return: RAW"
)
Check sensor status if targeting specific sensors:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: is_online
- Parameters: {\"oid\": \"[oid]\", \"sid\": \"[sid]\"}
- Return: RAW"
)
CRITICAL: Filter to Taskable EDR Sensors
Before spawning executor agents or tasking sensors, you MUST verify sensors are EDR agents (not adapters or cloud sensors). Non-EDR sensors will fail with
UNSUPPORTED_FOR_PLATFORM.Taskable sensors require BOTH:
- Platform:
windows,linux,macos, orchrome- Architecture: NOT
usp_adapter(code 9)A sensor running on Linux platform but with
arch=usp_adapteris an adapter (USP), not an EDR agent. These adapters forward logs but cannot execute commands.Use combined selector when listing sensors:
Task( subagent_type="lc-essentials:limacharlie-api-executor", model="haiku", prompt="Execute LimaCharlie API call: - Function: list_sensors - Parameters: { \"oid\": \"[oid]\", \"selector\": \"(plat==windows or plat==linux or plat==macos) and arch!=usp_adapter\", \"online_only\": true } - Return: List of SIDs with hostnames and platforms" )Or check sensor info before tasking - verify both
platformis in["windows", "linux", "macos", "chrome"]ANDarchis not9/usp_adapter.
Step 3A: Direct Tasking (≤5 Online Sensors)
For immediate data collection from a small number of online sensors (up to 5), use direct tasking functions with parallel execution.
Available Direct Task Functions (return response inline):
| Function | Description | Common Use |
|---|---|---|
get_processes | Running processes | Process investigation |
get_process_modules | Loaded modules | Malware analysis |
get_network_connections | Active connections | C2 hunting |
get_os_version | OS details | Asset inventory |
get_users | System users | Account enumeration |
get_services | Windows services | Persistence check |
get_drivers | Loaded drivers | Rootkit detection |
get_autoruns | Persistence mechanisms | Malware persistence |
get_packages | Installed packages | Software inventory |
get_registry_keys | Registry values | Config/persistence |
dir_list | Directory listing | File investigation |
find_strings | String search | Memory forensics |
yara_scan_process | YARA scan process | Malware detection |
yara_scan_file | YARA scan file | File analysis |
yara_scan_directory | YARA scan directory | Bulk scanning |
yara_scan_memory | YARA scan memory | Memory malware |
Example - Get processes from a sensor:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: get_processes
- Parameters: {\"oid\": \"[oid]\", \"sid\": \"[sid]\"}
- Return: RAW"
)
Step 3B: Reliable Tasking (>5 Sensors or Offline)
For more than 5 sensors, offline sensors, or fleet-wide operations, use reliable tasking.
IMPORTANT: Order of Operations
If you need automated response handling via D&R rules, you MUST create the D&R rule FIRST, before creating the reliable task. This ensures no responses are missed between task creation and rule deployment.
Order: D&R Rule → Reliable Task (not the other way around)
Read the function documentation first:
Read: ${CLAUDE_PLUGIN_ROOT}/skills/limacharlie-call/functions/reliable-tasking.md
Create a reliable task:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: reliable_tasking
- Parameters: {
\"oid\": \"[oid]\",
\"task\": \"os_version\",
\"selector\": \"plat==windows\",
\"context\": \"fleet-inventory-2024-01\",
\"ttl\": 86400
}
- Return: RAW"
)
Key Parameters:
task: The command to execute (e.g.,os_version,mem_map --pid 4,run --shell-command whoami)selector: Sensor selector expression (e.g.,plat==windows,production in tags,sid=='abc-123')context: Identifier that appears inrouting/investigation_idof responses (for collection)ttl: Time-to-live in seconds (default: 604800 = 1 week)
Available Task Commands:
Any sensor command can be used as a task. Common ones:
| Task Command | Description |
|---|---|
os_version | Get OS details |
mem_map --pid [pid] | Memory map of process |
mem_strings --pid [pid] | Strings from process memory |
file_get [path] | Get file contents |
dir_list [path] | List directory |
netstat | Network connections |
run --shell-command [cmd] | Execute shell command |
deny_tree -p [process] | Kill process tree |
isolate_network | Network isolation |
rejoin_network | End network isolation |
Step 4: Collecting Responses
Option A: LCQL Query (Manual Collection)
After reliable tasking, wait at least 2 minutes for responses to arrive, then query:
First, generate the LCQL query:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: generate_lcql_query
- Parameters: {
\"oid\": \"[oid]\",
\"query\": \"Find all events in the last 2 hours where investigation_id contains 'fleet-inventory-2024-01'\"
}
- Return: RAW"
)
Then run the query:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: run_lcql_query
- Parameters: {
\"oid\": \"[oid]\",
\"query\": \"[generated_query]\",
\"limit\": 1000
}
- Return: RAW"
)
Option B: D&R Rule (Automated Handling)
For automated response handling, create a D&R rule that matches the investigation_id.
CRITICAL: Create the D&R rule BEFORE creating the reliable task. Online sensors may respond within milliseconds - if the rule doesn't exist yet, those responses will be missed.
Step 1: Generate the detection:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: generate_dr_rule_detection
- Parameters: {
\"oid\": \"[oid]\",
\"description\": \"Match events where investigation_id contains 'fleet-inventory-2024-01'\"
}
- Return: RAW"
)
Step 2: Generate the response:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: generate_dr_rule_respond
- Parameters: {
\"oid\": \"[oid]\",
\"description\": \"Report to output 'siem' and add detection 'FLEET_INVENTORY_RESPONSE'\"
}
- Return: RAW"
)
Step 3: Validate the rule:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: validate_dr_rule_components
- Parameters: {
\"oid\": \"[oid]\",
\"detection\": [detection_yaml],
\"response\": [response_yaml]
}
- Return: RAW"
)
Step 4: Deploy with expiry:
Calculate expiry timestamp (e.g., 7 days from now):
date -d '+7 days' +%s
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: set_dr_general_rule
- Parameters: {
\"oid\": \"[oid]\",
\"name\": \"temp-fleet-inventory-handler\",
\"detection\": [detection_yaml],
\"response\": [response_yaml],
\"is_enabled\": true,
\"ttl\": [expiry_timestamp]
}
- Return: RAW"
)
Step 5: NOW create the reliable task
Only after the D&R rule is deployed, create the reliable task (see Step 3B above). The rule will catch all responses as sensors execute the task.
Monitoring Reliable Tasks
List pending tasks:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: list_reliable_tasks
- Parameters: {\"oid\": \"[oid]\"}
- Return: RAW"
)
Delete/cancel a task:
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: delete_reliable_task
- Parameters: {
\"oid\": \"[oid]\",
\"task_id\": \"[task_id]\"
}
- Return: RAW"
)
Example Workflows
Example 1: Get Processes from Single Sensor
User: "Get running processes from sensor abc-123"
# Check if online
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: is_online
- Parameters: {\"oid\": \"c7e8f940-...\", \"sid\": \"abc-123\"}
- Return: RAW"
)
# If online, get processes directly
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: get_processes
- Parameters: {\"oid\": \"c7e8f940-...\", \"sid\": \"abc-123\"}
- Return: RAW"
)
Example 2: Fleet-Wide OS Inventory
User: "Get OS version from all Windows servers when they come online"
# Create reliable task with context for later collection
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: reliable_tasking
- Parameters: {
\"oid\": \"c7e8f940-...\",
\"task\": \"os_version\",
\"selector\": \"plat==windows\",
\"context\": \"os-inventory-20240120\"
}
- Return: RAW"
)
Response to user: "Created reliable task to collect OS version from all Windows sensors.
- Online sensors will execute immediately
- Offline sensors will execute when they reconnect
- Task will remain active for 1 week (default TTL)
- Use context 'os-inventory-20240120' to query responses via LCQL"
Example 3: Incident Response Collection
User: "Run memory collection on all hosts tagged 'incident-response', I need the data sent to our SIEM"
# Step 1: Create D&R rule FIRST to forward responses to SIEM
# (Use detection-engineering skill or manual D&R creation)
# Rule should match: routing/investigation_id contains "ir-memcollect-001"
# Response should: report to SIEM output
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: set_dr_general_rule
- Parameters: {
\"oid\": \"c7e8f940-...\",
\"name\": \"temp-ir-memcollect-handler\",
\"detection\": {\"op\": \"contains\", \"path\": \"routing/investigation_id\", \"value\": \"ir-memcollect-001\"},
\"response\": [{\"action\": \"report\", \"name\": \"IR_MEMCOLLECT_RESPONSE\", \"to\": \"siem\"}],
\"is_enabled\": true,
\"ttl\": 172800
}
- Return: RAW"
)
# Step 2: THEN create reliable task (rule is now in place to catch responses)
Task(
subagent_type="lc-essentials:limacharlie-api-executor",
model="sonnet",
prompt="Execute LimaCharlie API call:
- Function: reliable_tasking
- Parameters: {
\"oid\": \"c7e8f940-...\",
\"task\": \"mem_map --pid 4\",
\"selector\": \"incident-response in tags\",
\"context\": \"ir-memcollect-001\",
\"ttl\": 172800
}
- Return: RAW"
)
Example 4: Quick Data Collection with Inline Response
User: "What's the OS version on all 5 of our production Linux servers?"
If sensors are online and small in number, parallel direct tasking is faster:
# Spawn parallel tasks for each sensor
Task(subagent_type="lc-essentials:limacharlie-api-executor", prompt="get_os_version for sid1...")
Task(subagent_type="lc-essentials:limacharlie-api-executor", prompt="get_os_version for sid2...")
Task(subagent_type="lc-essentials:limacharlie-api-executor", prompt="get_os_version for sid3...")
Task(subagent_type="lc-essentials:limacharlie-api-executor", prompt="get_os_version for sid4...")
Task(subagent_type="lc-essentials:limacharlie-api-executor", prompt="get_os_version for sid5...")
Important Notes
Taskable Sensors (EDR Only)
WARNING: Sensor tasking only works for EDR agents (real endpoint agents). Cloud sensors, adapters, and USP log sources cannot receive tasks.
Taskable sensors require BOTH conditions:
- Platform is Windows, Linux, macOS, or Chrome
- Architecture is NOT
usp_adapter(code 9)
Platforms that support tasking:
| Platform | Platform ID (hex) | Platform ID (decimal) | Selector |
|---|---|---|---|
| Windows | 0x10000000 | 268435456 | plat==windows |
| Linux | 0x20000000 | 536870912 | plat==linux |
| macOS | 0x30000000 | 805306368 | plat==macos |
| Chrome | Architecture 0x00000006 | 6 | arch==chromium |
Non-taskable sensors include:
- Cloud sensors (Office365, Okta, AWS, GCP, Azure, etc.)
- External adapters (
ext-*sensors like ext-zeek, ext-strelka) - USP log sources (Syslog, S3 buckets, webhooks)
- Any sensor with
arch=usp_adapter(architecture code 9) - even if platform is Linux/macOS
These sensors will return UNSUPPORTED_FOR_PLATFORM errors when tasked.
Recommendation: When tasking a fleet, filter by both platform AND architecture:
- Use combined selector:
(plat==windows or plat==linux or plat==macos) and arch!=usp_adapter - Or check sensor info for both
platformandarchfields before direct tasking
Extension Requirement
Reliable tasking requires the ext-reliable-tasking extension to be subscribed in the organization. If you get a 403 error, the extension may not be enabled.
Selector Syntax
Selectors use bexpr syntax:
plat==windows- Windows sensorsplat==linux- Linux sensorsproduction in tags- Sensors with 'production' taghostname matches '^web-'- Hostname starts with 'web-'sid=='abc-123-def-456'- Specific sensor*- All sensors
Context for Response Collection
The context parameter in reliable tasking:
- Appears in
routing/investigation_idof response events - Use unique identifiers (include date/time)
- Can be used in LCQL queries or D&R rules for matching
Timestamps
When setting TTL or expiry:
- Use bash for timestamp calculation:
date -d '+7 days' +%s - TTL is in seconds from now
- D&R rule expiry is absolute Unix timestamp
Related Skills
limacharlie-call- For function documentation and direct API operationsdetection-engineering- For creating D&R rules to handle responsessensor-health- For checking sensor online status across fleetsensor-coverage- For understanding fleet coverage before tasking
Reference
- reliable-tasking.md - Reliable task function details
- list-reliable-tasks.md - List pending tasks
- delete-reliable-task.md - Cancel tasks
- ext-reliable-tasking documentation
