Last updated: 2026-05-31 23:41 UTC

Configuration

The keys under BotDetection:* you'll actually edit. Everything is settable three ways, in precedence order:

  1. CLI flag (where one exists): --mode production.
  2. Env var with __ separator: BotDetection__BotThreshold=0.75.
  3. appsettings.json (or --config /path/to/your.json).

stylobot --output-config /etc/stylobot/appsettings.json dumps the full effective config with every default visible. Edit that.

Detection thresholds

Key Default Effect
BotDetection:BotThreshold 0.7 Probability above this counts as bot.
BotDetection:NonAiMinProbability 0.01 Floor when no LLM ran. Stops the heuristic pipeline saturating to literal 0.0 for confident humans.
BotDetection:NonAiMaxProbability 0.90 Ceiling when no LLM ran. Prevents heuristic-only "100% bot" claims.
BotDetection:EarlyExitThreshold 0.95 Probability above which the orchestrator short-circuits remaining detectors.
BotDetection:ImmediateBlockThreshold 0.98 Probability above which action policies skip the soft-throttle path.

Action policies

--policy <name> picks the default; per-route overrides live under BotDetection:Policies and BotDetection:BotTypeActionPolicies.

{
  "BotDetection": {
    "DefaultActionPolicyName": "throttle-stealth",
    "BotTypeActionPolicies": {
      "Scraper": "block",
      "Tool": "throttle-tools",
      "MaliciousBot": "mask-pii"
    },
    "Policies": {
      "block": { "ActionPolicyName": "block", "BlockThreshold": 0.7 },
      "throttle-stealth": { "ActionPolicyName": "throttle-stealth", "BlockThreshold": 0.7 },
      "block-ai-scrapers": { "ActionPolicyName": "block", "AllowVerifiedBots": true }
    }
  }
}
Built-in policy Behaviour
allow Pass through, log only.
throttle-stealth Task.Delay then real response (bots think it's slow; humans don't notice).
throttle-tools 429 + Retry-After.
challenge JS / cookie challenge.
block 403.
block-hard TCP RST.
redirect-honeypot 302 to a fake.
mask-pii Real response, PII stripped. Requires ResponsePiiMasking:Enabled=true.

PII masking (when using mask-pii)

{
  "BotDetection": {
    "ResponsePiiMasking": {
      "Enabled": true,
      "AutoApplyForHighConfidenceMalicious": true,
      "AutoApplyBotProbabilityThreshold": 0.9,
      "AutoApplyConfidenceThreshold": 0.75
    }
  }
}

Without Enabled=true, mask-pii is a no-op.

Identity matcher

Required for the dashboard's fingerprint radar to render and for cross-request behavioural learning.

Key Default Effect
BotDetection:Identity:Enabled false Master switch. Set true to write fingerprint_keys rows + render the radar.
BotDetection:Identity:VectorDimension 512 Centroid vector size. Don't change post-deploy.
BotDetection:Identity:MatchThreshold 0.85 Cosine similarity above which a session matches an existing fingerprint.

LLM

Per-provider knobs under BotDetection:AiDetection.

{
  "BotDetection": {
    "AiDetection": {
      "Provider": "ollama",
      "MaxConcurrentRequests": 2,
      "TimeoutMs": 5000,
      "Ollama": {
        "Endpoint": "http://localhost:11434",
        "Model": "gemma4:e2b"
      }
    },
    "EnableLlmDescriptions": true
  }
}

Provider accepts ollama, llamasharp (in-process CPU), openai, anthropic, azure. Each provider has its own subsection: BotDetection:AiDetection:OpenAi, :Anthropic, :Azure, etc.

EnableLlmDescriptions=true is the master switch for the background description coordinator (per-fingerprint names + cluster summaries). Off by default.

Threat-intel feeds

The IP/CIDR matchers used by ThreatIntelContributor.

{
  "BotDetection": {
    "ThreatIntel": {
      "Enabled": true,
      "Providers": {
        "SpamhausDrop":  { "Enabled": true },
        "TorExit":       { "Enabled": true },
        "CisaKev":       { "Enabled": true },
        "CloudRanges":   { "Enabled": true }
      }
    }
  }
}

Without ThreatIntel:Enabled=true, the contributor short-circuits and known-bad sources never get flagged.

API keys

Required for --enable-api (the /api/v1/* surface). Trusted callers send the key as X-SB-Api-Key: <value> and bypass detection.

{
  "BotDetection": {
    "ApiKeys": {
      "internal-cron": {
        "Key": "<output of: stylobot genkey>",
        "Name": "Internal cron job",
        "Enabled": true
      }
    }
  }
}

Without at least one enabled entry, --enable-api fails on startup.

Response headers

What Stylobot writes on every proxied response.

Key Default Effect
BotDetection:ResponseHeaders:Enabled true Master switch.
BotDetection:ResponseHeaders:IncludeReasonHeader true Adds X-Bot-Reasons listing top detector reasons.
BotDetection:ResponseHeaders:IncludeProcessingTime true Adds X-Bot-ProcessingTime.

Signature labels

Pin a human-readable name to a specific signature (overrides the matcher's auto-name).

{
  "BotDetection": {
    "SignatureLabels": {
      "abc123def456": "Internal monitoring",
      "789xyz456abc": "Pingdom check"
    }
  }
}

Kestrel profile

Picks transport-layer tuning. Also settable via --profile.

Profile Thread pool Conns Keep-alive Body cap Use it for
balanced (default) 50 / 50 10k 30s none Mixed traffic.
api 100 / 100 20k 15s 64 KB JSON APIs, no WebSockets.
site 200 / 200 10k WebSockets 120s 1 MB Public site with browsers + SignalR.
highrisk 50 / 50 2k 5s small Under attack right now. Reject fast (3s header timeout).
STYLOBOT_PROFILE=api stylobot 5080 http://localhost:3000 --mode production

Signature hash key

HMAC key used to hash IPs into signatures. Set this in production so signatures persist across restarts.

# Generate
stylobot genkey
# Set
BotDetection__SignatureHashKey=<base64-output>

Default behaviour: session-scoped random key (signatures rotate every restart; warning logged at startup).

Logging

Standard ASP.NET Core: Logging:LogLevel:Default=Information. Stylobot uses Serilog under the hood; everything also writes to logs/stylobot-*.log next to the binary by default.

More