Download docs/DESIGN.md from Dibachain/DibaGuard: direct link, hf CLI and curl.
- Browser
- Download file 23.6 kB
-
https://huggingface.co/Dibachain/DibaGuard/resolve/main/docs/DESIGN.md
- Command line
-
hf download hf://Dibachain/DibaGuard/docs/DESIGN.md
-
curl -L -o DESIGN.md https://huggingface.co/Dibachain/DibaGuard/resolve/main/docs/DESIGN.md
DibaGuard β design (v0.1, 2026-10-30)
Open-release copy (DibaAi/DibaGuard-Open). This document was written for the private build of DibaGuard. Numbers that involve the HTTP request model (
http_v2) were measured with that model, which is not part of the open release; the open release ships the licence-clean network models described inCLEAN_MODELS.md. Licence statements inside this document are superseded byDATA_LICENSES.md,CLEAN_MODELS.mdand the repository'sLICENSE,LICENSE-WEIGHTSandNOTICEfiles. Sections of the original that were written in Persian were removed from this copy; nothing else was edited.
Historical note. This is the v0.1 design document. Its statement that a licence-clean retrain could use the "MIT-licensed merged NetFlow v3" copy was wrong: that label was set by an uploader who is not the data owner (
DATA_LICENSES.md, row 3). The licence-clean models were trained as described inCLEAN_MODELS.md.
A CPU-only attack-intensity model for network traffic and web requests, built on the DibaOne decision architecture (typed calibrated outputs, tiered cascade with a router, adaptive memory with feedback). Working name: DibaGuard.
Answer it gives, for every event and continuously for every entity and site:
| level | output |
|---|---|
| event (one flow, one HTTP request, one packet burst) | p_attack (calibrated), family (open set: sqli, xss, traversal, cmdi, ssrf, ssti, xxe, jndi, scan, bruteforce, dos, botnet, exfil, β¦), severity 0β1, score 0β100, evidence (decoded payload span / features that drove it), tier and Β΅s spent |
| entity (source IP, session, API key, JA3/UA fingerprint) | risk 0β100 with decay, campaign id when several entities share a fingerprint |
| site | pressure 0β100: how much of the current traffic is attack, by family |
1. Hard budgets (the product is the budget)
| budget | target, 1 vCPU, no GPU |
|---|---|
| model files, all tiers | < 15 MB (int8) |
| RAM, runtime + 1 M tracked entities | < 300 MB |
| HTTP throughput (tiers 0β1, batched) | β₯ 20 000 req/s; p99 < 1 ms incl. tier 2 |
| flow throughput (tier 1) | β₯ 200 000 flows/s |
| tier 2 share of traffic | β€ 5 % (router-controlled) |
| dependencies at inference | numpy + onnxruntime (no torch) |
Every report states the machine, threads, batch size and the measured numbers. A run with no numbers is not a run.
2. Architecture (DibaOne heritage β what is new here)
βββββββββ tier 0: stream state (O(1)/event) ββββββββββ
packets βββΊ β flow aggregator (NetFlow v2-compatible features) β
NetFlow βββΊ β per-entity counters: count-min sketch + EWMA ββββΊ features ββ
HTTP βββΊ β canonicaliser: multi-round decoding, normal form β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ βΌ
tier 1 (Β΅s): flows β gradient-boosted trees (ONNX/treelite) calibrated p, family
HTTP β hashed char/token n-grams + structure β linear (sparse)
router: confident? ββyesβββΊ decision layer
no (β2β5 %)
tier 2 (β0.3 ms): tiny byte transformer (β1β2 M params, int8), multi-task heads
βΌ
decision layer: per-family temperature, Mondrian conformal thresholds (FPR β€ Ξ±
per site/endpoint), abstain = "review", entity risk accumulation, evidence
βΌ
adaptive layer: site profile (template + parameter-type model), kNN memory,
feedback (/v1/feedback) β instant correction without retraining
Innovations (each one has a benchmark that can prove or kill it)
- Guaranteed false-alarm budget per deployment. Thresholds are set by Mondrian conformal prediction on the
site's own benign traffic, per endpoint group and per source type, so
FPR β€ Ξ±holds with a finite-sample guarantee instead of a vendor-chosen threshold. Conformal martingales over the same p-values detect drift and trigger recalibration. Benchmark: realised FPR vs Ξ± on held-out benign traffic of unseen sites. - Compute-budgeted cascade. The router (as in DibaOne X1) sends each event to the cheapest tier that is
confident enough, under a per-request
max_us; thresholds fitted on dev to minimise expected cost. Benchmark: accuracy vs Β΅s/event curve; share of events per tier. - Evasion-invariant training. A deterministic canonicaliser (URL/HTML/unicode/hex/base64 decoding until a fixed point, SQL/JS comment and whitespace folding, case folding) plus a mutation engine (encodings, case, comments, synonyms, splitting) and a consistency loss: an attack and its obfuscated variants must score alike. Benchmark: detection on obfuscated attacks never seen in training (evasion suite).
- Self-learned positive model per site (zero-day). A compact profile of each site's normal requests: path templates (Drain-style tree), parameter types (int / uuid / email / slug / free text / Persian text), length and charset distributions, in sketches of a few MB. Violations add an anomaly term to the supervised score, so attacks with no known signature still raise the score. Benchmark: leave-one-family-out recall.
- Attack intensity, not only labels. Event probability Γ severity, accumulated per entity with decay and linked across entities by shared fingerprints (low-and-slow scans, credential stuffing from many IPs). Benchmark: time-to-detect and campaign recall on replayed multi-stage attacks.
- Persian-aware. Benign hard negatives with Persian text in parameters (percent-encoded UTF-8, ZWNJ, Persian digits), Iranian site traffic (zanbil.ir logs) β a common false-positive source for Western WAF rules.
- Learning from analysts in seconds (DibaOne adaptive memory): a marked false positive changes the next decision immediately (kNN override + gated adapter), and is kept for the next training round.
- Feeds anything: raw packets (own aggregator), NetFlow/IPFIX exports from routers (no capture needed), access logs (nginx/apache/IIS), reverse-proxy mirror, or an inline SDK call.
3. Data (private R&D first; licences tracked per row, meta.licence)
| modality | sources (Kaggle-attached; nothing downloaded through the office network) |
|---|---|
| flows, NetFlow v2 view | NF-UQ-NIDS-v2 (UNSW-NB15, BoT-IoT, ToN-IoT, CSE-CIC-IDS2018), NF v3 sample |
| flows, CICFlowMeter view | CIC-IDS2017 & CSE-CIC-IDS2018 (DistriNet label-corrected), CIC-DDoS2019, CIC-IoT2023 |
| real traffic | LUFlow (university network, honeypot-labelled, to 2026) |
| packets | UNSW + CIC-IDS2017 labelled PCAP subset |
| HTTP | CSIC 2010, SQLi/XSS sets (incl. obfuscated), labelled access logs, attack-simulation lab, payload repos (PayloadsAllTheThings, SecLists β MIT; OWASP CRS regression tests β Apache-2.0) |
| benign HTTP | CSIC normal, zanbil.ir logs (CC0), requests generated from public OpenAPI specs (APIs.guru, CC0), Persian/code/SQL-looking benign text as hard negatives |
Rules: no IPs, timestamps or flow ids as features (shortcuts); dedup by feature hash before splitting; splits by capture day / site where possible; licences per row (many NIDS sets are CC BY-NC-SA β private R&D; a commercial release trains on the permissive subset or needs permission).
4. Evaluation (frozen test sets; one scorer)
- in-distribution, and leave-one-dataset-out (train on the others, test on the held-out dataset) β the honest generalisation number most papers omit;
- TPR at FPR = 1e-3 and 1e-4, AUROC, AUPRC, macro-F1 over families, ECE;
- evasion suite (obfuscated attacks), Persian benign hard-negative FPR, leave-one-family-out recall;
- Β΅s/event, events/s, RAM, model MB on a stated CPU.
5. Plan
| step | where | status |
|---|---|---|
| S0 survey of every dataset (schemas, labels, counts) | Kaggle CPU, both accounts | running |
S1 build flows_v1 and http_v1 corpora β DibaAi/dibaguard-data-private |
Kaggle CPU | next |
| S2 tier 1 baselines (trees for flows, n-gram linear for HTTP) + leave-one-dataset-out | Kaggle CPU | |
| S3 tier 2 byte model + evasion training | Kaggle GPU (T4) | |
| S4 decision layer (calibration, conformal, entity risk) + runtime (numpy/onnxruntime) | local | |
| S5 site profile, feedback, private test Space, report | local + HF Space (private) |
6. Deployment: one engine, many hosts (added 2026-10-30)
Goal: the strongest detector we can train, packaged so it drops in next to any load balancer or becomes one.
Model contract (language-agnostic). A tier-1 model is a flat file:
feat[S] int16, border[S] float32, tree_tests[T,D] int16, leaves[T,2^D,C] float32|int8, bias[C], info json
(dibaguard/flows/trees.py). Evaluation = S compares, TΓD bit gathers, T leaf lookups, one sum. The HTTP tier-1
model adds emb[B,E] int8 + scale and the n-gram hash spec (dibaguard/hashing.py: FNV-64 rolling, splitmix mix,
orders 2β6). Both are ~100 lines in Go; a parity test (tests/parity/*.jsonl = inputs + expected raw scores,
bit-exact) is part of the release, so any port is proven, not trusted.
Three independent layers, each replaceable:
- Score β stateless, pure function
record β raw score. Library (Python/numpy, Go), or sidecar. - Decide β threshold (conformal, site- or shipped-calibrated), temperature, family head on alarms, entity risk, site pressure, drift martingale. Small mutable state (β a few MB), exportable/importable as JSON β can be shared across LB replicas or kept per replica.
- Adapt β site profile (percentile grids;
siteprofile.pycompiles them into the tree borders, so the deployed model stays raw-feature, same size, same speed), feedback memory, periodic recalibration. Runs offline or in a control-plane process; ships new borders/thresholds to layer 1/2 atomically.
Hosts. (a) Sidecar: HAProxy SPOE / Envoy ext_proc / nginx auth_request β local UDS/HTTP, p50 < 100 Β΅s.
(b) Native Go load balancer with the engine embedded (flows from conntrack/NetFlow mirror, HTTP from the proxy
itself) β score every request, rate-limit / challenge / block per entity risk and site pressure. (c) Library.
Decision outputs are the same objects everywhere: {p, alarm, family, severity, score 0-100, entity_risk, site_pressure, threshold_source}.
Test ladder (every release): unit parity (PythonβGo bit-exact) β LODO cross-network numbers β site-calibrated FPR actually β€ Ξ± on held-out benign β evasion/obfuscation suites (HTTP) β load test (req/s, p99, RSS) β 24 h soak on replayed real logs with drift alarms counted.
7. v2: the woven cascade (2026-10-30)
Why: v1 scores each flow / request alone. Cross-network evaluation with source-capped sampling (every source contributes at most one 60k-flow run, so a few DoS bots cannot dominate) shows that single-record detectors - trees or a GRU - transfer poorly; what a site can always have is its own normal traffic. v2 therefore weaves three threads per source and lets the site's benign stream condition every one of them:
| layer | what | cost (Go, 1 core) | runs on |
|---|---|---|---|
| 0 entity state | decayed counters, novelty memory, gaps (36 features) | 275 ns | every flow / request |
| 1 trees | oblivious trees on layer-0 features (shipped prior: "what attacks look like") | 1.7 Β΅s | every flow |
| 1' site anomaly | quantile p-values / isolation forest on layer-0 features, fitted on this site's benign only | ~1 Β΅s | every flow |
| 2 sequence net | causal GRU per source over its event stream (+ optional site context FiLM) | 8.6 Β΅s at d=24 (keep d β€ 64) | warm sources only (tier-1 above the low-water mark) |
| HTTP | hashed n-gram bag + MLP conditioned on the site profile (HttpModel2); the request also enters layer 0 as an event | 8.9 Β΅s | every request |
Decision: every thread yields a conformal p-value against the site's benign reference; the verdict is the minimum (Bonferroni over threads), so the realised false-alarm rate stays at Ξ± whatever the mix. Family head on alarms only; EntityRisk / SitePressure / drift as before.
Evaluation protocol (guard_jobs/seq_train.py): temporal + leave-one-network-out on source-capped corpora, each run reporting flow AUROC, site-calibrated FPR/TPR, entity-minute AUROC and the intensity correlation for the GRU, the trees, the two anomaly baselines and the fused verdicts. Honest result: single-record transfer is weak for everyone; the fused, site-conditioned verdict is what ships.
Modern threat landscape (2025-26 vendor reports): L7 floods and bots (half of web traffic), API abuse, credential stuffing, scanners, AI crawlers - i.e. behaviour over time per client, which is exactly what layers 0/2 and the HTTP session stream measure. Injection payloads still matter and are covered by the n-gram model; the 2025 ModSecurity thirty-day archive (CC BY 4.0) is the modern attack-payload source.
LLM bridge (dibaguard/llm.py): five tools (status, score flows, score http, calibrate, report) as MCP over stdio, OpenAI / Anthropic tool schemas and a stdlib HTTP server; the incident report (dibaguard/report.py) is a fixed JSON schema rendered to English and Persian from the engine's own state - no model narrates numbers it did not compute.
8. v2.1: the real-world layer (2026-10-30, evening)
What was weak after v2 and what fixed it - every item measured, none tuned by hand:
| weakness | root cause | fix (dibaguard/engine.py) |
|---|---|---|
| per-flow detection at Ξ± = 1e-3 β 0 on unseen networks | the flow is the wrong unit: an attacker sends hundreds of flows, a verdict per flow throws the sequence away | per-source accumulated evidence E β EΒ·e^(-Ξt/Ο) + (βlog p) (Ο = 120 s), judged per (source, minute) against the site's benign source-minute maxima (decide.SourceEvidence). LODO CIC-IDS2018: every attacking source caught, source-minute TPR 0.6 at FPR 2e-3 (was 0.0 per flow) |
| realised FPR above target when the traffic mix changes | one global reference; a new or bursty service moves every threshold | Mondrian references by service Γ source age (ent.service_key, AGE_EDGES): a client's first flows are judged only against other clients' first flows; a cell without reference abstains (p = 1) instead of guessing |
| false alarms on new clients (flash crowds) | births look extreme against steady-state sources | same age conditioning + abstention; destination-side features kept out of per-flow verdicts (they belong to site pressure) |
| FPR ratchet over hours (+1 %/h) | self-cleaning rolling references trimmed the benign tail; anomaly scores against a growing reference change scale | epoch re-calibration: every recal_s (30 min) all references are rebuilt at once from the last recal_window_s (2 h) of traffic, sources with evidence left out; the evidence reference is built out of sample (last quarter scored against the first three). Measured: 0.08 β 0.23 β 0.23 % per source-minute over three benign hours at Ξ± = 1e-3 (was 0.18 β 1.0 β 2.5 %) |
| wrong attack families | the GRU family head does not transfer | transparent rules on the stream counters (ent.family_rules): port walking β portscan, same (host, port) again and again β bruteforce, β₯ 10 flows/s at one service β dos/ddos, host walking with failures β recon |
| supervised prior scoring a SYN scan as benign | dataset artefacts in unconstrained trees | monotone trees on the 24 attack-direction features (more scanning can never look more benign) |
| unseen web application (WordPress) | vocabulary the model never saw | site n-gram novelty (HttpModel2.novelty): share of a request's n-grams absent from the site's own benign vocabulary, rarity-weighted - evaluated (WordPress AUROC 0.92 vs 0.61), not yet part of the live verdict |
False-alarm budget: Ξ±/2 to the evidence tier, Ξ±/(2Β·k) to each of the k per-flow tiers (Bonferroni), so the union stays at Ξ±.
What the engine learns on the server and what it does not: every reference, threshold, site profile and vocabulary is re-learned from the site's own traffic every epoch (no labels, poisoning-resistant by excluding sources with evidence); model weights (trees, GRU, HTTP MLP) are frozen - online weight updates without labels would be a poisoning surface. The feedback loop (operator-confirmed alarms β periodic offline retraining) is the next step, not an online one.
9. v2.2: distributed floods, slow scans, feedback, rules, and the live lab (2026-10-31)
The static cross-dataset numbers hid live failures that only a running, multi-wave environment shows. A local red-team/blue-team lab (scripts/redteam_lab.py: a self-owned localhost target, benign traffic, and the engine's own playbook patterns replayed over several waves, scored against known ground truth) drove the next round of fixes:
| gap the live lab exposed | fix |
|---|---|
| distributed / volumetric floods (amplification, peer/sybil floods) have no single guilty source | per-destination evidence tier: a destination's (flows, no-reply, new-source) anomaly accumulated per destination-minute; a flow is blamed only if it is new to the destination and off-profile, so the server's established benign clients are spared. NTP amplification 1.0, peer flood 0.76-0.80 |
| slow scans (nmap -T1 over ~30 min) never accumulate at tau=120 s | long-horizon source evidence (tau=1800 s) in parallel, fed only by mildly-suspicious flows; once its reference is warm (~2 epochs) slow-scan detection is 0.95 |
| detection decayed wave-over-wave (partially-detected attacks seeped into the self-training) | reference hygiene (as of v2.5): a source whose alarms name an attack family, or whose alarms keep coming while it behaves like an attacker, is excluded from the reference entirely; a sporadic un-named alarm stays in (it is the benign tail - excluding it ratchets the FPR up); no source may write more than ~1% of a reference; plus an immutable anchor mixed into every epoch and a drift guard that asks who moved the tail. Destinations are not excluded. See docs/TECHNICAL.md section 4.3-4.4 |
| HTTP session attacks (credential stuffing, scrapers, API enumeration) under-detected | the request is also an event of the client's stream; with the burst preserved, stuffing/scraper/BOLA all reach 0.98-0.99 |
Live lab, 6 waves, shipped models, 3000-client synthetic site: HTTP attacks caught 100% (JNDI/WordPress via rules 1.0, credential stuffing 0.99, scraper 0.94, BOLA 0.98); flow attacks 77% averaged over cold+warm waves (NTP amp 1.0, rapid reset 0.95, SSH brute 0.75, peer flood 0.76, slow scan 0.16 cold -> 0.95 once warm); benign false-alarm rate 2e-4 per (source, minute) for flows and 2e-4 for HTTP; blue-team feedback precision 0.95.
Operator layer:
- Rules (dibaguard/shield/rules.py): plain-language rules in Persian or English are interpreted (a built-in
bilingual parser, or an LLM returning the same JSON schema) into deterministic predicates and enforced from the
first second - so there is no "unprotected while it learns" window. The interpretation is shown back for
confirmation. An
allowrule shields a source; the playbook's hard signatures (JNDI, CMS paths) also act at t=0. - Feedback (dibaguard/shield/feedback.py): the operator confirms / rejects / allowlists alarms from a dashboard, MCP or REST. Reject lifts the action and the source's traffic becomes benign reference immediately; confirm takes (and escalates) the action; every verdict is logged and exported as labelled data for the offline retraining job. So there is no online weight update (poisoning-safe), but the layer adapts at once to operator intent and accumulates a retraining set.
- Licence-clean retraining: the network corpus is being rebuilt from the MIT-licensed merged NetFlow v3 (guard_jobs/entity_build.py nfv3_parquet, corrupt row groups skipped) so the shipped prior can be trained without the CC BY-NC-SA sets.
10. v2.3: hard adversarial lab, zero-benign-block guarantee, graceful degradation (2026-10-31)
A hard-mode lab (scripts/redteam_hard.py) runs 8 waves in which the attacker evolves to evade (slower, more source IPs, smaller payloads, UA mimicry) while the benign side is hostile (40% brand-new clients each wave, flash crowds, Persian traffic, a newly-added service). It scores recall per family and the benign BLOCK rate (the real harm), per wave, so the learning/degradation curve is visible.
What it forced, and the design principles that came out of it:
Evidence is a zero-mean CUSUM, not a sum. Per-flow increment is
-log p - k(k = the benign mean of the suspicious-flow -log p), floored at 0. A benign source drifts around 0 at any request rate, so a chatty client or a flash crowd no longer accumulates its way over the threshold; only a genuine excess builds up. Gated to off-profile flows (p < 0.5) on both the fast (tau=120 s) and slow (tau=1800 s) horizons.First contact is never auto-blocked. A source's first flows carry no behaviour to judge; behavioural tiers abstain for a newborn source unless the single flow is itself self-evident (p < alpha, e.g. a 1400-byte no-reply UDP to a victim) or the supervised prior fires hard. A soft newborn alarm becomes
block_ok = False-> the layer issues a challenge (HTTP) or monitor (flow), never a block. HTTP session alarms block only when the client's requests are largely failing (4xx/5xx): a user browsing many pages trips the path-diversity signal but gets 200s, so it is challenged; enumeration / credential stuffing / scanners fail requests and are blocked.Result (8 waves, evolving attacker, 40% flash-crowd benign): zero benign auto-blocks on both flows and HTTP in every wave; feedback precision 1.0. Recall: rapid reset / NTP amplification / SSH brute force ~1.0, peer flood 0.94; the learning curve degrades gracefully only against the maximally-evading low-and-slow distributed cases (evolving scan 1.0 -> 0.16, distributed credential stuffing 0.98 -> 0.23 as it spreads over many client IPs). These are the known-hard, aggregate-only attacks; they are documented honestly and are the roadmap's global-rate tier, not a silent failure. Benign alarms that are not blocks (challenges/monitors) are expected under a flash crowd and are the standard "under attack" trade-off, tunable via policy.min_action_score.
Multilingual, by design. Rule interpretation and the LLM bridge are language-agnostic: with an LLM wired in (the normal deployment of an LLM-facing product) a rule in any language is interpreted to the same JSON schema (rules.LLM_SCHEMA_PROMPT); the built-in parser is only the Persian/English offline fallback. The rendered report and dashboard are English + Persian.
What is honestly not done (roadmap, not claimed): a global/aggregate rate tier for fully-distributed low-volume attacks; automatic database- and network-topology profiling beyond the posture audit's service recognition; and online weight learning (kept offline on purpose, fed by the feedback-exported labels) - see section 8.