Skip to main content

How the fabric works

A toolbox is not a longer list of plugins. Fifteen plugins that each block independently produce a gateway nobody can reason about and nobody dares arm.

The fabric is the opposite arrangement: many independent detectors feed one shared judgement, and one component acts on it with a graded response.

reputation ──┐
waf ─────────┤
bots ────────┤
honeypots ───┤──▶ threat score ──▶ threat policy ──▶ log · challenge · throttle · tarpit · deny · ban
traffic ─────┤ (per request) (per route)
logins ──────┤
uploads ─────┤
objects ─────┤
contract ────┘
│
└──▶ incidents ──▶ one alert instead of nine thousand
└──▶ ledger ──▶ cross-request memory ──▶ ban

Signals, score, decision​

Every detector publishes signals rather than verdicts. A signal carries a source, a weight (0–100), a tag, and a confidence:

{ "source": "reputation.feed", "kind": "reputation", "weight": 80,
"tag": "feed:firehol-l1", "confidence": 1.0 }

The score is the sum of the weights, capped at 100, each scaled by its confidence. That is the point of having a bus: three weak signals can add up to something worth acting on, which no single detector could ever conclude on its own.

Confidence is clamped to [0, 1], so a detector can be honest about being unsure but cannot inflate its own weight.

Why a graded response matters​

Without the fabric, the only outcomes are 200 and 403. That binary is why blocking mode is frightening: every false positive is a broken customer.

With it, a score of 45 costs a suspect caller nothing but a log line, a challenge or a quota, 75 costs them three seconds, and 95 gets them banned. The same detectors become deployable, because the cost of being wrong drops by an order of magnitude.

What contributes today​

SourceSignalWeight
IP reputation (feeds, CrowdSec, ASN)one per matching sourcethe source's own weight
WAFwaf:match when rules matched without a block decision, waf:blocked when the ruleset reached one — in monitoring too, with contribute, which the preset always setsweighed by the policy: waf_match_weight 45, waf_block_weight 90
Bot guardbot:impersonator:<name>, bot:<category>:<name>60 for a forged crawler, the rule's weight for a category
Honeypotshoneypot:<path or token>100 by default
Traffic guardtraffic:route_surge, source_surge, consumer_surge, asn_surge40 (30 for a network), more past twice and four times the threshold; a route's surge never more
Login guardlogin:credential_stuffing, password_spraying, account_under_attack, breached_password50, 70, 40, 20, more past twice the threshold
Upload guardupload:<reason>from 10 (too many files) to 80 (polyglot, zip slip)
Object guardobjects:surge, sequential, enumeration, with contribute40, 50, 50
API contractapi:<mismatch>, with contribute20 for a path or method outside the contract, 30 for the rest
Ban storeshort-circuits before anything else runs—

Fail2ban, the error leakage guard and the sensitive data guard act on their own and record their decisions, but add nothing to the score: what they see is already a decision.

The WAF contributes, and stays the authority on its own blocking

The WAF publishes what it found to the bus whatever its mode: rules matching without a block decision, and a block decision, are two signals, and the policy says what each is worth. The case worth having is the one the WAF does nothing about itself: a match in monitoring mode still weighs on the score, against everything else. In blocking mode the WAF still refuses the request itself; waf_block_decisive makes a block reach the policy's top tier whatever the sum. See policies.

The two plugins​

PluginRunsDoes
Threat gateaccess validation, firstRefuses callers already banned, before any inspection
Threat responserequest transformation, lastReads the accumulated score and applies one tier

Order matters: the gate is deliberately separate so a ban costs a map lookup rather than a full inspection of a request nobody is going to serve. Put the response plugin after the WAF in the route's plugin chain, or it will read a score the WAF has not contributed to yet.

That last sentence is a requirement on whoever configures the route, which is why there is a preset plugin that expands into the whole chain with the ordering already fixed.

Everything is recorded​

Whatever the outcome, one normalised CloudApimSecurityEvent is emitted and correlated into an incident. That happens in dry run too — which is the whole point of dry run.