An AI-powered log pre-classifier for Microsoft Sentinel.
Harbinger sits alongside your existing log path to Microsoft Sentinel. It listens to a copy of your syslog feed, classifies each event with a local Ollama model, and produces a profile report showing which sources are signal, which are noise, what tier they belong on, and how much you could save by re-tiering. The traffic actually reaching Sentinel is untouched.
- Why Harbinger exists
- What it does
- Why observe-only matters
- How it works
- Installation
- Quick start
- Example output
- Requirements
- Security considerations
- Limitations
- Roadmap
- Documentation index
Microsoft Sentinel charges by ingested gigabyte. A typical SOC pipes everything into the Analytics tier "just in case", which means a non-trivial slice of the bill is funding storage for liveness probes, cron ticks, and routine kernel messages that no analyst will ever query. Sentinel offers a Basic tier and an Auxiliary tier specifically for these — Basic at roughly 1/5 the cost, Auxiliary at roughly 1/100 — but figuring out which sources should land where is a manual review job that operators rarely have time to do.
Harbinger is the answer to "what would happen if we automated that first pass". It reads a copy of the traffic, classifies each event with a small local LLM, and tells you per source:
- Is this high-signal, medium-signal, low-signal, or noise?
- Which Sentinel tier (Analytics / Basic / Auxiliary) belongs to this source?
- How many GB/day is this source contributing?
- What does the current spend look like and how much could you save by adopting the recommendation?
The output is a report. Not a routing decision, not a DCR change, not a Slack message — a JSON + Markdown report you read, evaluate, and act on yourself.
| Capability | Detail |
|---|---|
| Ingest | Listens on UDP/TCP for syslog (RFC 3164 + RFC 5424). Also detects NXLog and Winlogbeat JSON-over-syslog for Windows event semantics. |
| Classify | Local Ollama model (phi4-mini recommended). Returns log_type, signal_grade, tier_recommendation, confidence. |
| Aggregate | SQLite (WAL) rollups by (source, log_type, 5-minute window). Bounded retention. |
| Report | JSON (schema-pinned) + Markdown + HTML. Per-source breakdown, Windows EventID histograms, anomalies, top-10 noise sources, monthly cost projections in your chosen region and currency. |
| Cost projection | Live Azure Retail Prices API integration. Multi-region. Multi-currency via either operator-pinned rates or an optional Frankfurter (ECB) FX cache. |
| Run | One-shot installer, repo-root launcher (./harbingerctl), hardened systemd unit. Air-gapped supported. Release CI gated on pip-audit + pytest. |
A complete example of the JSON report shape is at docs/sample-profile.json.
Harbinger never modifies, filters, or routes traffic going to Sentinel. The traffic that reaches your existing ingestion pipeline is untouched — Harbinger only sees a fan-out copy.
This is a permanent design property, not a v1 limitation. There is no code path that could suppress an event before it leaves the relay. The reasoning:
- Blast radius. Anything that modifies live security telemetry needs an approval workflow, a threat model, a roll-back path, and a different posture toward false positives. Bundling that with a profiler doubles the surface area for a marginal benefit.
- Trust. Operators evaluating Harbinger should never have to wonder whether it might quietly drop a critical event. The answer is "no — by construction".
- Composability. If you want routing, you build it downstream of Harbinger's report —
rsyslogandsyslog-ngalready do this well using filters keyed on programname or content. Harbinger's job is to tell you what to filter; your existing tooling does the filtering.
See docs/architecture.md §6 for the full list of non-goals and the rationale for each.
flowchart LR
A[Linux syslog] --> R[rsyslog / syslog-ng]
B[Windows hosts<br/>via NXLog / Winlogbeat] --> R
R -- original copy --> S[Microsoft Sentinel<br/>untouched]
R -- fan-out copy --> H[Harbinger]
H --> O[profile.json<br/>profile.md<br/>profile.html]
E[.evtx files] -.->|harbinger replay-evtx| H
Internally Harbinger is a 5-stage asyncio pipeline with bounded queues. Full architecture, including trust boundaries and failure modes, is in docs/architecture.md.
git clone https://github.com/azurebeard/harbinger.git
cd harbinger
./scripts/install.shinstall.sh checks Python ≥ 3.11, creates a venv, installs Harbinger and its dependencies, and copies the example config into place. The script is idempotent — re-running is a no-op.
For Windows event ingestion via .evtx replay, install the optional extra:
.venv/bin/pip install -e ".[evtx]"For deployment under systemd (auto-restart, hardened sandbox), see the recipe at the top of scripts/harbinger.service and docs/live-test.md § 7b.
# 1. Pull a local model
ollama pull phi4-mini
# 2. Start the listener (defaults to 127.0.0.1:1514 — needs no root)
./harbingerctl run --config config/harbinger.yaml --heartbeat 5
# 3. Send 200 synthetic events from a second shell to validate the install
./harbingerctl smoketest --count 200
# 4. Render a profile report
./harbingerctl report --since 5m --output-dir ./reportsFor an rsyslog fan-out example, see docs/quickstart.md. For the 30-minute end-to-end VM playbook including real syslog forwarding and systemd deployment, see docs/live-test.md.
Excerpt from a 24-hour profile report rendered by harbinger report. Full example: docs/sample-profile.json.
Harbinger Profile Report
========================
Window: 2026-07-14 09:00:00Z → 2026-07-15 09:00:00Z (24 h)
Events: 412,857 across 9 (source, log_type) pairs
Region: uksouth Currency: GBP
Top sources by volume
─────────────────────
source log_type events GB/day signal recommend £/month
web-01.example.local nginx-access 198,432 0.0897 noise auxiliary 16.98
dc-01.example.local windows-security-logon 84,561 0.0532 high analytics 863.91
fw-edge-01.example.local firewall-deny 56,298 0.0225 medium basic 76.80
app-batch-03.example.local ssh-auth 32,487 0.0117 high analytics 189.93
dc-01.example.local windows-sysmon-process 26,041 0.0089 high analytics 143.81
app-batch-03.example.local cron 8,642 0.0014 noise auxiliary 0.26
…
If every source were placed on its recommended tier, projected savings vs an
Analytics-everywhere baseline: £1,284.42 / month.
Anomalies (across all sources): future_timestamp=12, oversized_message=3
Per-source Windows breakdowns (top EventIDs, channels, providers) and parser-side anomaly flags are surfaced inline. See docs/schema.md for the full output contract.
| Component | Minimum | Notes |
|---|---|---|
| Python | 3.11+ | 3.12 recommended; 3.13 supported |
| Ollama | Latest stable | One supported model pulled — phi4-mini is the default recommendation |
| OS | Linux | Tested on Ubuntu 22.04 and 24.04. Any Debian or RHEL family should work. macOS works for development but not the documented systemd path. |
| vCPU | 4 | For sub-second classification on CPU. Fewer is workable but cold-starts are slower. |
| RAM | 8 GB | Smaller is OK for short captures; 4 GB will OOM under sustained load with phi4-mini resident. |
| Disk | 20 GB | ~3 GB for Ollama + model; remainder for SQLite retention and reports. |
Air-gapped operators: see config/harbinger.example.yaml for the pricing.offline and pricing.fx_offline knobs and the configurable pricing.fx_api_url for mirroring Frankfurter internally.
Model sizing trade-offs (CPU latency, JSON conformance, memory footprint) are in docs/benchmark.md.
| Property | Implementation |
|---|---|
| Event content stays local | Classification runs against a loopback Ollama endpoint by default. No cloud inference path exists. |
| Outbound network limited and disable-able | Only two outbound HTTPS endpoints (prices.azure.com, api.frankfurter.app), both for cost projections, both off-switchable. |
| Unprivileged by default | Binds port 1514 (no CAP_NET_BIND_SERVICE needed). Loopback bind by default. |
| No back-pressure on upstream | Bounded queues + drop-on-overflow. Harbinger being slow never stalls the operator's syslog forwarder. |
| Per-source rate limiting | Token-bucket cap stops one bad sender starving others. |
| Hardened systemd unit | NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, MemoryDenyWriteExecute, RestrictAddressFamilies. |
| Release CI gated on pip-audit | Every tag-push runs pip-audit against the runtime dependency closure. CVEs in any direct or transitive dep block the release. |
| No telemetry, no phone-home | Harbinger collects no usage data. The only outbound calls are the documented two above. |
Full security posture and disclosure process: SECURITY.md.
Harbinger is honest about what it can and can't do. The full document is LIMITATIONS.md; the high-level summary:
- Classifier accuracy depends on the model. Small CPU models (1–4 GB) handle the common patterns well but can be confidently wrong on rare or vendor-specific log shapes. The confidence floor (default 0.6) catches the worst cases by downgrading them to
unknown; tune viaclassifier.confidence_floor. - Recommendations are advisory. The reports tell you what looks like noise; the operator confirms and acts.
- Single-host design. No clustering, no shared state between instances. For estates above ~1000 events/s sustained, shard by source.
- Linux-only deployment surface. Windows event ingestion works fine (via JSON-over-syslog or
.evtxreplay) but Harbinger itself runs on Linux. - No real-time alerting. Reports are batch-rendered on demand; Harbinger is a profiling tool, not a SIEM.
There is no committed roadmap beyond v1.x. The pre-1.0 backlog has all shipped (Windows event ingestion, FX cache, multi-region pricing, multi-currency conversion). Future direction will be issue-driven.
Permanently out of scope (with rationale in docs/roadmap.md):
- Active tier routing — by design.
- Native WEC/WEF endpoint — wrong shape for an observe-only tool.
- Cloud inference — event content never leaves the host.
- Hosted dashboard — Harbinger is a CLI tool; the JSON output is consumable by any external visualisation.
| Document | Purpose |
|---|---|
README.md |
You are here. |
docs/architecture.md |
Full runtime architecture, trust boundaries, failure modes, non-goals. |
docs/schema.md |
Public stability contract — JSON output, CLI, config file. |
docs/sample-profile.json |
Complete realistic example of the JSON output. |
docs/quickstart.md |
5-minute syslog fan-out example with rsyslog / syslog-ng / journald. |
docs/live-test.md |
30-minute end-to-end VM playbook with real syslog forwarding. |
docs/benchmark.md |
Sizing and tuning. Model comparison, worker-count guidance. |
docs/windows-events.md |
NXLog, Winlogbeat, and .evtx recipes. |
docs/roadmap.md |
Release history and permanently-out-of-scope items. |
SECURITY.md |
Security posture and disclosure process. |
LIMITATIONS.md |
Known limitations of v1.x. |
CONTRIBUTING.md |
Development workflow, testing, coding standards. |
FAQ.md |
Frequently asked questions. |
CHANGELOG.md |
Release notes. |