tinysoc / README.md
Mroqui's picture
Link Field Notes and tag field-notes badge
2378bed verified
|
Raw
History Blame Contribute Delete
9.86 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade
metadata
title: TinySOC
emoji: 🛰️
colorFrom: red
colorTo: gray
sdk: gradio
sdk_version: 4.44.1
app_file: app_tinysoc.py
pinned: false
license: mit
tags:
  - build-small
  - backyard-ai
  - off-the-grid
  - well-tuned
  - off-brand
  - llama-champion
  - field-notes
  - tiny-titan
  - best-demo
short_description: A fully-local AI SOC analyst on a small model

🛰️ TinySOC: a local SOC analyst for people with no SOC

TinySOC triages security logs for a solo operator or a small MSP with no SOC team. It reads a log stream (Linux syslog, a Wazuh alert feed, or raw Windows events), flags what actually looks wrong, highlights why, and explains each finding in plain English: severity, summary, risk, and the next concrete action.

Submission to the Build Small Hackathon (Hugging Face × Gradio), Backyard AI track. The "someone I know" is the solo security operator drowning in alert noise.

Runs 100% locally on CPU via llama.cpp: no cloud API, no data leaves the box.

Submission (Build Small Hackathon)

Why a small, local model is the point (not an accessory)

Most "explain this alert" demos could call a cloud API and be done. TinySOC can't, by design, and that is exactly what makes the small local model load-bearing:

  • The highlighter scores every token of a log line by its perplexity (how surprising it is given normal behavior). That needs token-level logprobs from the model itself, something cloud chat APIs do not expose. So the model has to run locally, and a small one keeps it real-time on CPU.
  • Perplexity only paints novel tokens (a random C2 domain, a base64 blob). It does not decide maliciousness, a code model finds a reverse shell mundane. The deterministic baseline decides; perplexity highlights; the model explains.

How it works: three honest stages

  1. Baseline (deterministic detector). Learns a host profile from normal logs (users, processes, IPs, active hours) and scores each line with a noisy-OR per axis, emitting plain-English reasons. This carries detection, no LLM guesswork.
  2. Perplexity (highlighter). A small base code model scores token surprise in a single forward pass, underlining the spans that are genuinely novel.
  3. Explanation. A small (≤4B) model turns a flagged line + its reasons into a compact JSON triage: severity, summary, why, next_action, likely_false_positive.

Timestamps and hostnames are masked before perplexity scoring, so it measures message content, not the clock.

Inputs it understands

  • Linux without Wazuh: paste /var/log/auth.log or journalctl -o short.
  • Windows with Wazuh: Wazuh JSON alerts, including data.win events.
  • Windows without Wazuh: Get-WinEvent | ConvertTo-Json output.
  • Live Wazuh (dev): tails a local Wazuh manager's alerts.json.

The UI is a dark SOC console: a live feed, one-shot injection, log upload, five built-in scenarios, a per-axis risk radar, and provenance badges (SAMPLE / INJECTED·DEMO / YOUR LOG / LIVE WAZUH) so a real alert is never confused with demo data.

Tracks and badges

Tagged in the YAML above to be considered for:

  • build-small (base) and backyard-ai (track): a local-first tool for the solo operator working in their own backyard, no cloud, no data leaving the box.
  • off-the-grid: no cloud APIs, the whole pipeline runs on CPU on the box in front of you.
  • well-tuned: ships a fine-tuned explainer published on the Hub (Mroqui/TinySOC-Qwen2.5-3B).
  • off-brand: a hand-built dark SOC-console theme with custom panels, radar, and heatmaps, not a default Gradio look.
  • llama-champion: the model runs through the llama.cpp runtime (llama-cpp-python).
  • field-notes: a full write-up of the design bets and the things that bit me, see FIELD_NOTES.md.
  • tiny-titan: a small (≤4B) model does load-bearing work, token-level perplexity highlighting plus plain-English triage, not a giant model in disguise.
  • best-demo: app, narrated demo video, and social post, a live dashboard that learns a baseline, flags an injected attack, and explains it in seconds.

Run locally

pip install -r requirements.txt
python app_tinysoc.py

On first launch the models download automatically: a small base model for the perplexity highlighter and a small instruct model for the explanation.

Configuration (environment variables)

Variable Default Role
WEC_BACKEND llamacpp Inference backend: llamacpp (self-contained) or ollama (dev)
WEC_PPL_REPO / WEC_PPL_FILE mradermacher/Qwen2.5-Coder-1.5B-GGUF / Qwen2.5-Coder-1.5B.Q4_K_M.gguf Highlighter base GGUF (perplexity)
WEC_PPL_MODEL_PATH - Explicit local path for the highlighter GGUF
WEC_MODEL_REPO / WEC_MODEL_FILE Mroqui/TinySOC-Qwen2.5-3B / TinySOC-Qwen2.5-3B-Q5_K_M.gguf Explainer GGUF
WEC_MODEL_PATH - Explicit local path for the explainer GGUF
WEC_N_CTX / WEC_PPL_N_CTX 4096 / 1024 Context size (explainer / highlighter)
WEC_N_GPU_LAYERS 0 GPU layers (set -1 on a GPU grant)
WEC_FLAG_THRESHOLD 0.6 Baseline score above which a line is enriched
WEC_OLLAMA_URL http://localhost:11434 Ollama server (dev backend only)
WEC_WAZUH_CONTAINER wazuh-wazuh.manager-1 Wazuh manager Docker container to tail (Docker live source)
WEC_ALERTS_PATH /var/ossec/logs/alerts/alerts.json Path to alerts.json inside that container
WEC_ALERTS_FILE - Tail this local alerts.json directly, no Docker (file live source)
WEC_ALLOW_UI_SOURCE false Allow choosing the live source from the UI. Keep off on a shared/public deployment (see security note)
WEC_TZ America/Toronto Local time zone for displayed timestamps

Dev: point at a remote Ollama server (WEC_BACKEND=ollama + WEC_OLLAMA_URL=http://<host>:11434) to skip CPU model loads, see .env.example. Public Space: keep the llamacpp default; a Space cannot reach a private IP.

Live feed sources

The live feed reads new Wazuh alerts as they happen. Two interchangeable transports read the same Wazuh alerts.json schema, so pick whichever fits your deployment:

  • Docker (default): tails the Wazuh manager container. If your container has a different name, set WEC_WAZUH_CONTAINER (and WEC_ALERTS_PATH if the alert file lives elsewhere).
  • Plain file (no Docker): set WEC_ALERTS_FILE to a local alerts.json. This fits a native (non Docker) Wazuh install, or a setup that mounts or ships its alerts.json onto the host running TinySOC.

The app auto-detects the source: if neither a reachable container nor a readable file is found (for example on a public Space), the live tab is hidden and the other inputs stay fully usable. The on-demand "Generate live event" injection works in Docker mode only; in file mode the upstream pipeline is not ours to write to, so that button is inert.

Format scope. The live tab is Wazuh specific: it expects Wazuh alert JSON and reads severity from rule.level. Other sources (Zeek, Suricata eve.json, Security Onion, an Elasticsearch or OpenSearch index) use different schemas and are not wired into the live tab. Feed those through paste or upload instead: the baseline plus perplexity engines run on raw log text and are format agnostic, they just score from the learned baseline rather than a vendor severity field. Native live support for those sources would mean adding a parser per format, which is out of scope here.

Security note. Choosing the live source from the UI is gated by WEC_ALLOW_UI_SOURCE and is off by default. On a shared or public deployment this prevents a visitor from pointing the file tail at arbitrary local files and reading them through the feed. With the gate off, the app only reads the source the operator configured via env vars; the UI fields are shown read-only. Enable the gate only when you self-host and trust who can reach the UI.

Why we read logits directly, not echo logprobs. llama-cpp-python's echoed prompt logprobs are misaligned in the 0.3.x line, so the highlighter loads the model with logits_all=True and reads Llama.eval() / .scores to compute each token's true conditional log-probability in one pass.

Fine-tuned explainer

TinySOC defaults to a distilled fine-tune of Qwen2.5-3B specialized for security log triage, auto-downloaded from the Hub (Mroqui/TinySOC-Qwen2.5-3B). To override it, point WEC_MODEL_PATH (a local GGUF) or WEC_MODEL_REPO/WEC_MODEL_FILE at another model.

Limits we own

  • The deterministic baseline carries detection; the LLM only explains and is a suggestion engine, not a verdict. Pure novelty detection produces false positives on real Windows fleets (benign-but-rare events), so live detection is shown on a controlled feed, with badged injection to demonstrate a catch.
  • Bundled examples are anonymized (TEST-NET IPs, fake hostnames). Never paste real infrastructure data or PII into a public Space.