ReferenceClustering

Clustering

Cluster detection parameters and tuning guide

Cluster detection is the core algorithm that identifies groups of high-volume IPs and aggregates them into CIDR ranges.

Parameter Format

CLI:

--clusterArgSets minSize,minDepth,maxDepth,threshold

TOML:

clusterArgSets = [[minSize, minDepth, maxDepth, threshold]]

The Four Parameters

ParameterTypeDescriptionTypical Range
minSizeintegerMinimum requests to flag a cluster50-10000
minDepthintegerSmallest CIDR prefix (widest range)12-24
maxDepthintegerLargest CIDR prefix (narrowest range)24-32
thresholdfloatSubtree balance threshold (0.0-1.0)0.05-0.3

minSize - Minimum Cluster Size

Minimum number of requests required for a cluster to be flagged.

Traffic VolumeRecommendedReasoning
< 10k req/day50-100Catch small clusters
10k-100k req/day500-1000Balance signal/noise
100k-1M req/day1000-5000Focus on significant clusters
> 1M req/day5000+Only major clusters

minDepth / maxDepth - CIDR Depth Range

Controls which CIDR prefix lengths the algorithm considers.

CIDR Size Reference

PrefixIP CountUse Case
/121,048,576Entire ISPs, large networks
/1665,536Large organizations, ASNs
/204,096Medium networks
/24256Small networks, typical subnets
/2816Small clusters
/304Tiny clusters
/321Single IP

minDepth Tuning

Traffic PatternRecommendedReasoning
Large networks12-16Catch network-wide patterns
Distributed clusters20-24Balance coverage
Focused clusters28-30Target specific subnets
Single-host32Individual IPs only

maxDepth Tuning

GoalRecommendedReasoning
Block networks only24Avoid blocking individuals
Include small clusters28-30Catch coordinated groups
Include single IPs32Maximum granularity

threshold - Subnet Balance Threshold

Controls where in the trie a cluster boundary is drawn (0.0-1.0).

A trie node between minDepth and maxDepth is reported as a cluster only when both of its children carry traffic and the load is balanced between them. With child request counts a and b, the node qualifies when:

2 * |a - b| < threshold * (a + b)

Lower values demand near-perfect balance and yield fewer, denser clusters; higher values tolerate lopsided subtrees and yield broader detections. A node with only one populated child is never reported at that depth - the search descends into the populated side instead. At maxDepth the balance check no longer applies: any node still holding at least minSize requests is reported. This is why detections land on the balanced subtree of an attack (e.g. a /27 inside a /24), not on the enclosing network.

Common presets: Strict 0.05-0.1 (only tightly balanced clusters, fewest false positives), Balanced 0.1-0.2 (good default), Loose 0.3+ (tolerates lopsided subtrees, broader detections).

Multiple Cluster Arg Sets

Different cluster patterns require different parameters. Specify multiple sets to catch clusters at various scales:

clusterArgSets = [
  [10000, 16, 24, 0.3],   # Tier 1: Large clusters
  [1000, 24, 28, 0.1],    # Tier 2: Distributed clusters
  [100, 30, 32, 0.05]     # Tier 3: Focused clusters
]
useForJail = [true, true, true]

Or via CLI:

--clusterArgSets 10000,16,24,0.3 \
--clusterArgSets 1000,24,28,0.1 \
--clusterArgSets 100,30,32,0.05

Each set runs independently against the trie. Results are combined, and duplicates are deduplicated in the jail.

Tuning Scenarios

Emergency Response

Low minSize, multiple ranges, moderate thresholds:

--clusterArgSets 500,28,32,0.1 \
--clusterArgSets 2000,20,28,0.2 \
--clusterArgSets 10000,16,24,0.3

Security Audit

Four tiers for comprehensive coverage, useForJail = false for analysis only:

clusterArgSets = [
  [100, 28, 32, 0.05],
  [1000, 24, 28, 0.1],
  [5000, 20, 24, 0.2],
  [10000, 16, 20, 0.3]
]
useForJail = [false, false, false, false]

API Protection

Endpoint-filtered, moderate sensitivity:

endpointRegex = "/api/.*"
clusterArgSets = [[500, 28, 32, 0.1]]

Brute Force Detection

Login-specific, low threshold:

endpointRegex = "/login|/wp-login\\.php|/admin"
clusterArgSets = [[100, 28, 32, 0.05]]

Performance

Clustering is extremely fast - typically <1ms even with multiple sets (measured on one machine, varies with hardware and traffic shape):

Cluster SetsTypical Time (1M requests, 500k unique IPs)
1~100 us
3~300 us
5~500 us

Narrower depth ranges and higher minSize values are slightly faster.

Common Mistakes

Threshold too low (0.001): Demands near-perfect balance between subtree halves - you will detect almost nothing. Start at 0.05+.

minSize too small for high traffic (10 on a 1M req/day site): Too much noise. Scale minSize with traffic volume.

Depth range too wide (8-32): Inefficient. Use focused ranges like 20-28.

Only one cluster arg set: Misses different cluster sizes. Use 2-3 sets with different scales.

Interpreting Results

Set 1: min_size=1000, depth=24-32, threshold=0.10
Detected Threat Ranges:
  198.51.100.192/26            3,083 requests  (  0.29%)
  203.0.113.91/32           1,308 requests  (  0.12%)
  • /26 = 64 IPs, with 3,083 requests - a cluster of IPs in a single subnet
  • /32 = 1 IP, with 1,308 requests - a single high-volume IP
  • The percentage shows each range’s share of total requests after filtering

Validation and Testing

Test parameters on known data:

flokbn static --logfile access.log \
  --clusterArgSets 1000,24,32,0.1 --plain > results-1.txt

flokbn static --logfile access.log \
  --clusterArgSets 500,24,32,0.05 --plain > results-2.txt

diff results-1.txt results-2.txt

Sweep thresholds:

for threshold in 0.05 0.1 0.2 0.3; do
  echo "threshold=$threshold"
  flokbn static --logfile access.log \
    --clusterArgSets 1000,24,32,$threshold --plain | grep "Detected"
done