Jamf telemetry basics
Get a deep dive into understanding Mac telemetry at Jamf, including how it works, why it matters and how Mac admins can use it today.
Table of contents
- Analyzing a telemetry event
- What is a telemetry message?
- The top-level structure
host— Who generated this event?process— What process caused this event?event— What actually happened?action— Was it allowed or blocked?seq_numandglobal_seq_num— Sequence and orderingtimeandmach_time— When did it happen?versionandschema_version
- Processes and telemetry
Analyzing a telemetry event
Jamf’s Mac platform collects a rich stream of behavioral telemetry from macOS endpoints. Each time something happens on a device, whether a process launches, a filesystem mounts or a user logs in, Jamf packages that activity into a structured JSON message and forwards it to your data pipeline. Understanding what's inside those messages is the foundation for effective threat hunting, incident response, forensics, performance monitoring and compliance analysis.
This article walks through the anatomy of a Jamf Mac telemetry message, explaining what each field means, how the pieces fit together and where to look first when investigating an event.
What is a telemetry message?
At its core, a telemetry message is a self-contained record of a single event that occurred on a macOS endpoint. Every message follows the same top-level structure regardless of the event type. That consistency is what makes it possible to write queries, correlation rules and detection logic that work across your entire fleet.
Messages are generated by the Jamf extension running on each device. The extension uses Apple's Endpoint Security API under the hood. This means the data is authoritative because it comes directly from the operating system, not from log scraping or filesystem polling.
Note that this article is not comprehensive documentation of the Jamf telemetry data model but rather a highlight of some of the most import telemetry fields and how they can be used by admins and security professionals.
The top-level structure
Every telemetry message is a JSON object with the following top-level keys:
Let's review each of these fields.
host — Who generated this event?
The host object tells you which device produced the event. It contains:
hostname— The device's hostname as configured in macOS System Settingsips— An array of IP addresses the device held at event timeos— The full macOS version string, e.g. "Version 15.0.1 (Build 24A348)"protectVersion— The version of the Jamf Protect agent running on the deviceprovisioningUDID— The hardware provisioning UDID, useful for correlating with Jamf Pro inventoryserial— The device serial number, the most reliable unique identifier for a Mac
When hunting across a large fleet, serial or provisioningUDID is your best anchor for grouping all activity from a single device. Hostnames can change but serial numbers and the UDID generally don't change.
process — What process caused this event?
The process object describes the source process, meaning the process whose activity triggered the event being reported. This is distinct from the target of the action. In an exec event, for instance, the source process is the one creating a new process, not the newly created process itself.
The process object is one of the most information-dense parts of a telemetry message, containing the executable path, code signing details, timing and a set of audit tokens that let you trace the process's ancestry. One subtlety worth knowing about the source process is that on macOS the source process is often a system-level Apple binary. While it's technically the process that triggered the event, it's frequently not what you actually care about, since most investigations focus on the user application or process that initiated the activity. The Processes and telemetry section goes deeper on this distinction and related topics.
event — What actually happened?
The event object holds the event-specific data, and its structure varies based on what occurred. Inside event, you'll find a key named after the event type, such as exec, mount or btm_launch_item_add, whose value is an object containing fields specific to that event.
The event type is also encoded numerically in the top-level event_type field (more on that below), but the named key inside event is the human-readable way to identify what happened.
event_type is an integer that identifies the category of event. For example, 9 maps to an exec event (process execution), 22 maps to mount and 124 maps to btm_launch_item_add (a persistence item being added). A full mapping is maintained in the data model documentation.
action — Was it allowed or blocked?
Endpoint security events come in two different types:
- Auth events are those where the Endpoint Security API asks Jamf (or any other application using the Endpoint Security API) to make an allow or deny decision before the action is completed. These are blocking events. Not all Endpoint Security events support Auth.
- Notify events are informational. The action has already occurred and Jamf is simply being informed about the occurrence.
Telemetry subscribes to notify events and will contain an auth result for the events that support Auth.
The action object records the outcome of the event:
For auth results, action.result.result.auth will be 0 (allowed) or 1 (denied). In most telemetry, you'll see 0, meaning the system allowed the activity to proceed. A value of 1 indicates Jamf (or another Endpoint Security client) actively blocked the action.
seq_num and global_seq_num — Sequence and ordering
These two counters are important for understanding event ordering and detecting gaps in telemetry.
seq_num is a counter for events of a specific type and will reset each time the Jamf extension starts. If you're tracking exec events and you notice seq_num jumps from 1042 to 1500, there's a gap. Events may have been dropped or the extension was restarted.
global_seq_num is a counter across all event types observed by the extension and will also reset on extension restart. This is useful for cross-event ordering. If you want to reconstruct a timeline of everything that happened on a device in sequence, sort by global_seq_num.
time and mach_time — When did it happen?
time is a human-readable ISO 8601 timestamp with nanosecond resolution, e.g. "2024-02-15T23:25:48.767740512Z". This is the wall clock time at which the event was generated, expressed in UTC.
mach_time is the raw Mach absolute time, a monotonic counter internal to the macOS kernel. It is not directly human-readable but is extremely precise and not subject to clock drift or NTP adjustments. For comparing the relative ordering of events within a single device, mach_time can be used.
version and schema_version
version reflects the version of Apple's Endpoint Security framework used to collect the event. As Apple adds new event types and fields in newer macOS releases, this version number increases.
schema_version reflects the version of the Jamf telemetry data model schema used to generate the message. This is useful for SIEM parsers that need to handle schema changes across Jamf Protect versions.
Now that you understand the overall message structure, the natural next step is to dig deeper into the two objects that appear in nearly every event: the process object and the event-specific payload. The upcoming section covers the process object in depth, including how to trace process ancestry using audit tokens.
Processes and telemetry
Why processes are central to telemetry
Almost everything that happens on a macOS system is driven by a process. When a user opens a terminal and types a command, that's a process. When malware drops a file and executes it, a process is doing the work. A LaunchDaemon starting at boot is managed by a process. Even filesystem events and network connections ultimately trace back to a process that initiated them.
Because of this, Jamf includes rich process context in nearly every telemetry event. Rather than just recording what happened, the data lets you answer who did it, with enough depth to trace back through the entire execution chain that led to the activity.
The three process roles
A telemetry message can contain process objects in up to three distinct roles. Understanding which role a process object is playing tells you how to interpret its data.
1. Source process
The source process is always present within the process key. It represents the process that is the direct source of the activity being reported.
In an exec event, for example, the source process is the one that called exec to create a new process. In a mount event, the source process is the one that initiated the mount operation. The source process is always the actor, not the object of the action.
2. Target process
The target process is specific to the exec event and in this case the target is the newly created process. It captures the state of that process at the moment execution began, including its executable path, arguments, code signing details and more. The target represents the result of the action rather than its initiator.
3. Instigator process
The instigator process also lives inside the event object, at event.<eventname>.instigator. It appears in events where a third party is responsible for triggering the activity. The instigator may be different from the source process, which makes this distinction useful for identifying indirect or delegated activity. For example, in a btm_launch_item_add event, the instigator is the process that requested that a new persistence item be registered. The source process that does the BTM registration is almost always the system-level com.apple.backgroundtaskmanagementd process.
Parent and responsible processes
Each of the three process roles above (source, target and instigator) also carries references to two additional processes, the parent and the responsible process. These are accessed through audit tokens (covered below).
Parent process: The process that directly created the process in question. For example, if bash launched curl, bash is the parent of curl. The parent audit token is at parent_audit_token.
Responsible process: The process that is responsible for the existence of the entire chain. If a user double-clicks an app in Finder, Finder is the responsible process for everything that app's process tree launches. The responsible audit token is at responsible_audit_token.
Most applications that get opened are assigned as their own responsible process. Further activity that those applications perform inherit that responsible process no matter how far deep into the process tree those subsequent processes run. This allows you to track any given command back to the responsible application.
Decoding a macOS process
Here's what's inside a process object:
Identity and code signing
Code signing can be a very complicated topic and this post will not go too deep into the internals of code signing on macOS but rather highlight some key code signing fields. Almost all code that is executed on macOS is signed one way or another, especially for Apple Silicon macs. Apple continues to make it more difficult to run unsigned code, and it is expected that this will be strictly enforced with no exceptions at some point.
Additionally it should be noted that binaries can be signed in many different ways, including ad-hoc signing. Ad-hoc code signing on macOS is the most basic form of code signing, where an executable is signed without using a specific developer identity or a certificate issued by a trusted Certificate Authority (like Apple). Essentially, you are signing the code yourself, without any external verification.
Code signing information is the preferred way to identify the executable image that was executed. The process object also includes information about the path to the executable image but paths can sometimes be misleading since files can be moved, copied and executed from different locations on a filesystem. As an example, an app could masquerade as terminal by running from /Applications/Terminal.app/Contents/MacOS/Terminal. The better way to identify Apple's terminal is by looking at the signing_id, is_platform_binary, team_id and cdhash fields.
signing_id is the application identifier embedded in the code signature, typically in reverse domain name notation (e.g., com.apple.Terminal, com.microsoft.Word). This field is null for unsigned binaries. Since it's a developer-controlled string, don't treat it as proof of legitimacy on its own. A malicious binary could declare any signing ID it wanted if signed ad hoc.
cdhash is the code directory hash, a cryptographic hash of the code signature itself. Unlike a file hash, the cdhash is bound to the signature, which means it can verify the integrity of a signed binary and uniquely identify a specific version of a specific signed application. cdhashvalues are an excellent way to identify particular executable versions and then block their execution using Jamf's custom threat prevention feature.
team_id is the 10-character Apple Developer team identifier assigned by Apple to the developer when they enroll in the Apple Developer Program. Unlike signing_id (which developers can choose), team_id is assigned by Apple and is consistent across all software published by the same organization. A null team_id means the binary is either unsigned, ad-hoc signed or is an Apple platform binary.
is_platform_binary is true for binaries that ship as part of macOS itself (e.g. binaries in /System/, /usr/bin/, etc.). Platform binaries are among the most trusted executables on the system.
codesigning_flags is a bitmask integer that encodes the code signing state of the process at the time of the event. Key bits include whether the signature is valid (CS_VALID), whether the binary is a platform binary (CS_PLATFORM_BINARY), and whether it was killed because of a signing problem (CS_KILLED). SIEM integrations for Jamf typically include helper macros for decoding this bitmask into human-readable flags.
is_es_client is true if the process is itself an Endpoint Security (ES) client, meaning it has registered with Apple's Endpoint Security framework. Jamf Protect extension itself would appear as an ES client, as would other security tools. Unexpected processes with this flag set are worth investigating, since ES clients have elevated visibility into system activity.
Execution context
executable is a File object that contains the full path, timestamps, and (if hash collection is enabled) the SHA-1 and SHA-256 hashes of the process executable on disk.
start_time is the timestamp at which the process was started. This is distinct from the event timestamp. It tells you when this particular process instance was launched, which is useful for correlating with other events.
tty is a File object representing the controlling terminal of the process, if any. A process with a TTY is running in an interactive terminal session. The absence of a TTY in contexts where you'd expect one (or the presence of a TTY where you wouldn't) can be informative. For example, malware running in a terminal, or a process spawned by automation masquerading as an interactive session.
ppid (parent process ID) and original_ppid are both parent process identifiers. ppid reflects the current parent, which can change over the life of a process (for example, if a process is re-parented to launchd after its original parent exits). original_ppid captures the original parent at the time of process creation and does not change.
session_id and group_id provide POSIX session and process group context, which can be useful for understanding whether processes belong to the same terminal session or pipeline. In terminal examples, the session_id will match the pid of the terminal instance. In interactive graphical examples (e.g. running a standard UI Application) this will represent the graphical user session.
Audit tokens: the key to process identity and lineage
The audit_token object is how Jamf uniquely identifies process instances — and how you can trace the full ancestry of any process in a telemetry event.
Using uuid for global process identity
The most important field in an audit token is uuid. This is a globally unique identifier for a specific process execution, and it will be the same across all telemetry events from the same process instance, even across different event types. This makes it the right join key when you want to find all activity from a specific process:
"Show me everything that happened on this device that was initiated by the process with uuid B16B5E9E-1C54-4CCC-B013-81F848C88D92."
The pid field, by contrast, is only unique while the process is alive. On macOS PIDs are reused, so a PID you see in one event might refer to a completely different process in a later event. For any kind of cross-event correlation, always use uuid.
User context: euid, ruid, egid, rgid
Audit tokens carry both effective and real user/group IDs. The real UID (ruid) is the user who launched the process. The effective UID (euid) is the user context under which the process is currently operating and this can differ from the real UID if the process is running with elevated privileges (e.g., via sudo or a setuid binary).
The root user typically has a UID of 0. Regular users on macOS typically start at UID 501. An euid of 0 combined with an ruid of 501 tells you a regular user's process is running with root-level effective privileges.
asid and auid
The audit session ID (asid) groups processes within an audit session. The audit UID (auid) represents the user who is responsible for the audit session. This is set at login and persists even if the user subsequently escalates privileges. It's a useful field for attribution when you want to know which human user is ultimately behind an action, regardless of privilege escalation.
Audit token locations across a message
Audit tokens appear in multiple places in a telemetry message, each providing a different level of ancestry:
process.audit_token: the source process itselfprocess.parent_audit_token: the source process's parentprocess.responsible_audit_token: the process responsible for the source process's existenceevent.<eventname>.target.audit_token: the target process (e.g., in exec events)event.<eventname>.target.parent_audit_token: the target process's parentevent.<eventname>.target.responsible_audit_token: the process responsible for the target processevent.<eventname>.instigator.audit_token: the instigator process (e.g., in BTM events)event.<eventname>.instigator.parent_audit_token: the instigator's parentevent.<eventname>.instigator.responsible_audit_token: the process responsible for the instigator
This means a single telemetry message can encode up to nine process audit tokens, giving you a rich picture of the execution context around an event. Each process with an audit token in the message should also have its own exec event in your telemetry, which you can look up using the uuid from the audit token.
Practical threat hunting patterns
Here are a few patterns that illustrate how process data enables effective investigations:
Unusual parent-child or responsible-child relationships: Many attacks involve spawning processes from unexpected parents, e.g. a Microsoft Word process (com.microsoft.Word) that has a child curl or bash process. Look for exec events where process.signing_id is a productivity application and event.exec.target.signing_id is a shell or download utility is a classic technique.
Unexpected privilege elevation: Look for process audit tokens where euid is 0 but ruid is not, particularly when the process in question is not a recognized system utility. This indicates a user-launched process is running with root privileges.
Non-platform binaries in system paths: is_platform_binary combined with executable.path is a good consistency check. A process at /usr/bin/something that has is_platform_binary: false is suspicious; legitimate macOS system binaries at those paths should always be platform binaries.
Processes without a code signature: A team_id of null combined with an is_platform_binary of false and a signing_id of null means a binary has no meaningful code signature. While this isn't automatically malicious, it's worth additional scrutiny, particularly if such a process is making network connections or writing files.
Use this foundational telemetry overview to deep dive into telemetry event analysis by Jamf Threat Labs