Important
This is a personal-hardware security console, not a hardened EDR product. Sentinel was built to explore how much real endpoint visibility, detection, and response tooling can live in a small native GPU-rendered app instead of an Electron shell plus a heavyweight agent. It has not been audited, is Windows-only for every sensing/response feature, and its response actions (kill / block / quarantine) touch the live system. Read Security Model & Limitations before pointing it at anything you'd be upset to lose.
- What is Sentinel?
- Why This Project Exists
- Feature Matrix
- Architecture
- Detection Pipeline
- MITRE ATT&CK Mapping
- Response / IPS Actions
- Hardware Budget & Design Constraints
- Tech Stack & Rationale
- Repository Structure
- Getting Started
- Security Model & Limitations
- Verified Findings From Code Review
- Extending This
- Roadmap
- Contributing
- License
- Acknowledgements
Sentinel is a small, native, GPU-accelerated security console for Windows (with a cross-platform UI shell that also runs on Linux). It's built on a from-scratch app shell — GLFW for windowing/input, wgpu-native for rendering (WebGPU → D3D12/Vulkan) — with no Electron, no Chromium, no garbage-collected runtime anywhere in the render path.
On top of that shell sits an actual endpoint-security stack:
Process/Network monitors ─┐
Wi-Fi/Bluetooth/USB polls ├──▶ EventQueue ──▶ SQLite store ──▶ RuleEngine ──▶ Alerts
Raw-socket packet capture ─┘ │ │
▼ ▼
Response Executor ATT&CK mapping
(kill / block / quarantine) + local AI analyst
Twelve navigation panels — Dashboard, Processes, Network, Packets, Wi-Fi, Bluetooth, USB, Alerts, Response, ATT&CK, AI Analyst, Settings — are all backed by real data paths, not placeholder screens.
Most consumer-facing security tools either ship as a heavyweight Electron app (a second Chromium process idling in the background) or as a cloud-tethered agent that assumes always-on bandwidth and a SOC on the other end. Neither fits a single laptop with a fixed RAM budget that also wants to run a local LLM on demand.
Sentinel starts from the opposite constraint: what does real telemetry, detection, and response look like when the UI has to cost almost nothing at idle, so that budget can go to a 7B-parameter model instead? That question shapes nearly every decision in this repository — from the immediate-mode UI renderer, to SQLite and GLFW being built from source rather than installed, to raw sockets instead of a Npcap driver, to an AI analyst that's explicitly a button, never a background daemon.
Every row below was confirmed directly against the source in src/ — nothing here is aspirational.
| Feature | Where | Platform |
|---|---|---|
| GPU-rendered UI shell (GLFW + wgpu-native, immediate-mode rects/text, no UI toolkit) | src/window/, src/gpu/, src/ui/ |
Windows + Linux |
| Process telemetry (start/stop diffing, pid-reuse-safe) | src/agent/process_monitor_win.h |
Windows only |
| Network connection telemetry (TCP/UDP owner-PID tables) | src/agent/network_monitor_win.h |
Windows only |
| SQLite-backed event store (WAL mode, batched transactions) | src/store/event_store.cpp |
Windows + Linux |
| Rule-based detection engine, 4 rules, 5-min per-(rule,pid) cooldown | src/detection/rule_engine.cpp |
Windows + Linux (logic), Windows (data source) |
| MITRE ATT&CK reference panel (~33 curated techniques, per-rule tagging) | src/attack/attack_knowledge.h |
Windows + Linux |
| On-demand LAN discovery (ICMP sweep → ARP → common-port check, no Nmap) | src/network/network_scanner_win.h |
Windows only |
Raw-socket IP packet capture (SOCK_RAW, no Npcap) |
src/network/packet_capture_win.h |
Windows only |
| Process CPU% / RAM / thread-count sampling | src/agent/process_monitor_win.h |
Windows only |
| Wi-Fi monitoring (current connection + nearby networks, WLAN API) | src/peripherals/wifi_monitor_win.h |
Windows only |
| Bluetooth monitoring (background poll + on-demand radio inquiry) | src/peripherals/bluetooth_monitor_win.h |
Windows only |
| USB device-tree monitoring (SetupAPI) | src/peripherals/usb_monitor_win.h |
Windows only |
| Response actions: kill process / block IP / quarantine file, two-step confirm, full audit log | src/response/response_executor_win.h |
Windows only |
| On-demand local AI analyst over Ollama (WinHTTP, no libcurl) | src/ai/ollama_client_win.h |
Windows only |
| Live-adjustable, store-persisted Settings (poll intervals, scan range, AI mode) | src/store/event_store.cpp (settings table) |
Windows + Linux |
| Process/Alert drill-down detail views | src/main.cpp |
Windows + Linux |
Not yet implemented for Linux: src/agent/agent.h compiles to a documented no-op on any non-Windows target — the UI shell, store, and rule engine all run, but there's no telemetry to feed them. The seam for a process_monitor_linux.h/network_monitor_linux.h pair already exists (#elif __linux__), it just isn't filled in yet.
Window (GLFW) ──creates──▶ GpuContext (wgpu-native / D3D12 or Vulkan)
│
▼
UiRenderer (immediate-mode rects + text)
│
▼
src/main.cpp (12 nav panels, AppState)
┌───────────────────┼────────────────────┐
▼ ▼ ▼
src/agent/ src/network/ src/peripherals/
(process/network poll) (scan, packet capture) (Wi-Fi/BT/USB poll)
│ │ │
└─────────► EventQueue ◄───────────────────┘
│
▼
src/store/event_store.cpp (SQLite)
│
▼
src/detection/rule_engine.cpp
│ │
▼ ▼
src/response/ src/attack/
(kill/block/quarantine) (ATT&CK tagging)
│
▼
src/ai/ollama_client_win.h
(on-demand, local, no daemon)
- Window (
src/window/) — GLFW window + input callbacks (resize, key, mouse move/button, scroll, close). - GpuContext (
src/gpu/) — one wgpu-native instance/adapter/device/surface bound to the window; owns the render-pass lifecycle. - UiRenderer (
src/ui/) — one solid-color-triangle pipeline, a growable vertex buffer, and three primitives (DrawRect,DrawLabelvia a compile-time 5×7 bitmap font,Button) — enough to build the full sidebar/titlebar/content/status-bar layout without a UI toolkit dependency. - Agent (
src/agent/) — process and network monitors, each a background thread doing a poll-and-diff loop into one sharedEventQueue.main.cppdrains it once per frame. - EventStore (
src/store/) — SQLite (vendored amalgamation), WAL mode,synchronous=NORMAL, one transaction per drained batch — not an fsync per event. - RuleEngine (
src/detection/) — polls whatever's new in the store on a fixed timer and emitsAlertrows for matches. - Response executor (
src/response/) — the only module that mutates the live system; re-validates every target itself rather than trusting the UI (see Response / IPS Actions). - AI analyst (
src/ai/) — talks to a local Ollama server over WinHTTP; builds a small structured prompt from one alert's fields, not raw logs or the full event database.
RuleEngine::Tick() runs on a 4-second timer against whatever's new in the event store, with a 5-minute cooldown per (rule, pid) so an ongoing condition doesn't spam the Alerts panel every tick. Four rules, each with a real reason to exist:
| Rule | Fires on | Severity |
|---|---|---|
SUSPICIOUS_PARENT_CHILD |
A script host (powershell.exe, cmd.exe, mshta.exe, regsvr32.exe, rundll32.exe, wscript.exe/cscript.exe) spawned by Office/a browser/a PDF reader — the classic macro-malware pattern |
Varies by binary |
UNRESOLVED_OWNER_CONNECTION |
A TCP row GetExtendedTcpTable couldn't attribute to a PID |
Low |
SUSPICIOUS_PORT |
A connection to a curated list of ports with a known malware/C2 association (common dev ports deliberately excluded) | Medium |
HIGH_CONNECTION_RATE |
One process reaching 12+ distinct remote hosts inside a minute | High |
Verified with a standalone test that inserts a simulated winword.exe → powershell.exe chain, a connection to a known C2 port, and a 12-host burst into a real SQLite database, and confirms all three fire while a benign Chrome connection doesn't, and the cooldown suppresses an immediate repeat.
Not a synced copy of the ATT&CK framework — a curated ~33-technique reference list (src/attack/attack_knowledge.h) spanning most tactics, with per-rule tagging so an alert's detail view and a dedicated ATT&CK panel show which technique it maps to and how much of the reference list this app can currently detect. The full ATT&CK Enterprise matrix has 600+ techniques distributed as a multi-megabyte STIX corpus — embedding that for four detection rules would be the kind of heavy dependency this project avoids everywhere else.
Mapping is done honestly rather than force-fit:
SUSPICIOUS_PARENT_CHILDmaps per spawned binary — PowerShell/cmd map to Execution (T1059.001 / T1059.003), while Mshta/Regsvr32/Rundll32 map to Defense Evasion (T1218.005/.010/.011), since ATT&CK itself splits these across tactics.SUSPICIOUS_PORT→ T1571 (Non-Standard Port) — a direct, confident fit.HIGH_CONNECTION_RATE→ T1071 (Application Layer Protocol) — flagged in code as a heuristic best-fit, not a precise signature match.UNRESOLVED_OWNER_CONNECTIONgets no technique at all — it's a visibility gap in the tool itself, not observed adversary behavior, and tagging it just to fill the field would be a false claim of precision.
Verified with an integration test running synthetic attack chains through the real RuleEngine against real SQLite and confirming each alert's attack_technique field lands exactly where the mapping says — including that a Word→Mshta chain lands in a different tactic than an Excel→PowerShell chain, and that the unattributed-connection alert's technique field comes back empty rather than guessed.
The one part of this app that acts on the live system, built with that taken seriously:
- Every action requires a two-step confirm, app-wide, via a
ConfirmButtonhelper — first click arms it (relabels, tints red), second click within 8 seconds executes it. There is no single click anywhere in this codebase that kills a process, blocks an IP, or moves a file. - The executor re-validates every target itself (
response_executor_win.h) rather than trusting the UI — a process's name is re-resolved from the live process at kill time, checked against a denylist of core OS processes (csrss.exe,lsass.exe,services.exe,svchost.exe, this app's own process) plus pid ≤ 4. Quarantine refuses any path insideSystem32/SysWOW64/WinSxS. - Kill/Block/Quarantine are wired into an alert's detail view, grounded in data the rule engine already populated (
target_ip/target_path), not invented UI state. - Block IP and Quarantine File are reversible; Kill Process isn't. Blocking uses
netsh advfirewall(ships with Windows) rather than the COM-based WFP API. Quarantine moves the file into this app's own local-data folder with a.quarantinedsuffix. - A RESPONSE nav panel is the full audit log — every attempted action is recorded whether it succeeds, fails, or gets refused by a safety check, with reversible entries getting their own Unblock/Restore button.
- Deliberately no automatic mode. Every action is a human decision made through a confirm button.
Validation logic (IPv4 format checks, including shell/SQL-injection-shaped strings like "1.2.3.4; rm -rf /"; the protected-process-name set; the protected-path-prefix check) is extracted and tested directly, not just eyeballed — along with a schema-migration test confirming EventStore::EnsureColumn correctly backfills target_ip/target_path on a database created before this feature existed.
Sentinel was built against a specific machine (11th-gen i5-1135G7, 11.7GB RAM, NVMe, no discrete GPU), and that budget shapes the whole roadmap, not just the UI framework choice:
- The shell itself: a few MB resident, no GC pauses, presentation paced by vsync (
WGPUPresentMode_Fifo) — leaves essentially the whole RAM budget free. - Local models via Ollama:
qwen2.5:7b(~4.7GB at Q4) for real investigations,llama3.2:3b(~2GB at Q4) for cheap triage. Never both loaded at once; never run as a background daemon — loaded on demand from the AI Analyst panel, unloaded by Ollama's own idle timeout. - Endpoint agent: polling/eventing, not continuous packet capture — no headroom for Suricata/Zeek-style full inspection running 24/7 alongside an LLM.
- Network scanning and packet capture: strictly button-triggered, never a background timer, and only intended for networks/devices you own or are authorized to test.
| Layer | Choice | Why |
|---|---|---|
| Windowing/input | GLFW 3.4 | Fetched and built from source via CMake FetchContent — nothing to preinstall |
| Rendering | wgpu-native (WebGPU → D3D12/Vulkan) | One pipeline, direct GPU access, no browser engine in the loop |
| UI | Hand-rolled immediate-mode renderer | No per-frame allocations beyond one growable vertex buffer; no text-shaping at runtime |
| Persistence | SQLite (vendored amalgamation) | Built from source as its own static lib; WAL mode + batched transactions |
| Network scanning | Raw ICMP/ARP/Winsock calls | Avoids a Nmap/Npcap install dependency |
| Packet capture | Windows raw sockets (SOCK_RAW + SIO_RCVALL) |
Native IP-layer capture, no kernel driver |
| Local AI | Ollama over WinHTTP | Built-in Windows HTTP stack, no libcurl to vendor |
| Peripherals | WLAN API / classic Bluetooth API / SetupAPI | Native Win32, no WMI/COM round trips |
Sentinel/
├── CMakeLists.txt # GLFW fetched, SQLite vendored & built, wgpu-native linked
├── README.md
├── src/
│ ├── main.cpp # UI shell: nav, all 12 panels, AppState
│ ├── window/ # GLFW window + input
│ ├── gpu/ # wgpu-native device/surface/render-pass
│ ├── ui/ # Immediate-mode renderer + bitmap font
│ ├── agent/ # Process/network monitors (Windows), EventQueue
│ ├── network/ # LAN scanner, raw-socket packet capture (Windows)
│ ├── peripherals/ # Wi-Fi / Bluetooth / USB monitors (Windows)
│ ├── detection/ # RuleEngine (4 rules, cooldown logic)
│ ├── attack/ # Curated MITRE ATT&CK reference + technique struct
│ ├── response/ # Kill/Block/Quarantine executor + safety checks
│ ├── ai/ # Ollama client (WinHTTP) + lightweight JSON extractor
│ └── store/ # SQLite-backed EventStore (events/alerts/settings/responses)
└── third_party/
├── sqlite/ # Vendored SQLite amalgamation
└── wgpu_native/ # wgpu-native headers (binary downloaded separately)
- Download the wgpu-native binary for your platform and drop it in
third_party/wgpu_native/lib/— seethird_party/wgpu_native/README.mdfor exact filenames and the release link. - GLFW is fetched and built from source automatically — nothing else to install on Windows.
- On Linux, install the X11 development headers GLFW's build needs:
sudo apt install libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libgl1-mesa-devWarning
CMakeLists.txt currently defaults WGPU_NATIVE_LIB_DIR to a hardcoded personal path (see Verified Findings). Override it explicitly on your own machine — don't rely on the default.
cmake -B build -DCMAKE_BUILD_TYPE=Release -DWGPU_NATIVE_LIB_DIR=/path/to/wgpu-native/lib
cmake --build build --config ReleaseRun the sentinel (or sentinel.exe) binary produced in build/.
The UI shell builds and runs on Linux, but process/network telemetry, the LAN scanner, packet capture, Wi-Fi/Bluetooth/USB monitoring, response actions, and the AI analyst are all Windows-only today (see Feature Matrix). For the AI Analyst panel specifically, you'll also need Ollama running locally with at least one of qwen2.5:7b / llama3.2:3b pulled.
- ❌ Not an audited or certified EDR/IPS product
- ❌ Not resilient against an attacker with SYSTEM/kernel-level access — it's user-mode telemetry, same trust boundary as Task Manager
- ❌ Not a promiscuous network tap — packet capture sees only this host's own traffic, and requires Administrator
- ❌ Not a live-synced ATT&CK matrix — the reference list is a checked-but-static snapshot (~33 of 600+ techniques)
- Linux telemetry doesn't exist yet.
Agent::available()returnsfalseon non-Windows builds; the Dashboard is honest about this rather than showing a silently empty view. - Packet capture is IP-layer only — no Ethernet/802.11 frames, no ARP, no non-IP protocols, and results are an in-memory 300-packet rolling buffer, never persisted.
- Bluetooth monitoring is classic Bluetooth only — BLE-only devices need the WinRT
Windows.Devices.BluetoothAPIs, a materially bigger integration left as a documented gap. - CPU% is unclamped past 100% (matching Task Manager's convention, one core fully busy = 100%) — a 4-core box can show ~400% for one process, by design, not a bug.
- No automatic response mode. Every kill/block/quarantine requires a human two-step confirm; there is deliberately no unattended mode in this codebase.
Concrete observations from reading the actual source — not general prototype disclaimers.
CMakeLists.txthardcodes a personal Windows path as the default forWGPU_NATIVE_LIB_DIR(C:/Users/<username>/Downloads/Project/...). This will silently fail to configure on anyone else's machine unless overridden with-DWGPU_NATIVE_LIB_DIR=..., and it's a minor local-path disclosure worth scrubbing before treating this as a public template.- No
.gitignoreships with the project, so a freshcmake -B buildwill happily let build artifacts get tracked if committed carelessly. - No CI configuration exists — there's no automated compile/test gate on changes.
third_party/sqlite/andthird_party/wgpu_native/are vendored (a full SQLite amalgamation and wgpu-native headers) — both are permissively licensed (public domain and MIT/Apache-2.0 respectively), which is compatible with this repo's MIT license, but worth double-checking against the versions actually vendored here if that ever matters for your use case.
- New screens: replace/extend
DrawFrame()insrc/main.cpp—DrawSidebar/DrawTitleBar/DrawContent/DrawStatusBarare just functions callingUiRenderer; copy the pattern. - Reskinning: every color lives in the
palettenamespace at the top ofmain.cpp. - More UI primitives: add methods to
UiRendereralongsideDrawRect/DrawLabel/Button— they all push vertices into the same buffer. - A real UI toolkit instead: once you outgrow immediate-mode rects (scrolling lists, text input, images),
GpuContextalready exposes the rawdevice()/queue()/surface_format()a Dear ImGui WebGPU backend needs. - Linux telemetry: implement
process_monitor_linux.h/network_monitor_linux.h(e.g./procdiffing +/proc/net/tcp) with the sameStart()/Stop()shape and wire intosrc/agent/agent.h's#elif __linux__—main.cppdoesn't need to change. - macOS: GLFW already supports it; add a
WGPUSurfaceSourceMetalLayerbranch togpu_context.cpp.
- Repository hygiene. Add a
.gitignoreand remove the hardcoded personal path fromCMakeLists.txt's defaultWGPU_NATIVE_LIB_DIR. - Linux telemetry. Implement the Linux process/network monitors behind the existing
agent.hseam — the biggest functional gap between platforms. - Linux network scanner / AI client. Port
network_scanner_win.handollama_client_win.hto POSIX equivalents (getifaddrs, libcurl or raw sockets). - CI. A basic compile-on-PR workflow for at least the Windows target.
- BLE support. WinRT
Windows.Devices.Bluetoothintegration alongside the existing classic-Bluetooth monitor. - Larger design directions (explicitly out of scope for the current single-host build): purple-team exercises, RAG over ATT&CK/detection-rule knowledge, cross-host telemetry aggregation.
Issues, pull requests, and design discussions are welcome. If you spot something incorrect in this README or in the security-relevant code (validation logic, the ATT&CK mapping, the response executor), please open an issue — for a security tool, an inaccurate claim in its own documentation is worth treating as a bug.
Licensed under the MIT License. Vendored third-party code (third_party/sqlite, third_party/wgpu_native) retains its own upstream license — see Verified Findings.
Built on GLFW, wgpu-native, SQLite, and Ollama. MITRE ATT&CK technique IDs and names are sourced from attack.mitre.org.