An open-source, self-hosted SIEM (Security Information and Event Management) system for website owners. Point it at your log files and get real-time threat detection, structured event storage, and a live security dashboard: all running locally on your machine.
No cloud services, no external databases, no agents to install on your server. Just your logs and Vigil.
- Architecture
- Project Structure
- Quick Start in Terminals
- Optional Terminals for Testing w/ Dummy-Site
- Connect Your Website
- Optional Backend Config
- API Endpoints
ββββββββββββββββββββββββββββββββββββββββββ
β LOCAL MACHINE β
β β
ββββββββββββββββ β ββββββββββββββββ βββββββββββββββ β
β Log Source β local β β Collector β β Classifier β β
β (log file) βββββββββββΌβ>β (watchdog) βββββ>β (severity) β β
ββββββββββββββββ tail β ββββββββ¬ββββββββ ββββββββ¬βββββββ β
β β POST /api/events/store β β
β v v β
ββββββββββββββββ β ββββββββββββββββ βββββββββββββββ β
β Deployed β POST β β FastAPI β<βββββ SQLite β β
β Website βββββββββββΌβ>β Backend βββββ>β vigil.db β β
β (Vercel) β /ingest β β :8000 β βββββββββββββββ β
ββββββββββββββββ β ββββββββ¬ββββββββ β
β β β WebSocket /ws/collector β
β VIGIL_API_URL β v β
β points here β ββββββββββββββββ β
β β β β Next.js β Dashboard, Events, β
β β β β Frontend β Alerts, Settings β
β β β β :3000 β β
β β β ββββββββββββββββ β
β β ββββββββββββββββββββββββββββββββββββββββββ
v v
βββββββββββββββββββ
β localtunnel β Public URL β localhost:8000
β (loca.lt) β npx localtunnel --port 8000
βββββββββββββββββββ
Two ingestion paths:
- Local logs (Option A): Collector tails a file on disk β parses β stores events
- Deployed site (Option B): Site POSTs raw log lines to
POST /api/ingestvia a public tunnel
| Component | Tech | Location |
|---|---|---|
| API Server | FastAPI + Uvicorn | backend/api/api_endpoint.py |
| Log Parser | grokmoment (inlined, MIT) + pygrok | backend/api/grokmoment.py |
| Classifier | Severity tagging (rule-based) | backend/api/classifier.py |
| Collector | Watchdog file tailer | backend/api/collector.py |
| Database | SQLite with WAL mode | backend/api/database.py |
| Dashboard | Next.js 16 / React 19 / Tailwind 4 | frontend/ |
| Email Alerts | Mailgun (optional) | backend/api/emailer.py |
| Dummy Site | Flask test target + traffic simulator | dummy-site/ |
- Grok pattern matching: 19 built-in patterns for syslog, auth.log, and Apache access logs
- Auto pattern learning: unrecognized log lines are sent to an LLM (OpenAI) to generate new Grok patterns automatically (optional, requires API key)
- Real-time streaming: WebSocket pushes new events to the dashboard as they arrive
- Manual log parsing: drag-and-drop or paste log content on the Log Parser page
- Severity classification: info / warning / critical tagging based on log content
- Event persistence: all parsed events stored in SQLite with 30+ structured fields
- Settings: configurable API URL, polling interval, display preferences, toast alerts
- Email notifications: Mailgun integration stub for alert emails
vigil/
βββ backend/
β βββ api/
β β βββ api_endpoint.py FastAPI server (REST + WebSocket)
β β βββ classifier.py Log parsing + severity classification
β β βββ collector.py File watcher β event pipeline
β β βββ database.py SQLite layer (init, read, write)
β β βββ emailer.py Mailgun email alerts (stub)
β β βββ grokmoment.py Inlined grok parser + LLM pattern gen
β β βββ data/
β β βββ patterns.json Active Grok patterns (grows over time)
β β βββ patterns_backup Reference patterns (19 pre-built)
β βββ dummy-logs/ Synthetic log generators
β βββ models/ (placeholder)
β βββ services/ (placeholder)
β βββ tests/ (placeholder)
β βββ env.example Environment variable template
βββ frontend/
β βββ app/
β β βββ page.tsx Log Parser (upload / paste)
β β βββ events/page.tsx Live event stream + pagination
β β βββ alerts/page.tsx Critical + warning filtered view
β β βββ health/page.tsx System health diagnostics
β β βββ settings/page.tsx User preferences
β β βββ components/ Sidebar, FileUpload, LogOutput, Toast
β β βββ contexts/ SettingsContext
β β βββ hooks/ useCollectorStream (WebSocket)
β βββ types/logs.ts TypeScript interfaces
βββ dummy-site/
β βββ app.py Flask site (generates Apache access logs)
β βββ simulate.py Traffic generator (benign + attack)
β βββ config.py Ports, log path, credentials
β βββ templates/ HTML pages
β βββ static/ CSS
βββ pyproject.toml Python dependencies
| Tool | Version | Notes |
|---|---|---|
| Python | 3.12+ | pyproject.toml requires >=3.14 but 3.12 works in practice |
| Node.js | 18+ | For the Next.js frontend |
| npm | 9+ | Bundled with Node.js |
cd backend/api
# Install Python packages
pip install fastapi uvicorn python-multipart pygrok openai watchdog requests python-dotenv
# Start the API server
python -m uvicorn api_endpoint:app --reload --host 127.0.0.1 --port 8000API: http://localhost:8000 Β· Docs: http://localhost:8000/docs
cd frontend
npm install
npm run devDashboard: http://localhost:3000
cd dummy-site
pip install flask
python app.py # starts on :5000, logs to dummy-site/logs/access.logcd backend/api
python collector.py # currently hardcoded to watch a specific file: see docs/constraints.mdcd dummy-site # separate terminal from 3rd terminal
python simulate.py --mode mixed # generates trafficNote: The simulator must run after the dummy site (Terminal 3) is up, since it sends HTTP requests to Flask on
:5000.
| Flag | Description |
|---|---|
--mode mixed |
(default) 60% benign / 40% attack, randomly interleaved |
--mode benign |
Normal browsing only (page visits, valid logins) |
--mode attack |
Malicious traffic only (see attack types below) |
python simulate.py --requests 500 # number of requests (default: 200)
python simulate.py --delay 0.1 # seconds between requests (default: 0.05)
python simulate.py --base http://127.0.0.1:5000 # target URL (default)Tip:
--requestssets the number of iterations, not individual HTTP requests. Some actions (e.g. a login flow) make 4β5 requests per iteration, so 30 iterations may produce ~75 log entries.
After the SIEM is running, there are only two supported ways to connect a real website:
If your site writes a normal access log file, point the collector at it:
cd backend/api
python collector.py /path/to/your/access.logThat is the bare minimum for a self-hosted site.
If your site is on Vercel, Netlify, or another hosted platform, the site must send raw log lines to Vigil over HTTP.
Requirements:
- Your site needs a small logger/proxy that POSTs to
POST /api/ingest. - Your site needs one website-side env var:
VIGIL_API_URL=https://your-public-vigil-backend.example.comVIGIL_API_URL is set on the monitored website, not in Vigil's backend .env.local.
Since the Vigil backend runs on your local machine, deployed sites can't reach localhost:8000. You need a tunnel to give your local backend a public URL.
localtunnel is an npm package that creates a temporary public URL (e.g. https://dry-doors-rescue.loca.lt) which forwards traffic to a port on your machine.
npx localtunnel --port 8000
# your url is: https://some-random-words.loca.lt
# on your hosting website, save this under env vars as "VIGIL_API_URL=..."Note: The URL changes every time localtunnel restarts. You must update your site's
VIGIL_API_URLenv var in Vercel (or wherever it's hosted) each time the URL changes, then redeploy.
Alternatives: ngrok (free tier gives a stable URL with an account), Cloudflare Tunnel, or deploy the backend to a server with a real domain.
These are for backend/.env.local only.
None: Vigil runs with zero configuration out of the box.
| Variable | Purpose |
|---|---|
AI_PROVIDER |
LLM backend for the voice agent: openai (default), anthropic, groq, or ollama |
OPENAI_API_KEY |
OpenAI β enables voice agent + auto Grok pattern generation |
ANTHROPIC_API_KEY |
Anthropic β enables voice agent (Claude) |
GROQ_API_KEY |
Groq β enables voice agent (Llama 3) |
OLLAMA_URL |
Ollama instance URL β enables voice agent locally (no key needed) |
ELEVENLABS_API_KEY |
ElevenLabs β enables text-to-speech for voice agent responses |
VIGIL_BACKEND_URL |
Where the Vigil frontend proxies API + WebSocket traffic when the backend runs on another host |
VIGIL_DB_PATH |
Override SQLite database location (default: backend/api/vigil.db) |
VIGIL_LOG_PATH |
Tell the collector which log file to watch (alternative to CLI arg) |
VIGIL_CORS_ORIGINS |
Comma-separated additional origins allowed to call the API (e.g. https://your-site.vercel.app) |
MAILGUN_API_KEY |
Email alert notifications via Mailgun |
MAILGUN_DOMAIN |
Mailgun sending domain |
MAILGUN_SENDER |
"From" address for alert emails |
See backend/env.example for the full template.
Create frontend/.env.local:
VIGIL_BACKEND_URL=http://localhost:8000
This is only needed if the Vigil frontend and backend are not running on the same host.
| Method | Path | Description |
|---|---|---|
GET |
/api/health |
Health check |
POST |
/api/logs/parse |
Parse log content (string body) |
POST |
/api/logs/upload |
Parse uploaded log file |
POST |
/api/ingest |
Accept raw log text from a remote site, then parse + store it |
POST |
/api/events/store |
Store parsed events in SQLite |
GET |
/api/events |
Query events (limit, offset, severity filter) |
POST |
/api/create_env |
Write .env file (for API key setup) |
POST |
/api/alerts/flush |
Force-send all pending alert digests immediately |
GET |
/api/alerts/pending |
Check queued alert count per recipient |
WS |
/ws/parse |
Stream log parsing results |
WS |
/ws/collector |
Live event stream from collector |