1. Overview

FusionSOC is an AI-powered Security Operations Center platform built by Fusion Cybersecurity. It integrates with LimaCharlie EDR to continuously ingest, enrich, triage, and respond to security detections โ€” fully autonomously or with human-in-the-loop approval.

FusionSOC is the defensive counterpart to FusionTester (our automated pentesting pipeline). Where FusionTester attacks, FusionSOC defends.

Key Capabilities

  • Continuous Detection Polling โ€” Background polling of the LimaCharlie Insight API every 30 seconds with cursor-based pagination, deduplication, and 24-hour backfill on first startup
  • Contextual Enrichment โ€” Every detection is enriched with sensor metadata (hostname, IP, OS, tags), process tree (parent/child chains via atom traversal), recent sensor events (30-minute window), D&R rule metadata, and MITRE ATT&CK technique mappings
  • Multi-Model Voting Triage โ€” 3 local LLM models (e.g. qwen3.5:4b, qwen3:4b, deepseek-r1:8b) analyze each detection in parallel. Unanimous verdicts trigger auto-action: all-FP โ†’ auto-close, all-TP โ†’ escalate to Incident Response. Split votes โ†’ manual review. Gemini CLI available as primary with Ollama fallback (qwen3.5:27b) for single-model mode
  • Custom Case Management โ€” SQLite-backed case system with automatic case creation, intelligent detection grouping (same sensor + category + 30-minute window), full timeline audit trails, analyst notes, and SOC metrics (MTTR, open case counts)
  • Smart Action Execution โ€” Recommended actions like "check for lateral movement" are mapped to real LimaCharlie API investigation queries (NEW_REMOTE_THREAD, NETWORK_CONNECTIONS, etc.). Results stored as detailed case notes. Also supports sensor tagging and process kill with safety controls
  • Raw JSON Viewer โ€” Collapsible raw detection JSON and AI triage JSON viewers in every case detail page for analyst transparency
  • Hide Closed Cases โ€” Toggle button on the cases page to show/hide closed and resolved cases
  • Real-Time Dashboard โ€” Flask + WebSocket dashboard with dark cyberpunk theme, live detection feed, case management UI, sensor inventory, and response action queue
๐Ÿ’ก Design Philosophy

FusionSOC follows a "safe by default" principle. All destructive actions (network isolation, process kill) are disabled out of the box. The system will analyze and triage detections automatically, but containment actions require explicit opt-in via config.yaml and/or manual approval through the dashboard.

2. Architecture

System Diagram

graph TB subgraph FusionSOC ["โšก FusionSOC Platform"] LC["๐Ÿ”Œ LC API Client"] --> POLL["๐Ÿ“ก Detection Poller"] POLL --> ENR["๐Ÿ” Context Enricher"] ENR --> TRI["๐Ÿค– Agentic Triage Engine"] TRI --> CM["๐Ÿ“‹ Case Manager"] CM --> RESP["โšก Response Automator"] RESP --> LC CM --> DASH["๐Ÿ–ฅ๏ธ Web Dashboard\n Flask + SocketIO"] end subgraph Infra ["Infrastructure"] DB[("๐Ÿ’พ SQLite Database")] CFG["โš™๏ธ Config\n YAML + .env"] end CM --> DB POLL --> DB CFG --> LC LC -.->|"JWT Auth"| LCAPI["โ˜๏ธ LimaCharlie API"]

Data Flow

graph LR A["๐Ÿ“ก LC Insight API"] -->|"Poll every 30s"| B["Detection\nPoller"] B -->|"Tag: fusion-soc-pulled"| C["Dedup &\nStore"] C --> D["Context\nEnricher"] D -->|"Sensor + Process Tree\n+ Events + MITRE"| E["๐Ÿค– AI Triage\nGemini / Ollama"] E -->|"Tag: fusion-soc-triage"| F["JSON Verdict"] F --> G["๐Ÿ“‹ Case\nManager"] G -->|"Tag: fusion-soc-case"| H{"Severity?"} H -->|"Critical / High"| I["๐Ÿšจ Alert\nTag: fusion-soc-alert"] H -->|"Med / Low / Info"| J["๐Ÿ“Š Dashboard"] I --> J G --> K["โšก Response\nAutomator"]

At every stage, sensors are tagged with pipeline step markers (fusion-soc-pulled, fusion-soc-triage, fusion-soc-case, fusion-soc-alert) for full observability.

Module Reference

Module File Purpose
config fusionsoc/config.py YAML + .env config loader with ${VAR} interpolation
utils fusionsoc/utils.py Logging setup, timestamp helpers, severity mapping
lc_client fusionsoc/lc_client.py LimaCharlie REST API wrapper โ€” JWT auth, 30+ endpoint methods
models fusionsoc/models.py SQLAlchemy ORM โ€” 6 database tables
poller fusionsoc/poller.py Background detection polling with cursor pagination & dedup
enricher fusionsoc/enricher.py Detection context enrichment (sensor, process tree, MITRE)
triage fusionsoc/triage.py Multi-model voting triage engine (3 Ollama models in parallel)
case_manager fusionsoc/case_manager.py Case creation, grouping, status workflow, notes, metrics
responder fusionsoc/responder.py Smart investigation + response actions with safety controls
dashboard dashboard/app.py Flask + SocketIO web application (routes, API, WebSocket)
main main.py Entry point โ€” wires all components, CLI options, shutdown

3. Installation & Setup

Prerequisites

  • Python 3.10+
  • LimaCharlie account with an organization and API key
  • Gemini CLI installed (primary LLM) โ€” sudo npm install -g @google/gemini-cli
  • Ollama (optional fallback LLM) โ€” curl -fsSL https://ollama.ai/install.sh | sh
    Note: You may need to run hash -r or start a new terminal session if the ollama command is not immediately found.
  • Note: Node.js and NPM are required to install the Gemini CLI. The dashboard itself is pure Python (Flask).

Quick Start

# Clone / navigate to the project
cd /home/dfusion/Documents/FusionSOC

# 1. Create your .env file from the example
cp .env.example .env

# 2. Edit .env with your credentials
#    LC_OID=your-limacharlie-org-id
#    LC_API_KEY=your-api-key
#    DASHBOARD_SECRET=random-secret-string

# 3. Activate the virtual environment
source venv/bin/activate

# 4. Launch FusionSOC
python main.py

The dashboard will start at http://localhost:5000. The detection poller begins immediately, pulling the last 24 hours of detections as backfill.

CLI Options

Flag Description
--config / -c Path to a custom config.yaml file (default: ./config.yaml)
--env / -e Path to a custom .env file (default: ./.env)
--no-poll Disable detection polling โ€” dashboard only mode
--no-dashboard Disable web dashboard โ€” headless poller mode
--port / -p Override the dashboard port (default: 5000)

LimaCharlie API Key Permissions

Your API key must have the following permissions:

insight.det.list    โ€” Fetch detections
insight.evt.get     โ€” Fetch historical events
sensor.list         โ€” List and export sensors
sensor.task         โ€” Send commands to sensors (isolate, kill, etc.)
sensor.tag          โ€” Add/remove sensor tags
dr.list             โ€” List Detection & Response rules
org.get             โ€” Get organization info
hive.*              โ€” Access Hive records (D&R rules, FP rules)

4. Configuration Reference

All configuration lives in config.yaml. Environment variables referenced as ${VAR_NAME} are auto-interpolated from the .env file or system environment.

LimaCharlie Connection

limacharlie:
  oid: "${LC_OID}"          # Your organization ID
  api_key: "${LC_API_KEY}"  # REST API key
  api_base: "https://api.limacharlie.io"  # API base URL

Detection Polling

polling:
  interval_seconds: 30       # Seconds between poll cycles
  backfill_hours: 24         # Hours of historical data to pull on first start
  max_detections_per_poll: 100  # Max detections per API call

AI Triage Engine

triage:
  primary_llm: "gemini-cli"     # Primary LLM for single-model mode
  fallback_llm: "ollama"        # Fallback if primary fails
  ollama_model: "qwen3.5:27b"  # Default Ollama backup model
  ollama_host: "http://localhost:11434"  # Ollama API endpoint
  auto_triage: true             # Automatically triage new detections
  confidence_threshold: 0.7     # Minimum confidence for auto-actions
  max_concurrent_triages: 5     # Thread pool size

  # Multi-Model Voting System (v1.0)
  voting_enabled: true          # Enable 3-model parallel voting
  voting_models:                # Models that vote on each detection
    - "qwen3.5:4b"              # Fast first opinion
    - "qwen3:4b"                # Deep technical analysis
    - "deepseek-r1:8b"          # Alternative model

Response Automation

response:
  auto_respond: false           # Master switch for auto-response (DISABLED by default)
  auto_isolate_on_critical: false  # Auto-isolate on critical + true_positive
  auto_tag: true                # Always tag investigated sensors
  tag_prefix: "fusionsoc"       # Prefix for auto-applied tags
  manual_approval_required: true  # Queue destructive actions for approval
โš ๏ธ Caution: Auto-Respond

Setting auto_respond: true AND auto_isolate_on_critical: true will cause FusionSOC to automatically network-isolate sensors when a critical severity + true positive verdict is returned by the AI. Only enable this after thoroughly validating AI verdict accuracy in your environment.

Dashboard & Database

dashboard:
  host: "0.0.0.0"
  port: 5000
  secret_key: "${DASHBOARD_SECRET}"

database:
  path: "./fusionsoc.db"     # SQLite database file path

logging:
  level: "INFO"              # DEBUG, INFO, WARNING, ERROR
  file: "./fusionsoc.log"    # Log file path

5. LimaCharlie API Client

The LCClient class (fusionsoc/lc_client.py) wraps the LimaCharlie REST API with automatic JWT authentication, error handling, and helper methods. All endpoints are prefixed with the configured api_base.

Authentication

JWT tokens are obtained by POSTing your OID + API secret to https://jwt.limacharlie.io. Tokens are cached and automatically refreshed 60 seconds before expiry (~1 hour lifespan).

Available Methods

Method LC Endpoint Description
get_detections() GET /insight/{oid}/detections Fetch detections with cursor pagination, time range, category, and sensor filters
get_detection_by_id() GET /insight/{oid}/detections/{atom} Fetch a single detection by its detect ID
get_detection_breakdown() GET /insight/{oid}/detections/breakdown Category-level detection counts over a time range
get_detection_stats() GET /insight/{oid}/detections/stats Detection count timeseries (per hour/day)
get_historic_events() GET /insight/{oid}/{sid} Historical telemetry events for a sensor
get_event_by_atom() GET /insight/{oid}/{sid}/{atom} Specific event by its atom ID
get_children_of_atom() GET /insight/{oid}/{sid}/{atom}/children Child events (process tree traversal)
export_sensors() POST /export/{oid}/sensors Full sensor list with metadata and tags
find_sensor_by_hostname() GET /hostnames/{oid} Find sensors by hostname prefix search
kill_process() POST /sensor_task Kill a process on a sensor by PID
add_sensor_tag() POST /sensors/{oid}/{sid}/tags Add an investigative tag (with optional TTL)
get_dr_rules() GET /hive/dr-general/{oid} List all Detection & Response rules
get_mitre_report() GET /mitre/{oid} Generate MITRE ATT&CK coverage map
search_object() GET /insight/{oid}/objects/{type} IOC search (IP, domain, hash, etc.)
health_check() Multiple Quick health check (org info + online sensor count)

6. Detection Poller

The poller (fusionsoc/poller.py) is a background thread that continuously queries LimaCharlie for new detections.

Behavior

  • Polling interval: configurable (default 30s). Each cycle queries GET /insight/{oid}/detections
  • Cursor pagination: automatically follows next_cursor to retrieve all new detections
  • Deduplication: maintains an in-memory set of seen detect_id values, plus database-level UNIQUE constraint
  • Backfill: on first startup (when no poll state exists in DB), pulls the last N hours (default 24) of detections
  • Resume: stores the last poll timestamp in the poll_state table so it resumes cleanly after restart
  • Callback: each new detection triggers on_new_detection(detection_record, raw_json) which feeds the triage pipeline

7. Context Enricher

The enricher (fusionsoc/enricher.py) wraps each raw detection with surrounding context before it reaches the AI triage engine.

Enrichment Layers

Layer Data Source What's Added
sensor Sensor export API (cached) Hostname, external IP, internal IP, platform, OS, isolated status, tags, version, enrollment date
process_tree Atom children API + event data Current process (file path, command line, PID, user), parent atom, child process summary (up to 5)
recent_events Historic events API Event type distribution in a ยฑ30 minute window around the detection (e.g., "NEW_PROCESS: 10, DNS_REQUEST: 20")
rule_info D&R rules Hive (cached) Rule name, enabled status, comment, tags
mitre_tags Detection rule_tags field Extracted MITRE ATT&CK technique IDs (e.g., T1059.001)

The sensor cache and D&R rule cache are populated on first use and can be invalidated via enricher.invalidate_cache().

8. AI Triage Engine

The triage engine (fusionsoc/triage.py) is the analytical brain of FusionSOC. It uses a multi-model voting system where 3 local LLMs independently analyze each detection.

๐Ÿ—ณ๏ธ Multi-Model Voting System

When voting is enabled (default), each detection is sent to all 3 Ollama models in parallel. Each model independently produces a verdict, severity, and confidence score. Results are tallied:

graph LR D["๐Ÿ”” Detection"] --> M1["llama3.1:8b"] D --> M2["qwen2.5-coder:32b"] D --> M3["qwen3-coder:30b"] M1 --> V{"Vote\nTally"} M2 --> V M3 --> V V -->|"Unanimous FP"| FP["๐ŸŸข Auto-Close\nas False Positive"] V -->|"Unanimous TP"| TP["๐Ÿ”ด Escalate\nto Incident Response"] V -->|"Split Vote"| MR["โš ๏ธ Manual\nAnalyst Review"]
Vote Outcome Auto-Action
Unanimous false_positive Recommend close case as FP
Unanimous true_positive Recommend escalate to IR + isolate + collect forensics
Unanimous benign Recommend close as benign
Majority vote Use winning verdict, flag confidence level
Split vote Flag for mandatory manual analyst review

Every triage result includes a voting object in the triage JSON showing each model's individual verdict, confidence, severity, and risk score for full transparency.

Fallback: Single-Model Mode

If voting is disabled (voting_enabled: false), the engine falls back to the original strategy:

  1. Primary: Gemini CLI (gemini -p "...") โ€” cloud-based, high quality
  2. Fallback: Ollama (POST /api/generate) โ€” local, privacy-first
  3. If both fail, the detection is marked suspicious with confidence: 0.0 and flagged for manual review

Prompt Structure

The system prompt instructs the LLM to act as a senior SOC analyst at Fusion Cybersecurity. Each detection prompt includes:

  • DETECTION EVENT โ€” rule name, category, event type, MITRE tags
  • EVENT DATA โ€” file path, command line, PID, user, hash, registry key, IPs, domains
  • SENSOR CONTEXT โ€” hostname, IPs, platform, online/isolated status, tags
  • PROCESS TREE โ€” current process details, child process summary
  • RECENT SENSOR EVENTS โ€” event type distribution in a 30-minute window
  • DETECTION RULE INFO โ€” rule name, enabled status, comment

Triage Output Schema (JSON)

{
  "severity": "critical | high | medium | low | informational",
  "verdict": "true_positive | false_positive | suspicious | benign",
  "confidence": 0.0 - 1.0,
  "summary": "[UNANIMOUS VOTE 3/3] One-paragraph detailed analysis",
  "ioc_analysis": "Explanation of why IOCs are malicious or benign",
  "iocs_extracted": ["ip:1.2.3.4", "hash:abc123", "domain:evil.com"],
  "mitre_techniques": ["T1059.001", "T1027"],
  "recommended_actions": ["ESCALATE to IR", "Isolate sensor immediately"],
  "investigation_questions": ["Was this user active at this time?"],
  "false_positive_reason": null | "explanation",
  "risk_score": 0 - 100,
  "voting": {
    "mode": "unanimous | majority | split",
    "auto_action": "escalate_ir | auto_close_fp | manual_review",
    "total_models": 3,
    "winning_verdict": "true_positive",
    "winning_count": 3,
    "votes": [{"model": "llama3.1:8b", "verdict": "...", "confidence": 0.95}],
    "vote_summary": ["llama3.1:8b: true_positive (critical, 95%)"]
  }
}

Severity Guidelines

Severity Criteria
Critical Active breach, data exfiltration, ransomware, credential theft in progress
High Confirmed malicious activity, C2 communication, privilege escalation
Medium Suspicious behavior needing investigation, potential lateral movement
Low Minor policy violation, reconnaissance activity
Info Noise, benign anomaly, known good behavior

9. Case Management

The case manager (fusionsoc/case_manager.py) provides a full case lifecycle with automatic creation, intelligent grouping, and SOC metrics.

Case Lifecycle

stateDiagram-v2 [*] --> new new --> triaging : AI analysis begins triaging --> open : Verdict returned open --> investigating : Analyst picks up investigating --> contained : Threat contained contained --> resolved : Remediation complete resolved --> closed : Case finalized open --> resolved : False positive investigating --> resolved : Benign confirmed

Detection Grouping

Detections are automatically grouped into an existing case if they share the same sensor ID + same category and the case was created within the last 30 minutes and is still in an active status (new, triaging, open, or investigating). If the new detection has a higher severity, the case severity is upgraded.

Database Schema

Table Key Fields Purpose
cases id, title, severity, status, verdict, assigned_to, summary_ai, created_at, resolved_at, detection_count Top-level case record
detections id, detect_id (unique), category, rule_name, sensor_id, hostname, severity_ai, verdict_ai, confidence_ai, raw_json, enriched_json, triage_json, case_id Every detection record
case_notes id, case_id, author, content, note_type, timestamp Analyst notes and AI analysis
case_actions id, case_id, action_type, target, status, result, executed_by, timestamp Response actions (pending, executed, failed, rejected)
case_timeline id, case_id, event_type, description, actor, timestamp Full audit trail for every case event
poll_state id, last_cursor, last_timestamp, updated_at Tracks poller state for resume after restart

SOC Metrics

  • MTTR (Mean Time to Resolve) โ€” Average time from case creation to resolution
  • Cases by Status โ€” Breakdown across lifecycle stages
  • Cases by Severity โ€” Distribution of critical/high/medium/low/info
  • Detections by Verdict โ€” True positive / false positive / suspicious / benign
  • Open Cases โ€” Count of cases in new, triaging, open, or investigating status

10. Response Automation

The responder (fusionsoc/responder.py) executes containment, remediation, and smart investigation actions through the LimaCharlie API.

Available Actions

Action What It Does Trigger Destructive?
Tag Sensor POST /{sid}/tags?tags={tag} Always (when auto_tag: true) No
Kill Process os_kill_process -p {pid} Manual execution only ๐Ÿ”ด Yes
Smart Investigation Queries LC historic events API based on action keywords Approve a "recommended" action No (read-only)

๐Ÿ” Smart Investigation System

When you approve a recommended action (e.g., "Check for lateral movement"), FusionSOC maps the action text to real LimaCharlie API investigation queries:

Action Contains Events Queried
"lateral" NEW_REMOTE_THREAD, NETWORK_CONNECTIONS, NEW_PROCESS
"process" NEW_PROCESS, EXISTING_PROCESS, TERMINATE_PROCESS
"persist" / "autorun" REGISTRY_CREATE, NEW_AUTORUN, SCHEDULED_TASK
"network" NETWORK_CONNECTIONS, DNS_REQUEST
"credential" NEW_PROCESS, SENSITIVE_PROCESS_ACCESS
"file" FILE_CREATE, FILE_DELETE, FILE_MODIFIED
"dns" DNS_REQUEST
"exfil" NETWORK_CONNECTIONS, FILE_CREATE
(anything else) General event overview sweep

Results are stored as a detailed case note with event counts, extracted details (file paths, command lines, IPs, domains), and a timeline entry.

Safety Controls

  • auto_respond: false (default) โ€” All destructive actions are queued as "pending" and must be manually approved/rejected from the Actions page in the dashboard
  • Network isolation is disabled โ€” The isolate_sensor() and rejoin_sensor() methods have been intentionally removed for safety
  • manual_approval_required: true (default) โ€” Actions go to the approval queue
  • Every action (executed, failed, or rejected) is logged in case_actions with a case_timeline audit entry

Pipeline Tags

Every sensor processed by FusionSOC is tagged at each stage of the pipeline for full observability in the LimaCharlie console:

Tag Applied When Purpose
fusion-soc-pulled Detection fetched from API Confirms sensor's detection was ingested by FusionSOC
fusion-soc-triage AI triage analysis completes Confirms AI has analyzed the detection
fusion-soc-case Detection linked to a case Confirms case management is tracking this sensor
fusion-soc-alert Critical or High severity verdict Flags high-priority sensors for immediate attention

11. Dashboard Pages

Overview (/)

Live operational overview showing: open cases count, total detections, total cases, AI triage stats (count + errors), poller status (active/stopped, cache size), pending response actions, severity distribution bar chart, AI verdict distribution, recent 10 cases table (clickable), and recent 15 detections table.

Cases (/cases)

Filterable case list. Filters include status (new, triaging, open, investigating, contained, resolved, closed) and severity (critical, high, medium). Each row is clickable and shows ID, title, severity badge, status badge, verdict, detection count, assignee, and creation date. Hide Closed toggle โ€” Click "๐Ÿšซ Hide Closed" to show/hide closed and resolved cases (active by default).

Case Detail (/cases/<id>)

Full case view with: AI analysis summary (with voting results), all linked detections (with IOCs and MITRE badges), collapsible Raw Detection JSON and Triage JSON viewers, response actions table (with execute/reject buttons that trigger smart investigations), analyst notes (add new notes via textarea), and full timeline audit trail.

Detections (/detections)

Raw detection feed with filters for severity and verdict. Columns include category, rule name, hostname, event type, AI severity badge, verdict, confidence percentage, and linked case ID.

Sensors (/sensors)

Live sensor inventory pulled from the LimaCharlie API. Shows online/offline status dot, hostname, external IP, internal IP, platform icon (Windows/macOS/Linux/ChromeOS), version, isolation badge, and up to 3 tags per sensor.

Actions (/actions)

Manual approval queue for pending response actions. Approving a recommended action triggers a smart investigation that queries the LimaCharlie API and stores results as case notes. Shows action type (with icon), target, status, queued timestamp, and approve/reject buttons.

Documentation (/docs)

This page. Comprehensive reference for the entire platform.

12. REST API Reference

The dashboard exposes internal JSON API endpoints for programmatic access and integration.

Endpoint Method Description
/api/metrics GET Case management metrics (counts, MTTR, severity/status/verdict breakdowns)
/api/health GET LimaCharlie connection health check (org name, sensors online/total)
/api/detections/recent GET Last 20 detections with triage results
/api/cases/recent GET Last 20 cases
/api/poller/stats GET Poller statistics (total stored, cache size, last poll timestamp, running status)
/api/cases/{id}/status POST Update case status. Body: {"status": "investigating"}
/api/cases/{id}/assign POST Assign case. Body: {"assignee": "analyst@fusion.sec"}
/api/cases/{id}/notes POST Add note. Body: {"content": "...", "author": "analyst"}
/api/actions/{id}/execute POST Approve and execute a pending response action
/api/actions/{id}/reject POST Reject a pending response action

13. WebSocket Events

The dashboard uses Socket.IO for real-time push updates. Connect to the same host/port as the dashboard.

Event Direction Payload
status Server โ†’ Client {"message": "Connected to FusionSOC"}
new_detection Server โ†’ Client {"detect_id", "category", "hostname", "rule_name", "severity_ai", "verdict_ai"}
case_update Server โ†’ Client {"id", "title"}

14. Troubleshooting

LC health check fails on startup

Verify your LC_OID and LC_API_KEY in .env. Test with: curl -X POST https://jwt.limacharlie.io -d '{"oid":"YOUR_OID","secret":"YOUR_KEY"}'. A valid response returns a JWT token.

No detections appearing

  • Check the poller is running (dashboard overview shows "๐ŸŸข Active")
  • Verify your org has D&R rules generating detections
  • Check fusionsoc.log for API errors
  • Ensure the API key has insight.det.list permission

AI triage returning errors

  • Verify Gemini CLI is installed: which gemini
  • If using Ollama fallback, verify it's running: curl http://localhost:11434/api/tags
  • Check fusionsoc.log for LLM error details
  • The fallback will auto-set verdict: suspicious with confidence: 0.0 if both LLMs fail

Dashboard not loading

  • Check port conflicts: ss -tlnp | grep 5000
  • Try a different port: python main.py -p 8080
  • Check fusionsoc.log for Flask startup errors

Database reset

To start fresh, delete the SQLite database file: rm fusionsoc.db. It will be recreated automatically on next startup with empty tables.

๐Ÿ“ฌ Support

For issues, feature requests, or to report false positive patterns, contact Fusion Cybersecurity.