edr-collector, routed through MSK Kafka and the enrichment service (ENRICH_PLATFORM=macos), and land in a single, purpose-built ClickHouse table: endpoint_events.
endpoint_events is a wide, macOS-native table typed by event_class and event_type. Per-class columns are populated only for the events that use them; everything else is left empty. There is no Linux cgroup, container, pod, or Kubernetes clutter in this schema — those concepts do not apply to an endpoint.
The defining property of this table — the macOS differentiator — is that every single row carries code-signing trust and application attribution as first-class columns. A file read, a network flow, and a process exec all arrive already stamped with “what signed this binary, do we trust that signature, and which user-facing application is responsible.” On Linux, none of that exists at the kernel-event level. On macOS it rides on every row, which turns questions like “which unsigned application read ~/.ssh?” into a single WHERE clause.
Schema overview
Each row is one observed kernel event with four layers of columns:
This document walks each layer in turn, then lists the
event_type values.
Common header (every event)
The header identifies when, where, and who — the same actor metadata on every row regardless of event class.wall_time_ns is the authoritative event timestamp from the Endpoint Security message. event_time is the derived DateTime64 column you filter and partition on in ClickHouse.Code-signing identity (every event) — the macOS differentiator
On macOS, the kernel knows the signing identity of every running binary, and Apple’s Endpoint Security framework hands it to ESensor on each message. The collector promotes that identity to dedicated columns, so trust is queryable on every event — not just on exec.The code_signature verdict
code_signature collapses Apple’s signature evaluation into one of four trust tiers:
This is the column that makes macOS telemetry distinctive. Because the verdict is on every row — file, network, and process alike — you can ask trust questions about any action:
Application attribution (every event) — the macOS differentiator
A modern macOS app is not one process. Google Chrome alone spawns dozens ofGoogle Chrome Helper, Google Chrome Helper (GPU), and Google Chrome Helper (Renderer) processes. Looking at raw comm values, the signal is scattered across helper processes that mean nothing to an analyst.
The macOS collector folds every event back to the owning application using Apple’s Endpoint Security “responsible process” concept, so activity is grouped the way a human thinks about it.
This means a single
WHERE application_id = 'com.google.Chrome' returns the entire footprint of Chrome — every helper exec, every file it touched, every flow it opened — without enumerating helper names. session_id further narrows that to one specific launch of the app, and responsible_pid lets you pivot from any event to the accountable process.
responsible_pid and application_id are how the dashboard’s Investigate, Files, Networks, and Users views attribute scattered helper-process activity to the application a user actually ran. The folding happens at enrichment time so it is consistent across every query.Per-event payloads
Beyond the common header and the two always-present identity layers, each event class carries its own payload columns. The long tail of less-common attributes lands in anattrs map.
Process payload — exec, fork, signal
exec is the spine of the process model: it carries the argv, the signing identity, the team id, and the cdhash all on one row. signal events record kill attempts — attrs['target_pid'] and attrs['signum'] reveal who tried to terminate whom, which surfaces both attacker kill-chains and attempts to tamper with the collector itself.
File payload — file_open, file_unlink, file_rename, file_chmod, xattr_set, xattr_delete
file_open dominates event volume (~95%+), which is why ESensor applies aggressive userspace filtering before anything leaves the host — see Agent filtering.
The xattr_* events are macOS-specific and high-signal: they capture changes to com.apple.quarantine, the extended attribute Gatekeeper uses to mark downloaded files. An xattr_delete on a quarantine attribute is a classic Gatekeeper bypass — stripping the “downloaded from the internet” flag so the OS will run a file without prompting.
Network payload — net_flow
Network flows come from a combination of an
lsof/socket poller and the NetFilter NetworkExtension content-filter system extension, which resolves each flow’s audit token back to the responsible app. Because of NetFilter, a net_flow row already knows which application opened the connection — bundle_id answers “what made this connection” without a separate process-to-socket correlation step.
net_flow records connection metadata only — destination, ports, byte counts, and the responsible app. It does not capture packet payloads, and by default it does not resolve DNS names.Persistence payload — btm_add
btm_add events come from macOS 13+ Background Task Management (BTM) and fire whenever a launch agent, launch daemon, or login item is registered — i.e. when something installs itself to persist across reboots. This is the single highest-signal persistence event on macOS: it is how the vast majority of macOS malware (and legitimate auto-start software) survives a restart.
Device payload — mount
mount events fire when a filesystem is mounted — most interestingly removable media (USB drives) and disk images (DMGs), both common vectors for data exfiltration and software delivery.
Event types
The collector subscribes to 12 Apple Endpoint Security events plus a network source, and emits the followingevent_type values into endpoint_events:
xattr_delete and mount are rare by nature (quarantine removal and USB/DMG mounts) — they are subscribed and emitted, but a quiet host may show none over a short window. signal, file_unlink, and file_rename are subscribed as AUTH events so ESensor can also deny tampering with itself, but they still emit equivalent telemetry rows.How identity and attribution are filled
The identity and attribution layers are not best-effort metadata — they are populated at high fill rates and are the columns most queries pivot on. Verified fill rates from live data:
For more on how telemetry travels from ESensor to ClickHouse, see End-to-end data flow. For how raw event volume is reduced before it leaves the host, see Agent filtering.
What the collector does not capture
Consistent with the Linux collector’s design philosophy, the macOS collector observes system activity at the metadata level, with one documented exception — the clipboard (below). It does not capture:- File contents — only the path, operation type, flags, and (for renames) the destination
- Packet payloads — only the flow’s destination, ports, byte counts, and responsible app
- DNS query contents —
net_flowdoes not resolve domain names by default - Keystrokes — paste detection is a listen-only Cmd+V tap; no key content is recorded
- Screen content — Screen Recording is never requested
- Memory regions or process memory
- Environment variables
- Credentials or any user data beyond what standard tools like
ps,lsof, orcodesignwould expose — except clipboard content, below
