Local network anomaly monitor for macOS. Detects suspicious outbound connections using a local LLM with tool calling, RAG short-term memory, a native menu bar agent, and a dark-theme web review panel — everything runs on-device, no cloud required.
lsof (60s) ──► anomalies.log ──► analyze.py (5 min) ──► notifications
│ │
embed + RAG lookup panel UI
(nomic-embed-text) localhost:6543
│
Ollama LLM (tool calls)
granite4.1:3b / llama3.2 / …
├─ send_notification → queues for review
├─ auto_resolve → autonomous confirm/reject
└─ mark_as_normal → adds to baseline
| Component | What it does |
|---|---|
monitor.sh |
Polls lsof -i 4 every 60 s, diffs against baseline.txt, writes [ANOMALY] lines |
analyze.py |
Reads new anomalies, embeds with Ollama, retrieves similar past events (RAG), calls LLM |
db.py |
SQLite event store with in-process cosine-similarity vector search |
embed.py |
Thin wrapper around POST /api/embed — model configurable |
panel.py |
Dark-theme HTTP review panel at localhost:6543 |
MenuBar/ |
Swift menu bar app — ⚡ N badge, inline confirm/reject, mode toggle |
build.sh |
Compile Swift app + reload LaunchAgents |
install.sh |
One-shot first-run setup |
- macOS 13 Ventura or later
- Ollama (at least one tool-capable model)
- Python 3.10+ — Homebrew recommended (
brew install python) - Xcode Command Line Tools —
xcode-select --install
git clone https://github.com/Algiras/netmon ~/.netmon
bash ~/.netmon/install.shinstall.sh:
- Checks dependencies (python3, ollama, xcrun)
- Installs Python packages (
anthropic,mcp) - Pulls
granite4.1:3bandnomic-embed-text-v2-moefrom Ollama - Writes LaunchAgent plists to
~/Library/LaunchAgents/ - Builds the Swift menu bar app and loads all agents
After install you'll see ⚡ in your menu bar and the panel at http://localhost:6543.
The panel API is token-protected. The token is auto-generated at ~/.netmon/panel_token on first run — the menu bar app reads it automatically. For curl access use $(cat ~/.netmon/panel_token).
netmon uses two separate models, both configurable from the panel:
| Role | Default | Config key |
|---|---|---|
| LLM (analysis + tool calls) | granite4.1:3b |
llm_model |
| Embedding (RAG memory) | nomic-embed-text-v2-moe |
embed_model |
The panel's model bar shows only models that support the required capability (tools for LLM, embedding for vectors). Any Ollama model works.
Note: Changing the embedding model clears all stored embeddings — the panel shows a confirmation dialog before doing so. Vectors computed with different models are incompatible.
Switch via API:
TOKEN=$(cat ~/.netmon/panel_token)
# Change LLM
curl -X POST http://localhost:6543/config \
-H "Host: localhost:6543" -H "X-Netmon-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"llm_model": "llama3.2:3b"}'
# Change embedding model (clears stored vectors)
curl -X POST http://localhost:6543/config \
-H "Host: localhost:6543" -H "X-Netmon-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"embed_model": "nomic-embed-text:latest", "_clear_embeddings": true}'Tested LLMs: granite4.1:3b · llama3.2:3b · qwen3.5:2b · gemma4:31b-cloud · mistral-large-3:675b-cloud
| Mode | Behaviour |
|---|---|
| Review (default) | LLM flags events → you confirm/reject via notification buttons or panel |
| Autonomous | LLM calls auto_resolve directly — benign traffic confirmed, suspicious rejected, no human step |
Toggle via ⚡ menu bar → 👁 Review Mode / 🤖 Autonomous: ON, or the green button in the panel.
The LLM is given three tools:
| Tool | When used |
|---|---|
send_notification |
Suspicious event requiring human review — creates a pending alert |
auto_resolve(decision="confirmed"|"rejected") |
Autonomous mode — direct decision, no queue |
mark_as_normal |
Clearly routine connection not yet in baseline — adds silently |
No Ollama required — all network calls are mocked.
cd ~/.netmon && python3 -m pytest tests/ -v
# 31 passedbash ~/.netmon/build.sh~/.netmon/anomalies.log # raw lsof detections
~/.netmon/analysis.log # LLM decisions and summaries
~/.netmon/menubar.err # Swift app crash log
~/.netmon/panel.log # HTTP access log
GitHub Actions builds a signed DMG on every push to main (artifact) and on version tags (GitHub Release).
git tag v1.0.0 && git push --tagsThe DMG contains NetmonMenuBar.app with a drag-to-/Applications installer layout.