Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rockfish Networks

Suricata, Supercharged.

A powerful bolt-on toolkit that transforms Suricata into a capable NDR with AI-powered detection and automated response.

Suricata-NativeReads EVE JSON directly from Unix socket.
Columnar StorageApache Parquet with Zstd compression for fast analytical queries.
Behavioral DetectionGraph-based threat hunting: beaconing, lateral movement, exfiltration.
Automated ResponsePublish alerts to MQTT, Kafka, or webhooks for n8n, Fluent Bit, Vector, or SIEM integration.
Interactive ReportsSelf-contained HTML dashboards with Chart.js and D3.js.
AI-ReadyMCP server and chat interface for conversational network analysis.
Air-Gap ReadyFully offline operation. No cloud dependencies required.
Single BinaryRust. No runtime dependencies. Deploy from .deb package or Docker container.

Architecture

Suricata EVE JSON ──► rockfish sigma ──► Parquet ──► S3 (optional)
                                              │
                          ┌───────────────────┼───────────────────┐
                          ▼                   ▼                   ▼
                       Report              Hunt                MCP/Chat
                    (HTML pages)      (threat detection)    (AI-native queries)
                                          │
                                          ▼
                                       Alert
                              (MQTT / Kafka / Webhook)

What Is an Event?

An event is a single JSON record from Suricata’s EVE log — one line, one record. Every alert, flow, DNS transaction, HTTP request, TLS handshake, or protocol log entry is one event. Event rate limits in license tiers are measured by counting these individual JSON records per minute.

Core Pipeline

Input — Rockfish connects directly to Suricata’s EVE output via Unix socket for real-time streaming or file tailing for batch processing. No agents. No sidecars. No middleware.

Sigma — Events are strongly typed, parsed into native Rust structures, and routed by event type. Optional enrichment layers add GeoIP geolocation and IP reputation scoring before writing to columnar storage. Configurable include/exclude event filtering, memory-bounded buffering, and multi-sensor partitioning for distributed deployments.

Store — All events are written to Apache Parquet with Zstd compression and hive-style date partitioning. Embedded DuckDB provides sub-second analytical SQL at query time. Optional AWS S3 / MinIO / DigitalOcean Spaces upload for long-term retention.

Analyze — Three complementary engines operate on the same Parquet data. Hunt builds communication graphs and applies 12 behavioral detection algorithms (beaconing, lateral movement, C2 fanout, port scanning, DNS tunneling, data exfiltration, and more) with ML-based anomaly detection for unknown threats. Report renders 12+ page self-contained HTML dashboards with Chart.js and D3. MCP and Chat expose data to AI assistants for conversational investigation.

Respond — Detection findings and enriched alerts are published to MQTT, Kafka, and webhooks for downstream automation. Integrate with Fluent Bit, Vector, n8n, Node-RED, or any consumer for SIEM forwarding and SOAR workflows.

Commands at a Glance

CommandDescription
rockfish sigmaIngest EVE JSON, run SIGMA detection and behavioral threat hunting
rockfish reportGenerate static HTML NDR report
rockfish alertPublish detection events to MQTT, Kafka, or webhooks
rockfish mcpStart MCP server for AI-powered queries
rockfish chatStart AI chat server for NDR data analysis
rockfish httpServe report pages over HTTP with authentication
rockfish autoRun hunt + report automatically at set intervals
rockfish pruneRemove old Parquet files by retention policy
rockfish compactCompact and merge Parquet files for storage efficiency
rockfish updateDownload and install Suricata rule updates
rockfish configShow resolved configuration and features
rockfish statsParse EVE JSON and show event type statistics

Technical Foundation

  • Language: Rust — single static binary, no runtime dependencies
  • Query Engine: Embedded DuckDB for analytical SQL on Parquet
  • Storage Format: Apache Parquet with Zstd compression
  • Protocols: MQTT, Kafka, Webhooks, MCP (Model Context Protocol)
  • Crypto: Ed25519 license verification, TLS 1.3 transport
  • Concurrency: Rayon parallel query execution, Tokio async I/O

Next Steps

Quick Start

Deploy Rockfish NDR in under 30 minutes. This guide covers detection, reporting, and rule management.

Architecture

Rockfish NDR runs as two services:

ServiceCommandWhat it does
Detectionrockfish sigmaSIGMA engine + Parquet ingest + Hunt
Reportingrockfish reportHTML dashboard + HTTP server + AI Insight

Both read from the same Parquet data directory.

1. Install

cd /develop/rockfish/ndr
./scripts/build-cli.sh --install      # rockfish + rockfish-curator → /opt/rockfish/bin/
./scripts/build-rules.sh --install    # rockfish-ruleset → /opt/rockfish/bin/

2. Configure Suricata Rules

sudo -u rockfish rockfish-curator select --count 256 \
    --cache /var/lib/rockfish/et-open-cache \
    --output /var/lib/rockfish/staging

sudo rockfish-ruleset refresh \
    --suricata-socket /var/run/suricata/suricata-command.socket \
    --suricata-binary /opt/suricata/bin/suricata

3. Start Detection

From a File

# Ingest EVE JSON into Parquet
rockfish ingest -i /var/log/suricata/eve.json \
  -o /data/rockfish --sensor my-sensor --hive

Continuous Ingestion

# Follow mode — tails the log like tail -F
rockfish ingest -i /var/log/suricata/eve.json \
  -o /data/rockfish --sensor my-sensor --hive --follow

# From a Unix socket (Suricata unix_stream output)
rockfish ingest --socket /var/run/suricata/eve.sock \
  -o /data/rockfish --sensor my-sensor --hive

Verify Output

ls -la /data/rockfish/my-sensor/
# alert/  flow/  dns/  http/  tls/  ...

2. Run Threat Detection

# Hunt across the last 24 hours
rockfish hunt -d /data/rockfish --sensor my-sensor --hive \
  -t "24 hours"

Hunt findings are written to /data/rockfish/my-sensor/hunt/*.parquet.

View Results on Stdout

# Pretty-printed JSON
rockfish hunt -d /data/rockfish --sensor my-sensor --hive \
  --stdout --pretty

# Table format
rockfish hunt -d /data/rockfish --sensor my-sensor --hive \
  --stdout --format table

3. Generate HTML Report

# Generate report for the last 24 hours
rockfish report -d /data/rockfish --sensor my-sensor --hive \
  -t "24 hours" -o /var/www/html/ndr

Open report/index.html in a browser to view the dashboard.

Demo Mode

Generate a report with synthetic data to see all features:

rockfish report --demo -o ./demo-report

4. Publish Alerts

# Publish to MQTT broker
rockfish alert -d /data/rockfish --sensor my-sensor --hive \
  --mqtt-broker mosquitto -t "1 hour"

# Continuous publishing
rockfish alert -d /data/rockfish --sensor my-sensor --hive \
  --mqtt-broker mosquitto --continuous

Subscribe to Alerts

# In another terminal, subscribe to all rockfish topics
mosquitto_sub -t 'rockfish/#' -v

5. Continuous Operation

Run all components together for ongoing monitoring:

# Terminal 1: Continuous ingestion
rockfish ingest --socket /var/run/suricata/eve.sock \
  -o /data/rockfish --sensor prod-01 --hive

# Terminal 2: Hourly threat hunts
rockfish hunt -d /data/rockfish --sensor prod-01 --hive \
  --continuous --interval-minutes 60

# Terminal 3: Report regeneration every 5 minutes
rockfish report -d /data/rockfish --sensor prod-01 --hive \
  --continuous --interval-minutes 5

# Terminal 4: Alert publishing
rockfish alert -d /data/rockfish --sensor prod-01 --hive \
  --mqtt-broker mosquitto --continuous

Using a Configuration File

Create rockfish.yaml to avoid repeating CLI arguments:

sensor:
  name: prod-01

input:
  socket: /var/run/suricata/eve.sock

output:
  dir: /data/rockfish
  hive_partitioning: true
  compression: zstd

s3:
  bucket: rockfish-data
  region: us-east-1

alert:
  mqtt:
    broker: mosquitto
    port: 1883
    topic_prefix: rockfish
rockfish -c rockfish.yaml ingest
rockfish -c rockfish.yaml hunt --continuous
rockfish -c rockfish.yaml report --continuous
rockfish -c rockfish.yaml alert --continuous

Next Steps

Installation

Quick Install

curl -fsSL https://docs.rockfishndr.com/install.sh | bash

The installer auto-detects your platform and installs via the appropriate method:

  • Debian/Ubuntu: APT repository (recommended)
  • Other Linux: Docker or binary
  • macOS: Docker or binary

Options:

# Install specific version
ROCKFISH_VERSION=1.0.0 curl -fsSL https://docs.rockfishndr.com/install.sh | bash

# Force specific installation method
ROCKFISH_METHOD=apt curl -fsSL https://docs.rockfishndr.com/install.sh | bash
ROCKFISH_METHOD=docker curl -fsSL https://docs.rockfishndr.com/install.sh | bash

APT Repository (Debian/Ubuntu)

The recommended installation method for Debian-based systems. Enables automatic updates via apt-get upgrade.

Add Repository

# 1. Download and install the GPG key
curl -fsSL https://repo.rockfishndr.com/apt/rockfish-archive-keyring.gpg | \
  sudo gpg --dearmor -o /usr/share/keyrings/rockfish-archive-keyring.gpg

# 2. Add the repository
echo "deb [signed-by=/usr/share/keyrings/rockfish-archive-keyring.gpg] https://repo.rockfishndr.com/apt stable main" | \
  sudo tee /etc/apt/sources.list.d/rockfish.list

# 3. Update and install
sudo apt-get update
sudo apt-get install rockfishtoolkit

Update

sudo apt-get update
sudo apt-get upgrade rockfishtoolkit

System Requirements

  • Operating System: Debian 11+, Ubuntu 20.04+, or Docker-compatible host
  • Architecture: x86_64 (amd64), ARM64 (arm64)
  • Memory: 2GB minimum (4GB+ recommended for high-traffic networks)
  • Storage: Depends on retention policy (10GB minimum)

Installation Directory Structure

After installation, Rockfish NDR is installed to /opt/rockfish:

/opt/rockfish/
├── bin/        # Compiled binaries
├── etc/        # Configuration files
├── lib/        # DuckDB extensions and libraries
└── example/    # Example systemd services and configs

System Directories

PathDescription
/opt/rockfish/bin/Rockfish binaries
/opt/rockfish/etc/Configuration directory
/opt/rockfish/example/Example configs and systemd services
/var/lib/rockfish/Data directory
/var/log/rockfish/Log directory
/var/run/rockfish/Runtime directory

Configuration

Configuration File

# Copy or create configuration
sudo cp /opt/rockfish/example/rockfish.yaml.example /opt/rockfish/etc/rockfish.yaml

Rockfish searches for configuration in this order:

  1. --config <path> (CLI argument)
  2. ./rockfish.yaml
  3. /etc/rockfish/rockfish.yaml
  4. ~/.config/rockfish/rockfish.yaml

Environment File

Credentials and secrets are stored in an environment file:

# Create environment file
cat > /opt/rockfish/etc/rockfish.env << 'EOF'
ROCKFISH_S3_BUCKET=rockfish-data
ROCKFISH_S3_REGION=us-east-1
AWS_ACCESS_KEY_ID=...
AWS_SECRET_ACCESS_KEY=...
ABUSEIPDB_API_KEY=...
EOF

Systemd Services

The package installs systemd service files to /lib/systemd/system/. To enable and start a service:

# Reload systemd to pick up new service files
sudo systemctl daemon-reload

# Enable service to start on boot
sudo systemctl enable rockfish

# Start the service
sudo systemctl start rockfish

# Check status
sudo systemctl status rockfish

# View logs
sudo journalctl -u rockfish -f

Docker Installation

Pull the Rockfish NDR image from Docker Hub:

docker pull rockfishnetworks/toolkit:latest

The toolkit image includes the Rockfish binary with all features enabled.

Running Rockfish (Ingest Mode)

docker run -d \
  --name rockfish \
  -v /opt/rockfish/etc:/opt/rockfish/etc:ro \
  -v /data/rockfish:/data/rockfish \
  -p 3000:3000 \
  -p 8082:8082 \
  rockfishnetworks/toolkit:latest \
  rockfish ingest --socket /var/run/suricata/eve.sock
PortService
3000MCP server
8082Chat server

Docker Compose

Example docker-compose.yml:

version: '3.8'

services:
  rockfish:
    image: rockfishnetworks/toolkit:latest
    ports:
      - "3000:3000"
      - "8082:8082"
    volumes:
      - ./config:/opt/rockfish/etc:ro
      - ./data:/data/rockfish
    command: ["rockfish", "ingest", "--socket", "/var/run/suricata/eve.sock"]
    restart: unless-stopped

Verify Installation

# Check version
rockfish --version

# Show configuration and features
rockfish config

Uninstalling

APT Package

# Remove package (keeps configuration)
sudo apt-get remove rockfishtoolkit

# Remove package and configuration
sudo apt-get purge rockfishtoolkit

# Remove repository
sudo rm /etc/apt/sources.list.d/rockfish.list
sudo rm /usr/share/keyrings/rockfish-archive-keyring.gpg

Docker

docker stop rockfish
docker rm rockfish
docker rmi rockfishnetworks/toolkit:latest

Next Steps

Configuration

Rockfish NDR uses YAML-based configuration with CLI overrides and environment-file credential management.

Configuration Search Paths

Rockfish searches for configuration in this order:

  1. --config <path> (CLI argument)
  2. ./rockfish.yaml
  3. /etc/rockfish/rockfish.yaml
  4. ~/.config/rockfish/rockfish.yaml

Full Configuration Reference

# ============================================================
# Sensor
# ============================================================
sensor:
  name: prod-sensor-01        # Sensor name (default: hostname)

# ============================================================
# Input — EVE JSON source
# ============================================================
input:
  file: /var/log/suricata/eve.json   # Path to EVE JSON file
  socket: /var/run/suricata/eve.sock # Or: Unix socket path
  socket_type: stream                # stream (default) or dgram
  follow: true                       # Tail file like tail -F

# ============================================================
# Output — Parquet destination
# ============================================================
output:
  dir: /data/rockfish              # Output directory
  hive_partitioning: true          # year=YYYY/month=MM/day=DD/
  compression: zstd                # none, snappy, zstd
  flush_interval: 60               # Seconds between flushes
  memory_threshold: 1073741824     # 1 GB memory flush threshold
  partition: true                  # Partition by event type

# ============================================================
# Event Filtering
# ============================================================
events:
  include:                         # Only process these types
    - alert
    - flow
    - dns
    - http
    - tls
  exclude:                         # Skip these types
    - stats

# ============================================================
# S3 Upload
# ============================================================
s3:
  bucket: rockfish-data
  region: us-east-1
  prefix: ""                       # Optional key prefix
  delete_after_upload: false       # Delete local files after upload

# ============================================================
# Report
# ============================================================
report:
  output_dir: ./report
  time_window: "24 hours"
  theme: /etc/rockfish/theme.yaml  # Optional theme file
  custom_css: ""                   # Optional custom CSS path

# ============================================================
# Hunt
# ============================================================
hunt:
  time_window: "24 hours"
  detections: "beaconing,lateral,fanout,portscan,community"
  min_severity: medium
  scoring_method: hbos             # hbos or iforest
  internal_networks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"

# ============================================================
# Alert — MQTT and Kafka publishing
# ============================================================
alert:
  mqtt:
    broker: localhost
    port: 1883
    client_id: rockfish-alert
    qos: 1
    topic_prefix: rockfish
    username: ""
    password: ""
    tls_enabled: false
  kafka:                           # Optional — requires kafka feature
    brokers: "localhost:9092"
    topic_prefix: rockfish
    client_id: rockfish-alert
    security_protocol: plaintext
    compression: none
  confidence_threshold: 0.75
  poll_interval_secs: 30
  heartbeat_interval_secs: 60
  dedup_window_secs: 300
  enabled_types:
    - signature
    - lateral_movement
    - c2_beacon
    - exfiltration
    - anomaly

# ============================================================
# Enrichment
# ============================================================
enrichment:
  geoip:
    database_path: /usr/share/GeoIP/GeoLite2-City.mmdb
    asn_database_path: /usr/share/GeoIP/GeoLite2-ASN.mmdb
  ip_reputation:
    enabled: true
    api_key: ${ABUSEIPDB_API_KEY}
    cache_path: /var/lib/rockfish/ip_cache.parquet
    cache_ttl_hours: 48
    memory_cache_size: 50000
    lookup_timeout_ms: 200

# ============================================================
# Data Retention
# ============================================================
retention: 30d                     # 30 days (supports: 7d, 24h, etc.)

# ============================================================
# HTTP Server
# ============================================================
http:
  dir: /var/lib/report           # Directory to serve
  host: 127.0.0.1                # Bind address
  port: 8001                     # Bind port
  users_file: /opt/rockfish/etc/users  # Password file path
  session_expiry_hours: 24       # Session cookie lifetime
  auth: true                     # Enable authentication (false to disable)

# ============================================================
# License
# ============================================================
license: /etc/rockfish/license.json

Environment File

Credentials and secrets should be stored in an environment file rather than the YAML config:

# /opt/rockfish/etc/rockfish.env
ROCKFISH_S3_BUCKET=rockfish-data
ROCKFISH_S3_REGION=us-east-1
AWS_ACCESS_KEY_ID=AKIAEXAMPLE
AWS_SECRET_ACCESS_KEY=secretkey
ABUSEIPDB_API_KEY=your-api-key
MQTT_PASSWORD=broker-password
KAFKA_PASSWORD=kafka-password

The environment file path defaults to /opt/rockfish/etc/rockfish.env and can be overridden with --env-file.

CLI Overrides

CLI arguments override YAML configuration values:

# Override sensor name and data directory
rockfish -c rockfish.yaml ingest --sensor custom-name -o /tmp/data

# Override MQTT broker for alert command
rockfish -c rockfish.yaml alert --mqtt-broker custom-host

Environment Variable Overrides

Alert command options can also be set via environment variables:

VariableDescription
MQTT_BROKERMQTT broker hostname
MQTT_PORTMQTT broker port
MQTT_USERNAMEMQTT authentication username
MQTT_PASSWORDMQTT authentication password
MQTT_CLIENT_IDMQTT client identifier
MQTT_TOPIC_PREFIXMQTT topic prefix
KAFKA_ENABLEDEnable Kafka transport
KAFKA_BROKERSKafka broker addresses
KAFKA_USERNAMEKafka SASL username
KAFKA_PASSWORDKafka SASL password
CONFIDENCE_THRESHOLDMinimum alert confidence

Licensing

Rockfish NDR uses Ed25519-signed license files for offline-verifiable feature gating.

Tiers

TierEvents/minGeoIPIP RepHuntMCPSIGMA
Basic25,000Yes
Professional100,000YesYesYes
EnterpriseUnlimitedYesYesYesYesYes

45-Day Enterprise Trial

Every license includes 45 days of Enterprise features from the issue date. After 45 days, the license settles to its purchased tier. The NDR engine re-checks the license once per day.

Purchasing

Licenses are purchased through the Rockfish Portal. Each license is valid for one Suricata instance. Multiple sensors require multiple licenses.

Installation

scp rockfish-license.json root@sensor:/opt/rockfish/etc/rockfish_license.json
rockfish sigma --license /opt/rockfish/etc/rockfish_license.json

No License

Running without a license defaults to Basic tier (25K events/min, GeoIP, Parquet to S3 export, reports).

Expiry Reminders

Email reminders are sent at 30, 7, 1, and 0 days before expiry. After expiry, the engine falls back to Basic tier. Licenses are perpetual on a per site basis with 12 months of software maintenance included.

License Tiers

TierEvents/minPrice
Basic25,000Free
Professional100,000$99
EnterpriseUnlimited$999

Basic (Free)

Available without a license file:

  • Ingest + Parquet to S3 export
  • GeoIP enrichment
  • HTML reports
  • Full documentation

Professional ($99)

  • Everything in Basic
  • IP reputation scoring
  • Basic hunt algorithms
  • MCP server integration
  • Priority email support

Enterprise ($999)

  • Everything in Professional
  • ML anomaly detection
  • Threat Intelligence support
  • Workflow integration support
  • Dedicated support

Deployment

  • Runs on your VPC or on-premise
  • No telemetry or phone home
  • Fully air-gap capable
  • Ed25519-signed licenses with provenance metadata included in every Parquet file

License File

Licenses are JSON files with an Ed25519 signature:

{
  "id": "rockfish_acme-corp-enterprise_Abc123",
  "tier": "enterprise",
  "customer_name": "Acme Corp",
  "customer_email": "[email protected]",
  "max_events_per_min": null,
  "issued_at": "2026-01-01T00:00:00Z",
  "expires_at": "2027-01-01T00:00:00Z",
  "signature": "base64-encoded-ed25519-signature"
}

Configuration

Specify the license file on the command line or in YAML config:

# CLI argument
rockfish --license /etc/rockfish/license.json sigma

# Or in rockfish.yaml
license: /etc/rockfish/license.json

Verify License

# Show license information with rockfish config
rockfish --license /etc/rockfish/license.json config

Next Steps

rockfish sigma

The primary detection service — runs the SIGMA detection engine with integrated Parquet ingest and behavioral threat hunting.

Overview

rockfish sigma is the main detection command that replaces the previous rockfish run and rockfish ingest commands. It combines:

  • SIGMA detection engine — tokenizes EVE events, builds HBOS baselines, scores anomalies
  • Parquet ingest — writes EVE events to Hive-partitioned Parquet files
  • Hunt — periodic behavioral threat detection (beaconing, lateral movement, C2, etc.)

Usage

rockfish sigma \
    --socket /var/run/rockfish/rockfish.sock \
    --output-dir /var/lib/rockfish/detections \
    --parquet-dir /var/lib/rockfish/parquet \
    --hunt --hunt-interval 60 \
    --license /opt/rockfish/etc/rockfish_license.json

Options

FlagDefaultDescription
--socket <path>Unix socket for EVE input (Suricata connects to this)
--eve-file <path>EVE JSON file input (alternative to socket)
--followfalseTail mode for file input
--output-dir <path>/var/lib/rockfish/detectionsDetection JSONL output directory
--parquet-dir <path>Parquet output directory (enables EVE-to-Parquet ingest)
--huntfalseEnable periodic behavioral threat detection
--hunt-interval <min>60Minutes between hunt runs
--window-minutes <min>15SIGMA window duration
--baseline-min-days <days>7Days before HBOS baseline activates
--baseline-min-samples <n>256Minimum samples before baseline activates
--surprisal-threshold <bits>1.5Default surprisal threshold
--status-interval <sec>60Status line interval (0 to disable)
--alert-webhook <url>POST elevated detections to this URL
--flush-interval <sec>60Parquet flush interval
--compression <codec>zstdParquet compression (none, snappy, zstd)
--license <path>License JSON file
--sensor <name>hostnameSensor name for partitioning

Systemd Service

[Unit]
Description=Rockfish NDR — SIGMA Detection Engine
After=network.target
Before=suricata.service

[Service]
Type=simple
User=rockfish
Group=rockfish
ExecStart=/opt/rockfish/bin/rockfish sigma \
    --socket /var/run/rockfish/rockfish.sock \
    --output-dir /var/lib/rockfish/detections \
    --parquet-dir /var/lib/rockfish/parquet \
    --hunt --hunt-interval 60 \
    --license /opt/rockfish/etc/rockfish_license.json
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Socket Mode

Rockfish creates a Unix socket and listens for Suricata to connect. Suricata must be configured to output EVE JSON to this socket. The order matters:

  1. Start rockfish sigma (creates socket, waits for connection)
  2. Start Suricata (connects to the socket)

Detection Output

SIGMA detections are written to both:

  • JSONL{output-dir}/{sensor}/sigma/year=YYYY/month=MM/day=DD/detections.jsonl
  • Parquet{parquet-dir}/{sensor}/sigma/year=YYYY/month=MM/day=DD/{timestamp}.parquet

The Parquet output is used by rockfish report for the Insight (AI assessment) and SIGMA dashboard pages.

Hunt Thread

When --hunt is enabled, a background thread runs DuckDB queries on the Parquet flow data at the specified interval. Detections include:

  • Beaconing (periodic C2 check-ins)
  • Lateral movement (internal-to-internal spread)
  • C2 fanout (single source → many destinations)
  • Port scanning
  • Community detection (clusters of communicating hosts)

45-Day Trial Window

All licenses receive Enterprise features for 45 days from issued_at. After 45 days, the license settles to its purchased tier. The SIGMA engine re-checks the license once per day.

rockfish report

The reporting service — generates HTML dashboards, serves them over HTTP, and manages compact/prune operations.

Overview

rockfish report runs as a separate service from rockfish sigma. It reads Parquet data produced by the SIGMA engine and generates a static HTML dashboard that can be served over HTTP.

It combines:

  • HTML report generation — continuous, regenerates every 10 minutes (configurable)
  • HTTP server — serves reports via --serve
  • Compact — merges small Parquet files hourly
  • Prune — removes old Parquet files daily (90-day retention)
  • Insight — AI-generated security assessment (daily, requires ANTHROPIC_API_KEY)

Usage

rockfish report \
    --data-dir /var/lib/rockfish/parquet \
    --sensor fmadio20p3-798-ubuntu22 \
    --hive \
    --output-dir /var/lib/rockfish/reports \
    --continuous \
    --interval-minutes 10 \
    --serve --port 8080 \
    --license /opt/rockfish/etc/rockfish_license.json

Systemd Service

[Unit]
Description=Rockfish NDR — Reporting Service
After=network.target rockfish.service

[Service]
Type=simple
User=rockfish
Group=rockfish
ExecStart=/opt/rockfish/bin/rockfish report \
    --data-dir /var/lib/rockfish/parquet \
    --sensor fmadio20p3-798-ubuntu22 \
    --hive \
    --output-dir /var/lib/rockfish/reports \
    --continuous \
    --interval-minutes 10 \
    --serve --port 8080 \
    --license /opt/rockfish/etc/rockfish_license.json
Restart=on-failure

[Install]
WantedBy=multi-user.target

Background Threads

ThreadScheduleDescription
ReportEvery 10 minRegenerates HTML dashboard
HTTPAlways onServes reports on configured port
CompactHourlyMerges small Parquet files
PruneDaily (midnight UTC)Deletes files older than 90 days

Insight (AI Assessment)

When ANTHROPIC_API_KEY is set, the report generates an AI-powered security assessment once per day.

  • ANTHROPIC_API_KEY — required
  • ANTHROPIC_MODEL — default: claude-haiku-4-5-20251001

Overview

The report command produces interactive HTML dashboards with Chart.js and D3.js visualizations — no web server required. Reports include 12+ pages covering alerts, threats, DNS, TLS, flows, hosts, network topology, asset inventory, and hunt findings.

Usage

rockfish report [OPTIONS]

Report Pages

PageHighlights
OverviewTraffic volume, hourly charts, event counts, top talkers, protocol breakdown
AlertsSeverity timeline, top signatures, alerted hosts, MITRE ATT&CK mapping
FindingsHunt detection results by severity and type, evidence table
ThreatsIP reputation, beaconing, large transfers, DGA, DNS tunneling, port scans
DNSTop domains, response codes (NOERROR, NXDOMAIN, SERVFAIL), DGA indicators
TLSVersion distribution, SNI hostnames, JA3 fingerprints, self-signed certs
ApplicationsProtocol distribution, hourly stacked charts, top HTTP hosts
FlowsVolume and direction, destination ports, top countries (GeoIP)
HostsTop alerted hosts, top talkers by flow count and volume
NetworkForce-directed graph with IP/24/16 aggregation, threat and anomaly overlays
InventoryPassive device discovery, device roles, OT protocol summary
QueryConversational AI interface (requires rockfish chat)

Visualization Features

  • World Map — Leaflet.js with country-level flow, alert, and reputation overlays
  • Network Graph — D3.js force-directed topology with Flows/Alerts/Hunt toggle layers, including anomaly (iForest/HBOS) findings overlay
  • Heat-Mapped Tables — Gradient backgrounds for volume, severity, and scores
  • Collapsible Tables — Expand/collapse with JSON export
  • Severity Colors — Consistent palette: critical (red) through info (blue)

Options

OptionDefaultDescription
-d, --data-dir./outputData directory with Parquet files
--sensorsensorSensor name subdirectory
--hiveEnable hive-style partitioning
-o, --output-dir./reportOutput directory for HTML
-t, --time-window24 hoursTime window filter
--themeYAML theme configuration
--custom-cssCustom CSS file path
--continuousRegenerate on schedule
--interval-minutes5Minutes between regenerations

Theming

Customize report appearance with a YAML theme file:

# theme.yaml
background: "#0d1117"
surface: "#161b22"
text: "#e6edf3"
text_heading: "#ffffff"
accent: "#1a73e8"
rockfish report -d /data --sensor my-sensor --theme theme.yaml

See theme.yaml.example for all available options.

Replace the default Rockfish logo with your own branding. Requires Standard or Enterprise license.

# theme.yaml
logo_path: "/path/to/your-logo.png"
PropertyValue
FormatsPNG, JPEG
Recommended size200 x 36 pixels
Display height36px (width scales proportionally)

The logo appears in the header bar of every report page.

Demo Mode

Generate a report with synthetic data to showcase all features:

rockfish report --demo -o ./demo-report

Demo mode is available on all license tiers.

Continuous Mode

# Regenerate every 5 minutes (default)
rockfish report -d /data --sensor my-sensor --hive --continuous

# Regenerate every 15 minutes
rockfish report -d /data --sensor my-sensor --hive \
  --continuous --interval-minutes 15

Examples

# 24-hour report
rockfish report -d /data/rockfish --sensor prod-01 --hive \
  -o /var/www/html/ndr

# 7-day report with custom theme
rockfish report -d /data --sensor prod-01 --hive \
  -t "7 days" --theme /etc/rockfish/theme.yaml

# Continuous regeneration for live dashboard
rockfish report -d /data --sensor prod-01 --hive \
  --continuous --interval-minutes 10 -o /var/www/html/ndr

rockfish hunt

Run graph-based behavioral threat detection on Parquet flow data.

Overview

The hunt engine builds a communication graph from network flow data and applies configurable detection algorithms to identify threats beyond signature matching — C2 beaconing, lateral movement, data exfiltration, and more.

Findings are scored with anomaly detection models (HBOS or Isolation Forest), assigned severity levels, and mapped to MITRE ATT&CK tactics.

Usage

rockfish hunt [OPTIONS]

Detection Types

DetectionDescriptionMITRE Tactic
beaconingC2 callbacks via inter-connection timing regularityCommand and Control
lateralMulti-hop internal attack chains (A -> B -> C)Lateral Movement
fanoutSingle external IP contacted by many internal hostsCommand and Control
portscanHosts probing many unique ports on a targetDiscovery
communityBotnet-like clusters via graph componentsCommand and Control
exfiltrationAsymmetric flows with disproportionate outbound volumeExfiltration
dns_tunnelingDNS queries with long or encoded subdomainsCommand and Control
new_connectionSource-destination pairs absent from 7-day baselineInitial Access
polling_disruptionInterruption of periodic communicationImpact
baseline_deviationVolume or pattern shifts vs. historical normsDiscovery

Select Specific Detections

rockfish hunt -d /data --sensor my-sensor --hive \
  --detections beaconing,lateral,fanout

Output

Parquet (default)

Findings are written to {data-dir}/{sensor}/hunt/*.parquet for ingestion into the report.

Stdout

# JSON output
rockfish hunt -d /data --sensor my-sensor --stdout

# Pretty-printed JSON
rockfish hunt -d /data --sensor my-sensor --stdout --pretty

# Table format
rockfish hunt -d /data --sensor my-sensor --stdout --format table

Severity Filtering

# Only high and critical findings
rockfish hunt -d /data --sensor my-sensor --min-severity high

Tuning

OptionDefaultDescription
--min-beacon-connectionsautoMinimum connections for beacon detection
--max-beacon-cvautoMaximum coefficient of variation
--min-fanout-sourcesautoMinimum internal sources for C2 fanout
--min-portscan-portsautoMinimum unique ports for port scan
--min-community-sizeautoMinimum nodes for community detection
--internal-networksRFC 1918Internal network CIDRs
--scoring-methodhbosAnomaly scoring: hbos or iforest

Anomaly Scoring Algorithms

All detections produce findings that are scored using one of two anomaly detection methods. When a detection has 5 or more candidate groups, statistical scoring is applied; otherwise, fixed severity thresholds are used.

HBOS (Histogram-Based Outlier Scoring)

The default scoring method. HBOS builds equal-width histograms for each feature dimension, then scores each observation based on how rare its bin is.

How it works:

  1. For each feature (e.g., coefficient of variation, connection count, byte ratio), divide the observed range into 10 equal-width bins
  2. Count the number of observations in each bin to compute density: density = count_in_bin / total_observations
  3. Apply a floor to prevent log(0): density = max(density, 0.5 / total)
  4. Compute per-feature score: score = -log10(density) — rarer bins produce higher scores
  5. Sum all feature scores for the final anomaly score

Properties:

  • Assumes feature independence (no cross-feature correlations)
  • O(n) time complexity — fast, single-pass histogram construction
  • Supports feature inversion for metrics where lower values are more suspicious (e.g., beacon CV where 0.01 is more suspicious than 0.5)

Isolation Forest (iForest)

An ensemble method that isolates anomalies using random partitioning trees.

How it works:

  1. Build 100 random isolation trees, each sampling 256 data points
  2. Each tree recursively partitions data by randomly selecting a feature and split value until each point is isolated (or max depth is reached)
  3. For each observation, compute the average path length across all trees — anomalies are isolated in fewer splits
  4. Convert to anomaly score: score = 2^(-avg_path_length / c(n)) where c(n) is the expected path length for a balanced BST
  5. Transform to match HBOS scale: final_score = -log10(1 - raw_score)

Properties:

  • Captures cross-feature interactions (unlike HBOS)
  • More robust to feature scaling
  • Higher computational cost than HBOS
  • Deterministic (seed = 42 for reproducibility)

Select scoring method:

rockfish hunt -d /data --sensor my-sensor --scoring-method iforest

Severity Mapping

Anomaly scores are mapped to severity levels using percentile-based thresholds across the entire finding population:

PercentileSeverity
≥ 95thCritical
≥ 85thHigh
≥ 70thMedium
< 70thLow

If the maximum score across all findings is below 2.0, severity is capped at Medium to suppress false positives in benign environments.

Detection Algorithm Details

Beaconing

Detects C2 callbacks by measuring the regularity of connection intervals.

  1. Group connections by (src_ip, dest_ip, dest_port)
  2. Compute inter-arrival time intervals between consecutive connections
  3. Calculate the coefficient of variation: CV = stddev / mean
  4. A perfect beacon has CV ≈ 0; random traffic has CV ≈ 1.0

Scoring features: CV (inverted), connection count, mean interval, byte consistency (CV of payload sizes)

ThresholdSeverity
CV < 0.05, connections > 50Critical
CV < 0.1High
CV ≤ 0.2 (max threshold)Medium

Lateral Movement

Detects multi-hop attack chains where internal hosts are progressively compromised.

  1. Build a temporal adjacency graph: src → [(dest, timestamp)]
  2. Identify pivot hosts (both source and destination)
  3. For each pivot, look for inbound → outbound sequences within a 1-hour window
  4. Extend chains recursively (up to 10 hops)
Chain LengthSeverity
≥ 5 hopsCritical
≥ 4 hopsHigh
≥ 3 hops (minimum)Medium

C2 Fanout

Detects a single external IP receiving connections from many internal hosts (botnet controller pattern).

Unique SourcesSeverity
≥ 20 internal hostsCritical
≥ 10High
≥ 5 (minimum)Medium

Port Scan

Detects hosts probing many ports on a target.

  1. Count distinct destination ports per (src_ip, dest_ip) pair
  2. Detect sequential port runs (e.g., 80-84) and compute sequential_ratio
  3. Compute scan rate (ports per second)

Scoring features: unique ports, flow count, sequential ratio, scan rate

Unique PortsSeverity
≥ 100Critical
≥ 50High
≥ 25 (minimum)Medium

Community Detection

Identifies botnet-like clusters using Kosaraju’s Strongly Connected Components algorithm.

  1. Build a directed graph from flow data
  2. Find SCCs where every node can reach every other node
  3. Compute density: edges / (n × (n-1))
Community SizeSeverity
≥ 10 hostsCritical
≥ 5High
≥ 3 (minimum)Medium

DNS Tunneling

Detects data exfiltration encoded in DNS subdomain labels.

  1. Pre-filter: average subdomain length must exceed 15 characters
  2. Analyze unique subdomain count, TXT record ratio, and query rate per base domain

Scoring features: unique subdomains, avg label length, TXT ratio, query rate

ConditionSeverity
≥ 500 subdomains AND avg length ≥ 25Critical
≥ 200 subdomains OR TXT ratio ≥ 0.5High
Meets pre-filter thresholdsMedium

Data Exfiltration

Detects internal hosts uploading disproportionate data volumes to external hosts.

  1. Compute byte ratio: bytes_out / (bytes_out + bytes_in) — ratio ≥ 0.8 is suspicious
  2. Filter: minimum 10 MB outbound

Scoring features: total bytes out, byte ratio, flow count

ConditionSeverity
≥ 1 GB AND ratio ≥ 0.95Critical
≥ 100 MBHigh
≥ 10 MB, ratio ≥ 0.8Medium

New Connection Pair

Detects (src_ip, dest_ip, dest_port) tuples never seen in the 7-day baseline window. Particularly important for OT/IoT networks where traffic is highly deterministic.

Known OT ports (Modbus 502, DNP3 20000, MQTT 1883/8883, BACnet 47808, EtherNet/IP 44818, S7comm 102, OPC UA 4840, IEC 104 2404) trigger elevated severity.

ConditionSeverity
OT port, flows ≥ 5Critical
OT port, any flowsHigh
Regular port, flows ≥ 10High
OtherwiseMedium

Polling Disruption

Detects when previously periodic communication becomes irregular or stops entirely. Designed for SCADA/OT environments.

  1. Identify connections periodic in baseline (CV ≤ 0.3)
  2. Detect disruption: either stopped (0 recent flows) or irregular (recent CV > 0.8)
ConditionSeverity
Stopped, baseline > 100 flowsCritical
StoppedHigh
Irregular, CV > 2.0High
IrregularMedium

Baseline Deviation

Detects significant deviations from historical traffic patterns.

  1. Compare recent (1 hour) vs baseline (7 days) for same connection tuples
  2. Compute ratios: flow_ratio = recent / baseline, bytes_ratio = recent / baseline
  3. Flag new protocols not seen in baseline

Scoring features: flow count ratio, bytes ratio, new protocol count

ConditionSeverity
Flow ratio > 10 OR ≥ 3 new protocolsCritical
Flow ratio > 5 OR bytes ratio > 5 OR ≥ 1 new protocolHigh
Ratio > 2.0Medium

Continuous Mode

# Run every hour (default)
rockfish hunt -d /data --sensor my-sensor --hive --continuous

# Run every 15 minutes
rockfish hunt -d /data --sensor my-sensor --hive \
  --continuous --interval-minutes 15

Time Window

rockfish hunt -d /data --sensor my-sensor -t "24 hours"  # default
rockfish hunt -d /data --sensor my-sensor -t "7 days"
rockfish hunt -d /data --sensor my-sensor -t "1 hour"

Examples

# Standard 24-hour threat hunt
rockfish hunt -d /data/rockfish --sensor prod-01 --hive -t "24 hours"

# Continuous with high severity filter
rockfish hunt -d /data --sensor prod-01 --hive \
  --continuous --interval-minutes 30 --min-severity high

# Beaconing with custom thresholds
rockfish hunt -d /data --sensor my-sensor \
  --detections beaconing --min-beacon-connections 50 --max-beacon-cv 0.15

rockfish alert

Publish detection events to MQTT, Kafka, and/or webhooks for automated response.

Overview

The alert command reads Suricata alert and hunt finding Parquet data, normalizes events into a common JSON payload, and publishes them to MQTT, Kafka, and/or webhook endpoints. It supports deduplication, rate limiting, confidence filtering, and continuous polling.

This is the “R” (Response) in NDR — enabling closed-loop automated response via n8n, Node-RED, Fluent Bit, Vector, or any downstream consumer.

Usage

rockfish alert [OPTIONS]

Alert Payload

All alerts are normalized to a common JSON schema:

{
  "alert_id": "RF-2026-00042",
  "timestamp": "2026-02-16T14:32:07Z",
  "detection_type": "signature",
  "confidence": 0.95,
  "source": {
    "ip": "10.0.12.45"
  },
  "destinations": [
    { "ip": "185.220.101.34", "port": 443 }
  ],
  "metadata": {
    "protocol": "TCP",
    "suricata_sid": 2025001,
    "suricata_signature": "ET MALWARE Cobalt Strike Beacon",
    "suricata_category": "A Network Trojan was detected",
    "community_id": "1:abc123"
  },
  "recommended_action": "block_ip"
}

Topic Mapping

MQTT Topics (forward slashes)

SourceTopic
Suricata alertrockfish/alerts/signature
Hunt: beaconingrockfish/alerts/c2_beacon
Hunt: lateral movementrockfish/alerts/lateral_movement
Hunt: exfiltrationrockfish/alerts/exfiltration
Hunt: DNS tunnelingrockfish/alerts/anomaly
Heartbeatrockfish/status/heartbeat

Kafka Topics (dots)

SourceTopic
Suricata alertrockfish.alerts.signature
Hunt: beaconingrockfish.alerts.c2_beacon
Heartbeatrockfish.status.heartbeat

MQTT Options

OptionEnv VarDefault
--mqtt-brokerMQTT_BROKERlocalhost
--mqtt-portMQTT_PORT1883
--mqtt-client-idMQTT_CLIENT_IDrockfish-alert
--mqtt-qosMQTT_QOS1
--mqtt-topic-prefixMQTT_TOPIC_PREFIXrockfish
--mqtt-usernameMQTT_USERNAME
--mqtt-passwordMQTT_PASSWORD
--mqtt-tlsMQTT_TLS_ENABLEDfalse

Kafka Options

Requires building with the kafka feature.

OptionEnv VarDefault
--kafkaKAFKA_ENABLEDfalse
--kafka-brokersKAFKA_BROKERSlocalhost:9092
--kafka-topic-prefixKAFKA_TOPIC_PREFIXrockfish
--kafka-client-idKAFKA_CLIENT_IDrockfish-alert
--kafka-usernameKAFKA_USERNAME
--kafka-passwordKAFKA_PASSWORD
--kafka-security-protocolKAFKA_SECURITY_PROTOCOLplaintext
--kafka-compressionKAFKA_COMPRESSIONnone

Supported security protocols: plaintext, ssl, sasl_plaintext, sasl_ssl

Supported compression: none, gzip, snappy, lz4, zstd

Webhook Options

Requires building with the webhook feature.

OptionEnv VarDefault
--webhookWEBHOOK_ENABLEDfalse
--webhook-urlWEBHOOK_URL
--webhook-secretWEBHOOK_SECRET
--webhook-timeoutWEBHOOK_TIMEOUT10

Multiple webhook URLs can be specified as a comma-separated list. When a secret is configured, requests are signed with HMAC-SHA256 via the X-Rockfish-Signature header. The webhook publisher includes automatic retry with exponential backoff and deduplication.

Confidence Mapping

Suricata severity:

SeverityConfidence
10.95
20.85
30.70
4+0.50

Hunt severity:

SeverityConfidence
critical0.95
high0.85
medium0.70
low0.55

Deduplication

Identical alerts (same source IP, destination IP, detection type, and SID) are suppressed within a configurable time window (default: 5 minutes).

YAML Configuration

alert:
  mqtt:
    broker: mosquitto
    port: 1883
    client_id: rockfish-alert
    qos: 1
    topic_prefix: rockfish
  kafka:
    brokers: "kafka1:9092,kafka2:9092"
    topic_prefix: rockfish
    security_protocol: sasl_ssl
    compression: snappy
  webhook:
    urls:
      - https://hooks.example.com/alert
    secret: my-webhook-secret
    timeout_secs: 10
  confidence_threshold: 0.75
  poll_interval_secs: 30
  dedup_window_secs: 300
  enabled_types:
    - signature
    - lateral_movement
    - c2_beacon

Heartbeat

Periodic heartbeat published to {prefix}/status/heartbeat:

{
  "timestamp": "2026-02-16T14:32:07Z",
  "uptime_secs": 3600,
  "alerts_published": 142,
  "status": "running"
}

Examples

# Single-shot MQTT publish
rockfish alert -d /data --sensor my-sensor --hive \
  --mqtt-broker mosquitto -t "1 hour"

# Continuous MQTT + Kafka publishing
rockfish alert -d /data --sensor my-sensor --hive \
  --mqtt-broker mosquitto \
  --kafka --kafka-brokers kafka1:9092,kafka2:9092 \
  --continuous

# High-confidence only with TLS
rockfish alert -d /data --sensor prod-01 --hive \
  --mqtt-broker mqtt.internal --mqtt-tls \
  --confidence-threshold 0.85 --continuous

# Webhook delivery with HMAC signing
rockfish alert -d /data --sensor prod-01 --hive \
  --webhook --webhook-url https://hooks.example.com/alert \
  --webhook-secret my-secret --continuous

Fluent Bit Integration

Fluent Bit can subscribe to Rockfish alert topics via its MQTT input plugin and forward alerts to any supported output (Elasticsearch, Splunk, S3, HTTP, etc.).

[INPUT]
    Name        mqtt
    Tag         rockfish.alerts
    Listen      0.0.0.0
    Port        1883

[FILTER]
    Name        parser
    Match       rockfish.alerts
    Key_Name    payload
    Parser      json

[OUTPUT]
    Name        es
    Match       rockfish.alerts
    Host        elasticsearch
    Port        9200
    Index       rockfish-alerts
    Type        _doc

Alternatively, use the Fluent Bit MQTT output to republish filtered or enriched alerts to a different broker:

[OUTPUT]
    Name        mqtt
    Match       rockfish.alerts
    Host        mqtt-central.example.com
    Port        1883
    Topic       soc/alerts/rockfish

Note: Fluent Bit’s MQTT input runs an embedded MQTT server. Configure Rockfish to publish to the Fluent Bit listen address instead of a standalone broker, or use a shared MQTT broker (e.g., Mosquitto) with Fluent Bit subscribing as a client via a custom Lua script or the mqtt input in listen mode.

n8n Integration

Subscribe to MQTT topics in n8n for automated response:

  1. Add an MQTT Trigger node subscribing to rockfish/alerts/#
  2. Parse the alert JSON payload
  3. Route by detection_type
  4. Execute response actions (block IP, quarantine host, create ticket)

rockfish mcp

Start an MCP (Model Context Protocol) server for AI-powered queries on Parquet data.

Overview

The MCP server exposes Parquet data to AI assistants and LLM toolchains using the Model Context Protocol. It provides query tools for data exploration and hunt tools for threat detection.

Usage

rockfish mcp [OPTIONS]

Transport Modes

ModeUse Case
stdio (default)Claude Desktop, local tool integration
httpWeb clients, remote access
# stdio mode (default)
rockfish mcp

# HTTP mode
rockfish mcp -t http --host 0.0.0.0 --port 3000

Built-in Tools

Query Tools

ToolDescription
queryQuery with SQL filters and column selection
aggregateGroup and aggregate data
sampleGet random sample rows
countCount rows with optional filter
schemaGet column names and types
list_sourcesList configured data sources

Hunt Tools

ToolDescription
detect_beaconingFind C2 beacon patterns
detect_lateral_movementTrace internal attack chains
detect_c2_fanoutIdentify C2 fan-out patterns
detect_port_scanFind port scanning activity
detect_communitiesDiscover botnet-like clusters
detect_dns_tunnelingFlag DNS tunneling indicators
detect_data_exfiltrationFind data exfiltration patterns

Options

OptionDefaultDescription
-t, --transportstdioTransport mode: stdio or http
--data-dirfrom configOverride data directory
--sensorfrom configOverride sensor name
--no-hiveDisable hive partitioning
--host127.0.0.1HTTP server host
--port3000HTTP server port

Authentication

HTTP mode supports JWT token authentication and OAuth2. See the MCP authentication documentation for configuration details.

Examples

# Start MCP server for Claude Desktop
rockfish mcp --data-dir /data/rockfish --sensor prod-01

# HTTP mode for web clients
rockfish mcp -t http --host 0.0.0.0 --port 3000 \
  --data-dir /data/rockfish --sensor prod-01

rockfish chat

Start an AI-powered chat server for conversational network security analysis.

Overview

The chat server provides a web-based conversational interface for querying network security data using natural language. It integrates with MCP for live data queries and supports pluggable LLM backends.

Usage

rockfish chat [OPTIONS]

LLM Modes

ModeDescription
slm (default)Local small language model via Ollama
cloudCloud LLM (OpenAI, Anthropic)
hybridTry local SLM first, fall back to cloud
# Local SLM mode
rockfish chat --mode slm

# Cloud mode
rockfish chat --mode cloud

# Hybrid mode
rockfish chat --mode hybrid

Data Modes

ModeDescription
cache (default)Query local pre-filtered Parquet files
storeQuery via MCP cold storage

Options

OptionDefaultDescription
-c, --configChat configuration file (chat.yaml)
--host127.0.0.1HTTP server host
--port8082HTTP server port
--modeslmLLM mode: slm, cloud, hybrid
--data-modecacheData mode: cache or store
--mcp-endpointhttp://localhost:3000MCP server endpoint
--slm-endpointhttp://localhost:8081/v1/chat/completionsLocal SLM endpoint
--data-dir./outputData directory for cache mode
--sensorsensorSensor name for cache mode

Features

  • Session management with state persistence
  • Security guardrails and response caching
  • MCP integration for live data queries
  • Natural language to SQL translation

Examples

# Start chat server with local SLM
rockfish chat --mode slm \
  --data-dir /data/rockfish --sensor prod-01

# Start with MCP integration
rockfish chat --mode cloud \
  --mcp-endpoint http://localhost:3000 \
  --host 0.0.0.0 --port 8082

rockfish update

Download and install Suricata rule updates. Functionally equivalent to suricata-update.

Overview

The update command manages Suricata rule sources — downloading rule archives (ET Open by default), applying local filter files (enable, disable, drop, modify), and writing a merged suricata.rules file. Optionally reloads Suricata after a successful update.

Usage

# Download rules, apply filters, write suricata.rules
rockfish update

# Force re-download and reload Suricata
rockfish update --force --reload

# Use ET Pro rules (requires oinkcode)
rockfish update --etpro YOUR_OINKCODE

# Include additional local rules
rockfish update --local /etc/suricata/rules/local.rules,/etc/suricata/rules/custom/

Options

OptionDefaultDescription
--suricata-versionauto-detectSuricata version (e.g. 7.0)
--data-dir/var/lib/suricataCache directory for downloaded archives and source state
--config-dir/etc/suricataDirectory containing filter files
--output/var/lib/suricata/rules/suricata.rulesOutput path for merged rules
--etpro-Proofpoint ET Pro oinkcode
--url-Custom URL for the primary rule source
--local-Additional local .rules files or directories (comma-separated)
--reloadfalseReload Suricata after update (via suricatasc)
--no-reloadfalseExplicitly disable reload
--forcefalseForce re-download even if cache is fresh

Source Management

List available sources

rockfish update list-sources

Fetches the OISF source index and displays all available rule sources with their enabled/disabled status.

Enable a source

rockfish update enable-source et/pro
rockfish update enable-source oisf/trafficid

Disable a source

rockfish update disable-source et/pro

Remove a source

Disables the source and deletes its cached archive:

rockfish update remove-source et/pro

Refresh the source index

rockfish update update-sources

Filter Files

Filter files control which rules are enabled, disabled, converted to drop, or modified. Place them in the config directory (/etc/suricata/ by default).

enable.conf

Force-enable rules matching the specified patterns:

# By SID
2100001

# By SID range
2100001-2100010

# By regex on rule text
re:ET SCAN

# By group (source filename)
group:emerging-scan.rules

disable.conf

Disable rules matching the specified patterns (same format as enable.conf):

# Disable noisy rules
re:ET INFO
2100498
group:emerging-deleted.rules

drop.conf

Change the action to drop for matching rules (same format):

# Drop all exploit rules
re:ET EXPLOIT

modify.conf

Regex find-and-replace on matching rules:

# Change action from alert to drop for specific SID
2100001 "alert" "drop"

# Change action for all SCAN rules
re:ET SCAN "alert" "drop"

YAML Configuration

Settings can also be specified in rockfish.yaml:

update:
  suricata_version: "7.0"
  data_dir: /var/lib/suricata
  config_dir: /etc/suricata
  output: /var/lib/suricata/rules/suricata.rules
  reload: true
  etpro_code: "YOUR_OINKCODE"

CLI arguments override YAML settings.

Pipeline

The update command follows this pipeline:

  1. Resolve Suricata version (auto-detect via suricata -V or --suricata-version)
  2. Download rule archives for each enabled source (ET Open by default)
  3. Extract .rules files from tar.gz archives
  4. Load additional local rule files (if --local is specified)
  5. Parse all rules, extracting SIDs and group metadata
  6. Apply filters in order: enable.conf → disable.conf → drop.conf → modify.conf
  7. Write merged, deduplicated suricata.rules sorted by SID
  8. Reload Suricata (if --reload is set) via suricatasc -c reload-rules

Reload Behavior

When --reload is specified, Rockfish attempts to reload Suricata rules:

  1. First tries suricatasc -c reload-rules
  2. Falls back to sending SIGUSR2 to the Suricata process (found via pidfile or pidof)

Default Rule Source

Without any additional sources enabled, Rockfish downloads the Emerging Threats Open ruleset:

https://rules.emergingthreats.net/open/suricata-{version}/emerging.rules.tar.gz

When an ET Pro oinkcode is provided (--etpro), ET Pro replaces ET Open:

https://rules.emergingthreatspro.com/{code}/suricata-{version}/etpro.rules.tar.gz

Examples

# Basic update with ET Open rules
rockfish update

# ET Pro with reload
rockfish update --etpro abc123 --reload

# Custom paths
rockfish update \
  --data-dir /opt/suricata/data \
  --config-dir /opt/suricata/etc \
  --output /opt/suricata/rules/suricata.rules

# Include local rules alongside downloaded rules
rockfish update --local /etc/suricata/rules/local.rules

# Force fresh download
rockfish update --force

# Automated cron job
rockfish update --reload --quiet

Cron Example

Run rule updates every 6 hours and reload Suricata:

0 */6 * * * /opt/rockfish/bin/rockfish update --reload --quiet 2>&1 | logger -t rockfish-update

rockfish prune

Remove old Parquet files based on a retention policy.

Overview

The prune command enforces data retention by removing Parquet files older than a configurable window. Supports dry-run preview and per-event-type granularity.

Usage

rockfish prune [OPTIONS]

Options

OptionDefaultDescription
-d, --data-dir./outputData directory with Parquet files
--sensorsensorSensor name subdirectory
--hiveEnable hive-style partitioning
-r, --retention30dRetention period
--event-typesallEvent types to prune (comma-separated)
--include-inventoryAlso prune inventory files
--dry-runPreview without deleting

Retention Periods

Supports various time formats:

rockfish prune -r "30d"        # 30 days
rockfish prune -r "7d"         # 7 days
rockfish prune -r "24h"        # 24 hours
rockfish prune -r "30 days"    # 30 days (verbose)

Examples

# Preview what would be deleted (dry run)
rockfish prune -d /data/rockfish --sensor prod-01 --hive \
  -r "30d" --dry-run

# Delete files older than 7 days
rockfish prune -d /data/rockfish --sensor prod-01 --hive -r "7d"

# Prune only alert and flow data
rockfish prune -d /data --sensor prod-01 \
  -r "14d" --event-types alert,flow

# Prune with inventory files
rockfish prune -d /data --sensor prod-01 \
  -r "30d" --include-inventory

rockfish compact

Compact and merge Parquet files for storage efficiency.

Overview

The compact command merges multiple small Parquet files from recent days into larger, optimized files and removes data older than the configured retention period. This reduces file count, improves query performance, and manages disk usage.

Usage

rockfish compact [OPTIONS]

Options

OptionDescriptionDefault
-d, --data-dirParquet data directoryrequired
--sensorSensor name for partitioning
--hiveUse hive-style date partitioningfalse
--retentionData retention period30d
--dry-runPreview changes without modifying filesfalse

Examples

# Compact and prune with 30-day retention
rockfish compact -d /data --sensor prod-01 --hive --retention 30d

# Preview what would be compacted
rockfish compact -d /data --sensor prod-01 --hive --dry-run

# 90-day retention
rockfish compact -d /data --sensor prod-01 --hive --retention 90d

How It Works

  1. Scans partitioned Parquet directories for small files
  2. Merges files from the same partition into larger, optimized files
  3. Removes partitions older than the retention period
  4. Preserves all data and metadata during compaction

rockfish config

Show resolved configuration, enabled features, and system information.

Overview

The config command displays the current configuration including YAML settings, environment file values (with secrets masked), enabled compile-time features, and license information.

Usage

rockfish config [OPTIONS]

Output

The command displays:

  • Config file — resolved path to the active YAML configuration
  • Environment file — loaded environment variables (secrets masked as XXX...last8)
  • Enabled features — compile-time feature flags
  • License information — tier, customer, expiration (if a license file is provided)

Examples

# Show all configuration
rockfish config

# With a specific config file
rockfish -c /etc/rockfish/rockfish.yaml config

# With license info
rockfish --license /etc/rockfish/license.json config

Feature Flags

The config output includes which features were compiled in:

Compile-time Features
---------------------
  s3           enabled
  mcp          enabled
  hunt         enabled
  report       enabled
  chat         enabled
  alert        enabled
  kafka        disabled
  geoip        enabled
  ip_reputation enabled

Enrichment

Rockfish NDR enriches flow data with geographic and reputation intelligence during ingestion.

GeoIP

Geographic location lookups via MaxMind GeoLite2 or GeoIP2 databases.

Enriched Fields

FieldDescription
dest_countryDestination country (ISO 3166)
dest_cityDestination city name
dest_as_orgDestination ASN organization
dest_asnDestination ASN number
dest_latitudeDestination latitude
dest_longitudeDestination longitude

Configuration

enrichment:
  geoip:
    database_path: /usr/share/GeoIP/GeoLite2-City.mmdb
    asn_database_path: /usr/share/GeoIP/GeoLite2-ASN.mmdb

Requirements

  • MaxMind GeoLite2-City and GeoLite2-ASN databases
  • Free account at maxmind.com
  • Requires the geoip feature (enabled by default)
  • Requires Standard or Enterprise license tier

IP Reputation

Abuse confidence scoring via the AbuseIPDB API.

Enriched Fields

FieldDescription
drepDestination abuse confidence score (0-100)
drep_reportsNumber of abuse reports
drep_ispISP/hosting provider
drep_domainDomain associated with the IP

Configuration

enrichment:
  ip_reputation:
    enabled: true
    api_key: ${ABUSEIPDB_API_KEY}
    cache_path: /var/lib/rockfish/ip_cache.parquet
    cache_ttl_hours: 48
    memory_cache_size: 50000
    lookup_timeout_ms: 200
    fail_open: false

Caching

IP reputation lookups are cached at two levels:

  1. Memory cache — LRU cache (default: 50,000 entries) for fast lookups
  2. Parquet cache — Persistent disk cache with configurable TTL

Requirements

  • AbuseIPDB API key (set in environment file)
  • Requires the ip_reputation feature (enabled by default)
  • Requires Standard or Enterprise license tier

Report Integration

Both GeoIP and IP reputation data appear across multiple report pages:

  • Overview — Top countries by flow volume
  • Flows — Country breakdown with GeoIP data
  • Threats — IP reputation scores and flagged hosts
  • Network — Node detail panel with geographic info
  • World Map — Leaflet.js globe with country-level overlays

Note: GeoIP and IP reputation columns are only populated when the probe runs with those features enabled. Report queries gracefully return zero rows when enrichment data is absent.

Asset Inventory

Passive device discovery from observed network traffic.

Overview

Rockfish builds an asset inventory by analyzing network flow patterns, extracting DHCP metadata, and inferring device roles — all without agents or active scanning.

Capabilities

FeatureDescription
IP TrackingAll observed IPs with communication patterns and protocol usage
DHCP MetadataMAC address, hostname, vendor class ID extraction
Device Role InferenceAutomatic classification based on traffic patterns
New Device DetectionFlags IPs not present in baseline
OT Protocol AwarenessIdentifies industrial protocol usage
Inventory SnapshotsPeriodic snapshots written to Parquet

Inferred Device Roles

RoleDetection Criteria
PLCModbus, DNP3, EtherNet/IP, or S7comm traffic
HMIMixed OT and standard protocols
SensorRead-only OT protocol patterns
Engineering WorkstationOT + administrative protocols
ServerListening on well-known ports
ClientOutbound-initiated connections

OT Protocol Support

ProtocolDescription
ModbusIndustrial serial communication
DNP3Distributed Network Protocol
MQTTIoT message queuing
BACnetBuilding automation
EtherNet/IPIndustrial Ethernet
S7commSiemens S7 communication
OPC UAOpen Platform Communications
IEC 104Telecontrol protocols

Report Integration

The Inventory report page displays:

  • Device list with inferred roles and protocol usage
  • New/unknown device alerts
  • OT protocol traffic summary
  • First-seen and last-seen timestamps
  • Communication pattern metrics (connection count, bytes)

Deployment Profiles

Rockfish NDR supports different deployment profiles optimized for specific environments.

IT Profile

Enterprise network monitoring with full enrichment capabilities.

ComponentConfiguration
GeoIPEnabled — MaxMind GeoLite2 databases
IP ReputationEnabled — AbuseIPDB API integration
S3 UploadEnabled — cloud storage for retention
ChatCloud LLM (OpenAI, Anthropic)
HuntFull detection suite
AlertMQTT/Kafka for SIEM integration
# IT deployment example
enrichment:
  geoip:
    database_path: /usr/share/GeoIP/GeoLite2-City.mmdb
  ip_reputation:
    enabled: true
    api_key: ${ABUSEIPDB_API_KEY}
s3:
  bucket: security-data
  region: us-east-1
alert:
  mqtt:
    broker: siem-mqtt.internal

OT Profile

Industrial / IoT environments with asset inventory and baseline monitoring.

ComponentConfiguration
GeoIPOptional
IP ReputationOptional
S3 UploadOptional — local storage preferred
ChatLocal SLM (air-gap compatible)
HuntBaseline deviation, polling disruption
InventoryOT protocol awareness enabled
# OT deployment example
hunt:
  detections: "baseline_deviation,polling_disruption,beaconing,lateral"
  internal_networks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"

Key OT detections:

  • Baseline deviation — traffic pattern shifts in control networks
  • Polling disruption — interruption of periodic SCADA polling
  • New connection pairs — unexpected host communication

Military Profile

Air-gapped networks with no internet dependencies.

ComponentConfiguration
GeoIPOffline databases only
IP ReputationDisabled — no API access
S3 UploadDisabled — local storage only
ChatLocal SLM only (Ollama)
HuntData exfiltration focus
AlertLocal MQTT broker only
# Air-gapped deployment example
enrichment:
  geoip:
    database_path: /opt/rockfish/geoip/GeoLite2-City.mmdb
  ip_reputation:
    enabled: false
hunt:
  detections: "exfiltration,beaconing,lateral,fanout,dns_tunneling"
alert:
  mqtt:
    broker: localhost

Key considerations:

  • All enrichment data must be pre-loaded locally
  • SLM (small language model) runs entirely on-device
  • No S3, no cloud APIs, no external network access
  • Focus on data exfiltration and unauthorized communication detection

Docker Deployment

Rockfish NDR provides Docker images for containerized deployment.

Production Container

# Build
docker build -t rockfish:latest -f Dockerfile .

# Run with mounted config and data
docker run -d --name rockfish \
  -v /opt/rockfish/etc:/opt/rockfish/etc:ro \
  -v /data/rockfish:/data/rockfish \
  -p 3000:3000 -p 8082:8082 \
  rockfish:latest ingest --socket /var/run/suricata/eve.sock

The production image is a multi-stage build (Rust builder, Debian slim runtime) and includes all default features plus Kafka, with DuckDB bundled from source.

Demo Container

# Build and run demo report on port 8080
docker build -t rockfish-demo:latest -f Dockerfile.demo .
docker run -d --name rockfish-demo -p 8080:8080 rockfish-demo:latest

The demo image generates a synthetic report at build time and serves it via nginx.

License Tiers

Detailed feature matrix for Rockfish NDR license tiers.

Tier Comparison

FeatureBasic (Free)Professional ($99)Enterprise ($999)
Event Rate25,000/min100,000/minUnlimited
Ingest + Parquet to S3 exportYesYesYes
GeoIP enrichmentYesYesYes
HTML reportsYesYesYes
S3 cloud exportYesYesYes
Full documentationYesYesYes
IP reputation scoringYesYes
Basic hunt algorithmsYesYes
MCP server integrationYesYes
Priority email supportYesYes
ML anomaly detectionYes
Threat Intelligence supportYes
Workflow integration supportYes
Dedicated supportYes

Basic (Free)

Available without a license file. Provides core NDR functionality:

  • Ingest + Parquet to S3 export
  • GeoIP enrichment
  • HTML reports
  • Full documentation

Limit: 25,000 events per minute.

Professional ($99)

Adds detection intelligence and AI-powered analysis:

  • Everything in Basic
  • IP reputation scoring
  • Basic hunt algorithms
  • MCP server integration
  • Priority email support

Limit: 100,000 events per minute.

Enterprise ($999)

Full NDR capability:

  • Everything in Professional
  • ML anomaly detection
  • Threat Intelligence support
  • Workflow integration support
  • Dedicated support

No event rate limit.

License File Format

{
  "id": "rockfish_acme-corp-enterprise_Abc123",
  "tier": "enterprise",
  "customer_name": "Acme Corp",
  "customer_email": "[email protected]",
  "max_events_per_min": null,
  "issued_at": "2026-01-01T00:00:00Z",
  "expires_at": "2027-01-01T00:00:00Z",
  "signature": "base64-encoded-ed25519-signature"
}

Licenses are verified using Ed25519 digital signatures with a public key embedded in the binary. License provenance metadata is included in every Parquet file.

Deployment Model

  • Perpetual licenses on a per site basis with 12 months of software maintenance
  • Runs on your VPC or on-premise
  • No telemetry or phone home
  • Fully air-gap capable

Portal Overview

The Rockfish Portal is a self-service web application that combines the marketing website with license management. It provides:

  • Marketing pages — product information, features, pricing
  • Shop — dynamic pricing from Stripe with tier comparison
  • Registration — passwordless email-based authentication (magic links)
  • License management — purchase, download, and manage licenses
  • Stripe integration — payment processing with webhook support
  • License server integration — delegates license signing to the license server

Architecture

User → Portal (Axum) → Stripe (payments)
                     → License Server (signing)
                     → S3 (data persistence)
                     → SMTP (email)

The portal is a single Rust binary (rockfish-portal) that serves both the static marketing site and the dynamic commerce functionality.

URL Structure

PathDescriptionAuth
/Marketing landing pagePublic
/features.htmlFeature overviewPublic
/shopDynamic pricing (from Stripe)Public
/enterEmail entry / loginPublic
/auth?token=...Magic link authenticationPublic
/dashboardLicense listLogged in
/dashboard/buyPurchase a licenseLogged in
/checkoutStripe checkout redirectLogged in
/webhook/stripeStripe payment webhookStripe
/termsTerms & ConditionsPublic
/privacyPrivacy PolicyPublic

Authentication

The portal uses passwordless magic link authentication:

  1. User enters email at /enter
  2. If email is new → account created automatically
  3. Magic link sent via SMTP
  4. User clicks link → session cookie set
  5. First-time users complete profile (name, company, accept terms)
  6. New users → redirected to Buy tab
  7. Returning users → redirected to Licenses tab

License Flow

  1. User selects a tier on the Buy tab
  2. Enters an Installation Name (min 5 characters, identifies the Suricata instance)
  3. Redirected to Stripe Checkout
  4. On payment confirmation:
    • Stripe sends webhook to /webhook/stripe
    • Portal asynchronously requests license from the license server
    • License stored in DuckDB and synced to S3
    • User can download or copy the license JSON from their dashboard

45-Day Enterprise Trial

Every license includes 45 days of Enterprise features from the issue date:

  • The NDR engine checks issued_at in the license
  • If within 45 days → grants Enterprise features regardless of tier
  • After 45 days → settles to purchased tier
  • Re-checked once per day

Tiers

TierPriceEvents/minFeatures
Basic$0.99/yr25,000GeoIP, Parquet to S3 export, Reports
Professional$99/yr100,000+ IP Reputation, MCP
Enterprise$999/yrUnlimited+ SIGMA, Hunt, ML, Anomaly

Prices are fetched dynamically from Stripe and cached for 8 hours.

Portal Control

  • PORTAL_DISABLED=true — shows “Coming Soon” page, allows pre-registration but disables purchasing
  • When disabled, users can still log in and view existing licenses

Data Persistence

The portal uses DuckDB locally and syncs to S3 after every write:

  • New user registration
  • Profile completion
  • License issuance

On startup, data is loaded from S3 into local DuckDB. This supports ephemeral environments like DigitalOcean App Platform.

CLI Commands

# Run the portal server
rockfish-portal

# List registered users
rockfish-portal --list-users

# List licenses
rockfish-portal --list-licenses

# Verbose mode
rockfish-portal -v

Portal Deployment

Building

cd /development/rockfish/ndr

# Build Docker image
./scripts/build-portal.sh

# Push to DigitalOcean Container Registry
./scripts/build-portal.sh --push

# Build local binary
./scripts/build-portal.sh --install

Environment Variables

All configuration is via environment variables (.env file or platform settings).

Required

VariableDescription
PORTAL_DOMAINPublic URL (e.g., https://portal.rockfishndr.com)
STRIPE_SECRET_KEYStripe API secret key (sk_test_... or sk_live_...)
STRIPE_WEBHOOK_SECRETStripe webhook signing secret (whsec_...)
STRIPE_PRICE_BASICStripe Product or Price ID for Basic tier
STRIPE_PRICE_PROFESSIONALStripe Product or Price ID for Professional tier
STRIPE_PRICE_ENTERPRISEStripe Product or Price ID for Enterprise tier
LICENSE_SERVER_TOKENBearer token for the license server

Optional

VariableDefaultDescription
BIND_ADDR0.0.0.0:8080Server bind address
DATABASE_PATH/var/lib/rockfish/portal.dbDuckDB file path
LICENSE_SERVER_HOSThttp://127.0.0.1:8080License server URL
SMTP_HOSTSMTP server (emails logged if unset)
SMTP_PORT587SMTP port (587=STARTTLS, 465=TLS, 25/1025=plain)
SMTP_USERSMTP username
SMTP_PASSWORDSMTP password
SMTP_FROM[email protected]From address
S3_BUCKETS3 bucket for data persistence
S3_REGIONS3 region
S3_ENDPOINTS3 endpoint (for DigitalOcean Spaces, MinIO)
S3_ACCESS_KEY_IDS3 access key
S3_SECRET_ACCESS_KEYS3 secret key
S3_PORTAL_PREFIXportalS3 key prefix
S3_FORCE_PATH_STYLEfalseUse path-style S3 URLs
MAGIC_LINK_TTL_MINUTES15Magic link expiry
PORTAL_DISABLEDfalseShow “Coming Soon” page, disable purchasing

DigitalOcean App Platform

1. Push Docker image

./scripts/build-portal.sh
./scripts/build-portal.sh --push

2. Create App

DigitalOcean Dashboard → Apps → Create App:

  • Source: Container Registry → rockfishnetworks/rockfish-portal:latest
  • HTTP Port: 8080
  • Instance size: Basic ($5/mo)

3. Set environment variables

Add all required variables in the App Settings → Environment Variables.

4. Custom domain

App Settings → Domains → Add portal.rockfishndr.com

DNS: Add CNAME record pointing to your-app.ondigitalocean.app

5. Stripe webhook

Stripe Dashboard → Developers → Webhooks → Add:

  • URL: https://portal.rockfishndr.com/webhook/stripe
  • Events: checkout.session.completed

Docker (self-hosted)

docker run -d \
    --name rockfish-portal \
    --env-file .env \
    -e BIND_ADDR=0.0.0.0:8080 \
    -p 8080:8080 \
    rockfish-portal

Startup Connectivity Checks

On startup, the portal tests:

  1. License serverGET {LICENSE_SERVER_HOST}/api/v1/health
  2. S3 — attempts to list objects under the configured prefix

Results are logged to help diagnose configuration issues.

S3 Data Layout

s3://{bucket}/{prefix}/
├── users.parquet
├── licenses.parquet
├── pending_licenses.parquet
├── magic_links.parquet
└── reminders.parquet

Retry Logic

If the license server is unavailable when a payment is confirmed:

  1. License request is queued in pending_licenses table
  2. Background task retries every 60 seconds
  3. Up to 100 retries (~100 minutes)
  4. On success, license is moved to licenses table and synced to S3