DEV Community

Cover image for GhostWatch: Making the Ghost in Your Machine Visible — with Local AI
Praveen Kumar
Praveen Kumar

Posted on AI-assisted

GhostWatch: Making the Ghost in Your Machine Visible — with Local AI

Hacktoberfest Open-Source AI Challenge: Ghost in the Machine Submission 👻🌼

This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 4, Theme: Ghost in the Machine.


What I Built

GhostWatch is an open-source, local-first AI investigation tool that helps you understand unusual computer activity through a clear timeline, evidence-backed findings, and plain-language explanations — without sending a single byte to a cloud API.

Your computer is constantly doing things in the background: processes starting, log entries being written, system events firing. Most of it is normal. Some of it isn't. But raw logs and process lists are unreadable noise for most people.

GhostWatch turns that noise into a transparent investigation report: what happened, what looks unusual, what the evidence supports, and what remains unknown.

GitHub logo praveen-pravi6B / ghostwatch

Local-first AI investigation tool for unusual computer activity. Hacktoberfest 2026.

GhostWatch

Local-first AI investigation tool for unusual computer activity.

GhostWatch collects process snapshots and OS log events, applies deterministic rules to identify interesting sequences, then asks a locally-running open-weight model to explain what the evidence shows — without sending a single byte to a cloud API.

Hacktoberfest 2026 — Ghost in the Machine challenge entry


What it does

  1. Collects process snapshots (via psutil) and OS log events (journalctl on Linux, log show on macOS).
  2. Normalises everything into a shared event schema and stores it in a local SQLite database.
  3. Applies rules deterministically: temp-directory processes, interpreter download patterns, hidden process names, high-severity log entries.
  4. Redacts sensitive fields (tokens, passwords, API keys) before any AI inference.
  5. Asks a local Ollama model to explain the evidence, express uncertainty, and suggest manual follow-up steps.
  6. Displays deterministic findings and AI explanations separately — facts stay facts.

GhostWatch is a lightweight investigator, not…


The Theme Connection

The ghost in the machine is a perfect metaphor for what GhostWatch does. Your computer runs dozens of background processes you never see: processes launched from temporary directories, interpreters making network connections, privilege escalations happening in /tmp. This activity is the literal ghost in the machine — invisible, difficult to interpret, and easy to miss.

GhostWatch makes it visible. And it keeps the analysis on your own machine.


Demo

Activity timeline — real macOS process data

GhostWatch activity timeline showing real macOS processes

28 new processes observed since the last snapshot. The incremental collector only shows what changed, not the whole system every time.

AI investigation — qwen2.5:1.5b running locally

GhostWatch investigation panel with AI explanation, uncertainty disclosure, and suggested manual checks

The AI correctly identifies gh as GitHub CLI, wdk as Automox remote management, and mdmdmclient as macOS MDM — all from local inference with no internet connection.


How It Works

The pipeline is deliberately simple and auditable:

  1. Collect native OS telemetry via psutil and journalctl / log show
  2. Normalize everything into a shared Pydantic v2 event schema
  3. Store locally in SQLite with deduplication
  4. Apply deterministic rules — no AI involved yet
  5. Redact credentials and tokens before building the prompt
  6. Ask the local model to explain the evidence
  7. Display rule findings and AI output in separate UI sections

Deterministic rules come first. Four rules in the MVP:

Rule What it flags
PROC_TEMP_DIR Process launched from /tmp, /var/tmp, or Downloads
PROC_HIDDEN_NAME Process name starts with a dot
INTERP_NETWORK_CMDLINE Python/bash/ruby with network URLs in command line
HIGH_SEVERITY_LOG Log entries already rated high by the OS

The AI explains findings in plain language — it cannot override severity or declare the system compromised. That separation stays enforced in the UI.


Stack

Layer Technology
Backend Python 3.11 + FastAPI
Validation Pydantic v2
Storage SQLite via aiosqlite
Process data psutil
Linux logs journalctl --output=json
macOS logs log show --style=json
Local AI Ollama with qwen2.5:1.5b
Dashboard Vue 3 + Vite

Why Local Open-Weight AI

System logs contain sensitive information: process names, command-line arguments, usernames, file paths, error messages. Sending this to a hosted AI service means your logs leave your machine, a third-party retains them, and you have no control over what happens next.

With an open-weight model running in Ollama, none of that happens. The model runs at 127.0.0.1. The API is bound to localhost. GhostWatch validates the configured URL at startup and refuses to connect to anything that is not a local address:

def __init__(self, base_url: str, model: str, timeout: float = 120.0) -> None:
    if not base_url.startswith(("http://127.0.0.1", "http://localhost")):
        raise ValueError(
            f"AI base_url must be a localhost address, got: {base_url!r}. "
            "GhostWatch never connects to remote AI endpoints."
        )
Enter fullscreen mode Exit fullscreen mode

This is not a config flag. It is enforced in code. You can read it. You can fork it.

This is what open-source AI makes possible: you can inspect the guarantee, not just trust a privacy policy.


Redaction Before Inference

Even with a local model, log content can contain credentials — tokens, passwords, API keys embedded in command-line arguments. GhostWatch redacts before building the prompt:

_SECRET_PATTERNS = [
    re.compile(r"(?i)(password|passwd|secret|token|api[_-]?key)\s*[=:]\s*\S+"),
    re.compile(r"Bearer\s+[A-Za-z0-9\-._~+/]+=*"),
    re.compile(r"sk-[A-Za-z0-9]{20,}"),
]
Enter fullscreen mode Exit fullscreen mode

Dict keys are also checked by name. An api_key field gets its value replaced regardless of what the value looks like. The original events in SQLite are never modified; redaction happens on a copy immediately before the AI request is built.


Running It

git clone https://github.com/praveen-pravi6B/ghostwatch
cd ghostwatch
bash scripts/setup_dev.sh

# Sample mode — no privileges required
GHOSTWATCH_SAMPLE_MODE=true .venv/bin/ghostwatch

# In a second terminal
cd frontend && npm run dev
# Open http://localhost:5173
Enter fullscreen mode Exit fullscreen mode

For AI explanations:

ollama pull qwen2.5:1.5b
GHOSTWATCH_MODEL=qwen2.5:1.5b GHOSTWATCH_SAMPLE_MODE=true .venv/bin/ghostwatch
Enter fullscreen mode Exit fullscreen mode

Live mode on real OS telemetry:

GHOSTWATCH_MODEL=qwen2.5:1.5b .venv/bin/ghostwatch
Enter fullscreen mode Exit fullscreen mode

Build Session

Here is the full Claude Code session where GhostWatch was built — from an empty directory to a working MVP in two days:

Building GhostWatch: Local-First AI Security Investigation Tool from Scratch
You

@GhostWatch_Planner.md — build this from scratch. Today is October 29, Hacktoberfest 2026 Ghost in the Machine challenge, deadline October 31.

Agent

No ghostwatch directory exists yet — starting from scratch. Building Day 1 foundation and Day 2 collection in one pass.

You

run tests

Agent

40/40 tests passing, Ruff clean. All platform detection, adapter mocking, redaction, rules, schema, and storage tests green.

You

frontend vue js

Agent

Switching from Astro+React to Vue 3 + Vite. Building the dashboard.

You

start it up — run ollama — use qwen2.5:1.5b

Agent

Both servers running. UI at http://localhost:5173, API at :8765. Ollama ready with qwen2.5:1.5b.

You

run without sample mode — show real macOS data

Agent

Live investigation working. qwen2.5:1.5b correctly identifies real processes on the system and discloses uncertainty appropriately.

You

validate first — run all acceptance criteria

Agent

Running all 12 acceptance criteria from the planner.

All 12 acceptance criteria pass. Ready for submission.

You

let's finish and draft dev.to submission today

Agent

Drafted DEV post, created GitHub repo praveen-pravi6B/ghostwatch, set up branch ruleset with admin bypass, pushed all commits as praveen-pravi6B, added LICENSE/CONTRIBUTING/issue templates/PR template/dependabot/CI matching pixelbreak repo structure.


Limitations

GhostWatch is a lightweight investigator, not a security product.

  • Process polling misses short-lived processes. A process that starts and exits between two snapshots will not appear.
  • macOS Unified Logging applies privacy redactions. Some fields appear as private. GhostWatch notes this in the capability status.
  • journalctl requires systemd. Non-systemd Linux distributions see the journal listed as unavailable.
  • No kernel-level tracing. No eBPF, no kernel module, no EDR. Lightweight observer only.
  • Small models make small mistakes. qwen2.5:1.5b is fast on CPU but not infallible. Deterministic rules are the authority on severity, not the AI.

Why Open Innovation Matters Here

Security tooling is one of the last places you should trust a black box.

If a tool explains why a process looks suspicious, you should be able to read the rules that produced that finding. If a tool redacts credentials before AI inference, you should be able to verify that it actually does. If a tool claims it does not send your logs anywhere, you should be able to confirm that in the source.

GhostWatch works with the internet off. It works when hosted services change their pricing. It works in environments where sending system logs to a third party is simply not acceptable.

That is not a marketing claim. It is the architecture.


What's Next

  • Live collection loop with configurable intervals
  • More detection rules: unusual parent-child relationships, processes on unexpected ports
  • Ubuntu CI matrix
  • Baseline snapshots to flag new processes not seen in a previous run

Built with Python, FastAPI, Vue 3, Pydantic v2, psutil, aiosqlite, and Ollama. Tested on macOS. Sample mode works without OS privileges or a running Ollama instance. 40 tests, Ruff clean.

Top comments (0)