Entrysrc/main.cpp
ui2/index.html

Every process,
on the record.

MLPA is a monitoring tool for Windows, focused on security: a task manager on steroids. It reads live telemetry from every process on the machine and turns it into something you can act on — a score, the reasons behind it in plain words, and, when the evidence is genuinely ambiguous and you ask for it, an argument between two AI agents and a judge.

C++17 WebView2 UI Isolation Forest ETW Headless CLI GPL-3.0
32
runtime features
100
forest trees
1.5s
scan cadence
30s
model hot-swap
10
views
0
files uploaded
Collectorssrc/core/
src/etw/
src/scan/

What it sees

Task Manager tells you a process exists. MLPA asks the questions that separate a renamed backup from a loader: where does the binary really live, who does it hold handles into, and does its import table match what it claims to be?

Identity

The real image path from QueryFullProcessImageName next to the path the PEB reports, and who signed the file — embedded, catalogue or Store package. When the paths disagree, something renamed itself after launch.

core/ProcessSnapshot.cpp

Reach

Cross-process handles carrying VM_READ / VM_WRITE / VM_OPERATION into other processes — the shape an injector leaves behind. A handle to your own child is not one.

core/HandleEnumerator.cpp

Capability

The import table sorted into buckets — INJECTOR, HOOKER, KEYBOARD_INPUT, CRYPTO, SCREENSHOT — plus .text entropy to catch packing.

analysis/PeImportAnalyzer.cpp

Surface

Layered, topmost, click-through windows: an overlay you cannot see and cannot click through to. Classic cheat and credential-capture geometry.

core/OverlayDetector.cpp

Traffic

Per-process throughput straight off the Kernel-Network ETW provider, and who each process is actually connected to, with the owner of every address.

etw/NetworkMonitor.cpp

Names

The name each program asked the DNS client for, off its own ETW provider, so a connection reads gateway.discord.gg instead of a bare address.

etw/DnsMonitor.cpp

Lineage

The spawn chain. Word launching a script host launching a downloader reads very differently from Explorer launching Word.

etw/ProcessMonitor.cpp

Machine

What belongs to no single process: a download-and-run command pasted into the Run box, remote-control tools installed or connected, Remote Desktop switched on.

scan/HostChecks.cpp
Verdict pathanalysis/Classifier.cpp
ml/AnomalyDetector.cpp
ai/Tribunal.cpp

How it judges

Three layers, each more expensive than the last, each only asked when the one below it is not enough. Cheap heuristics run on everything; the AI tribunal runs on the handful of processes where a person would actually want a second opinion.

01always

Heuristic classifier

Weighted rules over every signal above, producing a score and — importantly — a plain-language reason list. You never get a number without the sentence that explains it. Signed, installed or long-familiar software has to actually do something odd to be flagged.

~0 ms/proc
local
02always

Isolation Forest

An anomaly model that learns what normal looks like on your machine — no malware samples required. Inference is pure C++; an in-process trainer keeps a rolling buffer and hot-swaps a fresh model every ~30 seconds.

100 trees · 32 feat
local
03on demand

AI Tribunal

A Prosecutor argues the process is a threat, an Advocate argues it is benign, and an Arbiter interrogates both before ruling. Only a redacted text dossier leaves the machine — never the binary — and you see its exact text before the first one goes. It needs your own API key; without one, nothing is sent.

cheap models debate
strong model judges
CLEAN INFO SUSPICIOUS ≥ 40 MALICIOUS ≥ 80
Interfaceui2/index.html
ui2/js/
ui2/css/

The interface

Two faces on one engine. Home is written for someone who has never opened Task Manager; everything technical waits behind the Advanced switch, as a workbench with the inspector docked beside the grid rather than covering it. English and Romanian, light and dark. The shots below are the real interface driven by its built-in mock engine (ui2/js/mock.js): sample data, real UI, in the theme this page is in.

MLPA's first screen: a radar and the words 'Something dangerous is running', above cards for a command pasted into the Run box, a program named Injector marked dangerous, and PowerShell marked unusual, each with its reasons and buttons. MLPA's first screen: a radar and the words 'Something dangerous is running', above cards for a command pasted into the Run box, a program named Injector marked dangerous, and PowerShell marked unusual, each with its reasons and buttons.
HomeIs this PC all right? Anything that needs you comes with its reason in plain words and a button — Stop it, Block, I trust it. No PIDs, no hashes.
The process grid led by verdict and score, with injector.exe selected and its inspector docked on the right: three findings, the chain explorer.exe to chrome.exe to injector.exe, and its identity. The process grid led by verdict and score, with injector.exe selected and its inspector docked on the right: three findings, the chain explorer.exe to chrome.exe to injector.exe, and its identity.
ProcessesEvery process with its verdict, score, signer and resources. The inspector beside it carries the findings, the chain that started it, and the actions. The filter takes unsigned, net, score>50, path:temp, verdict:susp.
The network map: apps as tiles, every address around the network that owns it, and 91.92.240.10 selected, its line to injector.exe drawn in the accent while the rest fade, and its inspector naming a hosting provider and port 4444. The network map: apps as tiles, every address around the network that owns it, and 91.92.240.10 selected, its line to injector.exe drawn in the accent while the rest fade, and its inspector naming a hosting provider and port 4444.
Network mapWho each app talks to. Click an app, a bubble, an address or a line: it is ringed, its lines light up while the rest fade, and the side pane explains it — owner, reverse DNS, the apps holding it, its ports, how long it has been there.
Network groups by owner — Microsoft, Cloudflare, Google and others — each with its apps, ports and an activity line, and one group open in its inspector listing its addresses and the apps using them. Network groups by owner — Microsoft, Cloudflare, Google and others — each with its apps, ports and an activity line, and one group open in its inspector listing its addresses and the apps using them.
GroupsThe same places as a list, gathered by who runs them, by name or by app. A group opens its own inspector with every address in it and the apps behind them.
The Activity timeline: today a suspicious PowerShell, a command pasted into the Run box, injector.exe flagged and seen for the first time, AnyDesk installed; earlier days below. The Activity timeline: today a suspicious PowerShell, a command pasted into the Run box, injector.exe flagged and seen for the first time, AnyDesk installed; earlier days below.
ActivityMLPA's own record, on disk for 30 days by default: first-time programs, flags, findings, new startup items and what you did. With Sysmon it also knows what ran while MLPA was closed. The live process-start and PowerShell feeds sit beside it.
Autoruns: five entries set to start by themselves, each with its verdict, location, state, signer, image path and the reason, led by an unsigned updater in a Temp folder. Autoruns: five entries set to start by themselves, each with its verdict, location, state, signer, image path and the reason, led by an unsigned updater in a Temp folder.
AutorunsEverything configured to start by itself — Run keys, Startup folders, services, scheduled tasks — resolved and verdicted, with the reason. A scheduled task can be switched off and back on; nothing is ever deleted.
Services: each Windows service with its state, start type, PID, account and binary. Services: each Windows service with its state, start type, PID, account and binary.
ServicesEvery Windows service in every state, not only the ones that start themselves, with the account it runs as — the column Task Manager does not have — and the process hosting it.
Performance: CPU, memory, GPU, disk and network tiles with their charts, and a system summary. Performance: CPU, memory, GPU, disk and network tiles with their charts, and a system summary.
PerformanceThe machine rather than any one process, from the PDH counters Task Manager reads. Sampled only while this view is open, because a PDH collection is not free.
Modules: collectors grouped as scanners, live activity and network, each with its switch, its state, and how many runs and items it has done. Modules: collectors grouped as scanners, live activity and network, each with its switch, its state, and how many runs and items it has done.
ModulesEvery collector and detector with its switch and what it is doing now. Off, idle, failed and unavailable are different facts and are shown as such.
The end of Settings: lists of what was marked safe or blocked, and the About card with MLPA's version, the copyright line, the GPL notice and no-warranty statement, and buttons for the licence, the licence notice and the source code. The end of Settings: lists of what was marked safe or blocked, and the About card with MLPA's version, the copyright line, the GPL notice and no-warranty statement, and buttons for the licence, the licence notice and the source code.
AboutThe notices the GPL asks an interactive program to show: who wrote it, that it comes with no warranty, and where the licence is. A modified version has to keep showing them.
Runtimesrc/app/App.cpp
src/app/Bridge.cpp

Engine

One orchestrator owns four loops. The snapshot vector sits behind a mutex; detail and hash requests ride condition-variable work queues so a slow reputation call never stalls the scanner.

ThreadCadenceJob
scannerLoop~1.5 sSnapshot → enrich → classify → ML score → mitigate
detailsLoopon demandModules, handles, connections, registry for the selected PID
hashLoopon demandSHA-256 / MD5 via BCrypt, then reputation lookup
OnlineTrainer~30 sRetrain the forest and hot-swap it in
Bridge push~1.5 sThe snapshot, posted to the page as a web message rather than a megabyte of script to evaluate; nothing is drawn while the window is hidden

Closing the window does not stop it: MLPA stays in the tray, keeps writing the timeline, and warns through Windows notifications. The startup task starts it there at sign-in.

The same engine runs without a window. MLPA.exe --cli --fail-on-threat exits 20 on any malicious verdict and 10 on any suspicious one, which makes it a scheduled-task or CI gate rather than something you have to sit and watch.

Defaultscore/ProcessActions.cpp
app/Telemetry.cpp
core/GameGuard.cpp

Safety posture

A tool that can terminate processes has to be conservative about when it does. MLPA observes by default and makes every state-changing path explicit.

Observe by default

Auto-mitigation ships off. Turning it on starts you in dry-run, which logs the decision it would have made so you can read a day of them before arming anything.

Hard-wired exclusions

PID 0 and 4, smss, csrss, lsass, services, signed System32 components — never acted on, whatever the score says.

Never judges itself

MLPA excludes its own process at the snapshot boundary, runs one instance at a time, and recognises another copy of its own file rather than scoring it for its design.

Hash-only reputation

Reputation lookups send the SHA-256 and nothing else, and the background sweep is off by default. Your binaries stay on your disk; the AI tribunal receives a text dossier, not a file.

Asks before anything leaves

The update check sends a version number. Reports to the developer are off until you say yes, and every one shows its exact text first. A build without a report server sends nothing at all.

Leaves games alone

With game mode on, games with kernel anti-cheat are looked at only from the outside: name, file, signature and network — never their memory, threads or handles.

Kept honest bytools/
telemetry/test/

Checks

A mock proves the interface does not crash; it cannot prove it says true things. So each check below runs against something real or measures something real, and the numbers are published rather than claimed.

CheckWhat it provesResult
tools/fp_report.py How quiet MLPA is on a clean PC: every networked and acting module off, verdicts counted per distinct program, the numbers kept in docs/fp/. 0 of 136 programs flagged, 3 notes
2026-09-23 · Windows 11, standard user
The real engine The interface driven over WebView2's DevTools port against real processes, sockets and services. It found what the mock hid: autoruns named "2" and "4", a desktop.ini called suspicious, catalogue-signed files shown as unsigned. fixed
Cost Share of one core with ~500 processes, and the page's memory, before and after snapshots moved to web messages. Home 96 → ~35 %
Network 230 → ~40 %
heap 300–530 → ~15 MB
tools/module_gate_audit.py Every module switch is read at every call site it governs, so turning one off really stops it. pass
tools/i18n_check.py Every string in the interface has its Romanian translation. 1046 of 1046
telemetry/test/ The report server keeps a ping's day and country and nothing else about the sender, refuses what MLPA would not send, keeps a verdict report exactly as approved, and deletes what is past its time. 8 of 8 pass
tools/*_smoke.cpp Sixteen engine pieces checked without a live machine: the classifier's rules, signature reading, PE bounds, redaction, the key store, telemetry against the real server code. The surveys that set the thresholds are in tools/research/. 16 files

--devtools-port is off unless asked for: an open DevTools port is an unauthenticated channel into a page that can end processes as administrator.

Changelognewest first

Release log

The map2026-09-24

It shows what you picked, and holds still

  • One click moved everything. Selecting an address opened the side pane, the map was laid out again for the narrower space, and every node went somewhere else — the one clicked 470 px away. Now the picture scales as a whole and closing the pane puts it back exactly.
  • A selection you can see. An app, a bubble, an address or a line is ringed; its lines are drawn in the accent and everything unrelated fades. Lines can be selected too: a line is one app talking to one place.
  • A click selects, a second one goes further. A near miss inside an open group used to fold the whole group; an app sent you to another tab. Now a click explains, and a double click opens a bubble, opens the app's process, or folds a cloud.
  • Nothing on top of anything else. Only bubbles kept a distance before; now every node does, by its size.
  • Four tabs that say what they are. Each has a line saying what it shows; Listening became Open ports; grouping by subnet — one address per /24 on most desktops — is gone; narrow values are centred in narrower columns.
Clean-up2026-09-24

Cleared for a public release

  • The first interface is gone, with the eight scripts that tested or reshaped it. The new one is what CMake and the installer build, and a build tree still set to the old one is moved over by itself.
  • Ten bridge actions only that interface called went with it, and what sat behind them: a posture series written every 30 seconds that nothing read any more, a blocklist import and the matching only its lists fed, window state the new interface keeps itself.
  • It builds with MinGW again. The new filtering-platform code used three constants MinGW's headers do not define, and build.ps1 prefers MinGW when it finds it.
  • The notifications switch says what it does — every desktop notification, not "bad endpoints" — and takes effect at once rather than after a restart.
  • The licence, completed. Settings → About shows the notices the GPL asks for, a section 7(b) term keeps "Based on MLPA by Ionut-Eugen Oancea" in modified versions, and every source file points to NOTICE.md.
Ready to ship2026-09-24

An installer, the GPL, blocking that holds, reports only when asked

  • An installer with an information page instead of an "I accept" — the GPL is not an end-user agreement — that closes a running MLPA before an upgrade and is safe for silent installs.
  • Blocking that holds: a Windows Filtering Platform filter at connect time (IPv4 and IPv6, TCP and UDP, so QUIC too) beside the firewall rule. Open connections are torn down and the block is checked, not assumed; a third-party firewall in charge is named.
  • Reports only when asked: a daily anonymous ping, crash reports and false-positive or missed-detection reports, each showing its exact text first. What the server keeps and for how long is in PRIVACY.md.
  • Touch: pointer events throughout, bigger targets, pinch on the network map. Addresses gathered by who owns them.
  • Release hardening: the C++ runtime linked in (nothing extra to install), navigation locked to the interface's own origin, DevTools only when asked for, crash dumps, a rotating log.
Watching2026-09-23

It keeps watching, and says so plainly

  • The second interface: a simple Home first, the workbench behind the Advanced switch. Cheap to leave open — snapshots as web messages, nothing drawn while hidden. Measured with ~500 processes: Home from 96 % of one core to ~35 %, Network from 230 % to ~40 %.
  • English and Romanian, following the Windows language; the tray speaks the same one.
  • Watching from the tray, with a timeline on disk: first-time programs, flags, new startup items, findings and what you did.
  • Machine checks and new detectors: the fake "I'm not a robot" command pasted into the Run box, remote-control tools, Remote Desktop; fake game cheats, miners, a hidden browser in remote-debugging mode, .NET build tools online, an unsigned download starting a script host.
  • Script hosts are trusted one exact command at a time, never by file: a mark on powershell.exe would hide the very script a ClickFix page just pasted.
One look2026-09-23

The site and the app, one design

  • The radar mark; square corners, hairline rules, mono labels, teal only for what is live or selected; light by default, dark on request.
  • The site's screenshots are rendered from the interface itself by tools/site_assets.py.
Signatures2026-08-12

The signature check lied one level lower

  • "This file is in no catalogue" and "the catalogue service could not run" were one answer, and it became unsigned — so on a machine with Cryptographic Services unhealthy, every Windows binary would have read as unsigned. Now three-valued: could-not-check is unknown, not an accusation. On 380 processes of a healthy machine the change moved zero verdicts, which is what it should do.
The first interface2026-08-07 → 11

Fourteen iterations, since replaced

  • They built and audited the first interface and much of the engine under it: an ETW parser that recovers which socket each byte belongs to (the ports arrive big-endian), a signature memo that took a scan pass from 10–19 s back under 1.5 s, "not measured" shown as such rather than as 0 B/s, and no all-clear before the first scan has finished.
  • That interface has been replaced. What it taught about the collectors is in LESSONS.md.