Most home lab IDS setups follow the same pattern. You stand up Suricata, it generates thousands of alerts, you get overwhelmed, and eventually you stop looking at the dashboard. The signal is in there somewhere. You just can never find it fast enough to care.
I wanted something different. I wanted a system that would actually get my attention when something worth attention happened, without requiring me to babysit a SIEM or tune alert thresholds every weekend. I wanted it to work on hardware I already had. And I wanted the intelligence layer to be swappable so I could run it locally or route through a cloud API without touching the detection logic.
The result is Tattle.
The name
Tattle traces back to Middle Dutch tatelen, meaning to babble or chatter. A tale is a report, a story, a count of what happened. A tattletale is someone who overhears something and makes sure the right person knows about it. That is exactly what this pipeline does. Suricata chatters constantly. Tattle listens to the chatter, figures out what actually matters, and tells you.
What it is
Tattle is a passive intrusion detection pipeline. It does not actively probe, scan, or respond. It watches. When something interesting happens on the network, it classifies the event, scores it, deduplicates it, and sends a structured alert to Slack. The name is intentional. It is a motion detector, not a guard dog.
The pipeline has four stages:
A Suricata sensor running on a Raspberry Pi monitors network traffic and writes events to a structured JSON log. The Pi is dedicated to this role. It does nothing else.
A forwarder picks up those events and sends them to the triage service running on a separate host.
A deduplication and episode engine called woop_dedoop filters out repeated alerts from the same source within a rolling time window. Without this layer, a single port scan generates hundreds of near-identical events. With it, you get one consolidated episode with a count.
A triage agent calls an LLM via LiteLLM, classifies the event, assigns a severity score, and formats a structured result for Slack delivery.
The entire pipeline runs as a Docker Compose stack. The LiteLLM abstraction layer is the piece I am most glad I included. It means I can point the triage model at a local inference server or a cloud API by changing a single config value. The detection logic does not care.
The sensor design
I made a deliberate choice to run the sensor on a Raspberry Pi 3 rather than a VM on the main hypervisor. There are a few reasons for this.
A VM sensor on the same host as your other workloads creates a conflict of interest. If the host is compromised or overloaded, your sensor goes down with it. A dedicated physical node has its own failure domain.
The Pi 3 is also resource-constrained in a useful way. It forced me to be disciplined about what I run on the sensor. No commercial ruleset. No full ET Open. Just a small inventory of local rules covering the specific things I actually want to know about: port scans, canary file access, suspicious probe patterns, and honeytoken callbacks.
woop_dedoop
The name is intentional. Whoop-de-doo, in its modern usage, is sarcasm. It is what you say when someone presents something as exciting and you are not buying it. That is exactly the job description. woop_dedoop looks at the flood of Suricata events, most of which represent the same scan or probe repeating itself, and responds with the computational equivalent of “big deal.” One consolidated episode. One alert. Move on.
Deduplication is not optional. I initially thought I could handle repeat events downstream in the triage agent. I was wrong. A single nmap scan against the local subnet generates enough Suricata events to overwhelm an LLM context window and run up API costs fast. woop_dedoop exists because I needed to solve that problem properly, and once I did, the rest of the pipeline became significantly more useful.
Honeytoken callbacks
The honeytoken mechanism deserves its own mention because it is the part of the pipeline I find most useful.
A honeytoken is a fake credential placed somewhere an attacker might look. If the credential is ever used, you know something is wrong. The hard part is building a reliable callback mechanism so you actually find out.
Tattle includes an always-fire callback route. Any request to that endpoint generates an immediate critical-severity alert regardless of deduplication state. The decoy credentials are formatted to look like real service tokens. If something or someone uses them, Slack lights up within seconds.
The word “honeytoken” appears nowhere in any path or route name in the running service. That was a conscious decision.
The triage layer
The LLM triage layer is genuinely useful, but it works best when you give it structured input and ask for structured output. Passing raw Suricata JSON and asking for a freeform assessment produces inconsistent results. Passing a normalized event object and asking for a scored TriageResult with specific fields produces something you can act on.
The LiteLLM abstraction was the right call here. During development I swapped between a locally hosted model and a cloud API multiple times without touching a single line of detection or triage code. When the local inference hardware comes online, the migration will be one config change.
Local rules
Local rules are better than no rules and worse than well-tuned commercial rules. The ET Open ruleset is comprehensive but too noisy for a Pi 3 to handle at wire speed on a busy network. Writing a small inventory of targeted local rules and knowing exactly what each one fires on turns out to be more operationally useful than inheriting tens of thousands of rules you do not understand.
If you know your environment, write rules for your environment. Generic coverage at scale is a problem for enterprise SIEM budgets. A focused sensor on a home lab network can afford to be specific.
What is next
The pipeline is complete and wired end to end. The next phase is closing the purple team loop. I am building out an isolated attack range on a separate virtual bridge with no uplink to the production network. A dedicated attacker node on that segment will generate realistic attack traffic against intentionally vulnerable targets, and those logs will flow into the same Tattle pipeline. The goal is to validate detection coverage, tune rules against real attack patterns, and generate the kind of messy, realistic log data that makes AI-assisted triage actually interesting to work on.
The inference layer will eventually move to a local vLLM instance running on dedicated GPU hardware. When that happens, the only change to Tattle is a single line in the LiteLLM config.
That is the point.