n8n Workflow
One workflow, Automated Domain & IP Reputation Guard (config/n8n/workflows/), imported by playbook 04.6. It runs every 3 minutes and analyses one observable — a domain and the IP it resolved to — per run, which keeps within the VirusTotal free tier (4 requests/min, 500/day).
An earlier version is published in the n8n library: Score DNS Threats with VirusTotal, Abuse.ch, HashiCorp Vault, and Gemini.
Flow
flowchart TD
A[Load AI configuration] --> B[Pick observable<br>v_pending_observables]
B --> C[VirusTotal: IP]
C --> D{detections ≥<br>vt_gate_min_malicious?}
D -- no --> E[Rule score] --> F[Clean verdict<br>no AI, no email]
D -- yes --> G[ThreatFox: IP<br>URLhaus: domain]
G --> H[Rule score 1–5]
H --> I[AI agent<br>Gemini + historical_verdicts]
I --> J[Parse and clamp score]
J --> K[Store verdict, audit, raw CTI]
F --> K
K --> L[Vector memory]
K --> M{score ≥ email_min_score?}
M -- yes --> N[Email alert]
K --> O{auto-block conditions?}
O -- yes --> P[Pi-hole denylist + block log] Steps
- Load configuration — one query reads all
ai_settings, the threat scale and the active prompt. Every threshold below comes from there and is edited in the AI Config UI. - Pick observable —
v_pending_observables LIMIT 1. Private IPs, allow-listed domains and pairs analysed in the lastcache_ttl_daysare already filtered out. - VirusTotal gate — VirusTotal checks the IP. Below
vt_gate_min_maliciousdetections the observable is scored by rules only and stored as clean: no further API calls, no AI tokens, no email. A failed lookup (quota, auth, server error) is never scored as clean: the run fails and the observable stays in the queue for the next run. - Enrich — ThreatFox (by IP) and URLhaus (by domain). Both are supporting sources.
- Rule score —
compute_threat_score()in PostgreSQL turns the results into a score 1–5 with a step-by-step trace. - AI agent — reviews the evidence and the rule score (details below).
- Store — raw responses, verdict, audit record and the embedding for vector memory.
- Act — email and Pi-hole auto-block (details below).
Rule Score
| Step | Rule |
|---|---|
| 1 | VirusTotal detections → CLEAN / LOW / MEDIUM / HIGH (vt_low_max, vt_medium_max); trusted infrastructure with ≤ vt_big_player_noise_max detections is CLEAN |
| 2 | ThreatFox → CLEAN (not listed), MEDIUM (listed), HIGH (listed and seen in the last tf_active_days) |
| 3 | Combine: active ThreatFox IOC with a malware family or ≥ tf_active_vt_min detections → 5; any HIGH or hits in both → 4; MEDIUM → 3; LOW → 2; else 1 |
| 4 | URLhaus: +1 when the base is ≥ 3 and the domain has online malicious URLs |
| 5 | Trusted infrastructure without a ThreatFox malware family is capped at 2 |
AI Agent
| Model | Gemini 3.1 Flash Lite, temperature 0.2 |
| Prompt | Active version from prompt_templates; threat scale and ai_max_deviation injected at run time |
| Input | Observable, VirusTotal / ThreatFox / URLhaus evidence, rule score with its trace |
| Tool | historical_verdicts — similarity search over past verdicts in pgvector, called once |
| Output | Structured JSON: label, English summary, primary evidence, supporting context, Polish analysis, proposed score, reason for any deviation, cited past verdicts |
The agent explains the rule score and may change it only for a reason visible in the evidence. The workflow then enforces the limits itself:
- the final score stays within
ai_max_deviationof the rule score, whatever the model proposes; - if the agent fails or returns invalid output, the rule score is stored with
ai_status = failed— nothing is lost; - cited past verdicts are kept only if they exist;
- evidence from the internet is treated as data; the prompt forbids following instructions found in it.
Every verdict is recorded in verdict_audit: rule score, final score, deviation reason, model and prompt version. Successful verdicts are embedded and added to verdict_vectors, so the next analysis can find them.
Actions
Email — sent when the AI analysed the observable and the final score is ≥ email_min_score. Colour follows severity; all values are HTML-escaped.
Pi-hole auto-block — the domain is added to Pi-hole's exact denylist when all of these hold:
pihole_block_enabled = 1,- VirusTotal answered and detections ≥
pihole_block_min_vt_malicious, - the IP is not trusted infrastructure.
The workflow logs in with a Pi-hole application password, adds the domain with a comment, logs out and writes the result (blocked, already_blocked or error) to pihole_block_log. To unblock, remove the domain in Pi-hole.
Secrets
The workflow reads secrets from HashiCorp Vault at run time:
| Vault path | Used for |
|---|---|
cyber-sentinel/credentials/postgres/app_manager | PostgreSQL |
cyber-sentinel/api-keys/virustotal | VirusTotal |
cyber-sentinel/api-keys/abuse/api-key | ThreatFox, URLhaus |
cyber-sentinel/api-keys/gemini/home-network-guardian | Gemini |
cyber-sentinel/credentials/gmail | Email: SMTP login and alert recipient (to) |
cyber-sentinel/credentials/pihole-api | Pi-hole API |
The n8n credentials needed by the AI and database nodes (Vault token, PostgreSQL, Gemini, SMTP) are created with fixed IDs by playbook 06.2, so the imported workflow works without manual setup.