ESF for AI agents — what FSEvents can't tell you
The first thing most macOS developers reach for when building file-system observability is FSEvents. It's documented. It's stable. It's been around since Leopard. The first thing they learn — usually the hard way — is that FSEvents was never built for the question we're trying to answer.
FSEvents tells you what changed. It cannot tell you who changed it.
The question, precisely
rAIzr exists to answer a single question, in real time, with kernel-level
confidence: which process just touched this resource? Not "did
something on the machine read ~/.ssh/id_ed25519" — that's a
cheap question, and FSEvents can answer it. The expensive question, the
one that matters when you're auditing AI agents, is "Was it Claude
Desktop, Spotlight, mds_stores, or a script the user double-clicked
sixty seconds ago?"
For a security product, anything less than per-process attribution is just noise.
Why FSEvents fails the assignment
FSEvents is a system-wide file-system event stream. The kernel coalesces changes by directory, batches them, and hands them to user-space consumers. It tells you "this path got modified". It does not tell you which PID, which audit_token, which code-signing identity, or which parent process initiated the change.
There are workarounds. You can correlate FSEvents output with
lsof snapshots, or with fs_usage output. Both
are race-prone, both are after-the-fact, and both fall apart the moment
you have more than one process touching adjacent paths in the same
millisecond — which is exactly what happens on a developer machine
running Claude Desktop, ChatGPT, and Spotlight at once.
You can also walk /proc-equivalents and try to infer who
had the file open at scan time. By the time you get there, the agent
has read, exfiltrated, and closed the file. The window is sub-second.
What ESF gives you instead
The Endpoint Security Framework — introduced quietly with macOS Catalina and matured through Sequoia — is Apple's answer to the same question that Microsoft answered with kernel callback drivers and Linux answered with eBPF. It's a kernel-mediated event stream where each event arrives with full process attribution attached.
A single ES_EVENT_TYPE_NOTIFY_OPEN event on
~/.ssh/id_ed25519 contains, out of the box:
es_message_t { event_type: "ES_EVENT_TYPE_NOTIFY_OPEN", process: { audit_token: [uid, gid, ppid, pid, asid, …], executable: "/Applications/Claude.app/Contents/MacOS/Claude", codesigning_id: "com.anthropic.claudefordesktop", team_id: "Q6L2SF6YDW", // codesign -dv /Applications/Claude.app parent_pid: 1, is_platform_binary: false, }, file: { path: "~/.ssh/id_ed25519", }, timestamp: 2026-05-03T14:22:11.402891Z, classification: "sensitive_path_read" }
Per-process attribution is not a derived value here. It's a primary key on every event. We don't infer it; the kernel hands it to us.
What it costs you
ESF is not free. Three things you trade away when you build on it:
Distribution friction. ESF clients run as System Extensions. System Extensions require code-signing with the Developer ID with Endpoint Security entitlement — which Apple grants by application, review, and (sometimes) negotiation. They require notarization. They require user-side approval through the Privacy & Security pane. None of this is fatal, but none of it is one-click.
Permission surface. Full Disk Access is non-negotiable. Without
it, ESF will hand you opaque events that say "open happened
on a path you cannot see". You become a watcher with no eyes.
Operational footprint. A System Extension survives reboots differently than a normal daemon. Crash recovery is different. Update flow is different. The first time you see your extension stuck in a failed-to-load state because its signature didn't validate, you'll understand why most desktop AV products charge what they do.
Why we paid the cost
Because the alternative is a security product that lies. If rAIzr
flagged "file_read on ~/.ssh/id_ed25519" without
being able to attribute it to which agent, the operator's only
next move is to close the alert and stop trusting the product. Crying
wolf without naming the wolf is worse than not watching at all.
ESF is the only macOS API that lets us name the wolf at the moment it bites. That's the whole product, in a single dependency.
rAIzr's daemon runs as an ESF System Extension on macOS. Windows will get the equivalent treatment with ETW + WFP when we ship the port in 2027 Q1. The architecture is identical: kernel-attributed events, local classification, no cloud egress.