AI agents: see /llms.txt for a full index of this site, or /llms-full.txt for concatenated documentation.

Workshop Docs

Enterprise control plane for Santa. Manage rules, approvals, telemetry, and policies across your macOS fleet.

Workshop Docs
View chapters on this page

Linux Telemetry Schema

This page documents the complete schema for all telemetry event types collected by Workshop from Santa agents on Linux.

Each event type below is also the table name in SQL queries, prefixed with linux_: exec fields live in linux_exec_2026, linux_exec_202603, or linux_exec_20260315 (see table naming). The prefix exists because fork and exit are collected on macOS too, with entirely different columns. Columns drift across Santa versions, so DESCRIBE linux_exec_20260315 on your own data is the authoritative list.

Linux telemetry is not shared with the macOS schema, including the base fields. A telemetry field with the same name on both platforms does not necessarily mean the same thing, and a query written against one platform will not run against the other. Package inventory is the exception: its platform-independent packages table has the same columns on every host.

Contents

Event tables

Common types

Credentials, Namespaces, Cgroup, Process, Lineage, Filesystem, FileIdentity, Digest, File, FileDescriptor, Tty, Image, WorkloadOwner, KubernetesPod, Container

Base Fields

Every Linux telemetry table carries these identity and timing columns.

FieldTypeDescription
EventIDtextUnique identifier for the event
MachineIDtextThe unique machine ID (host UUID)
HostnametextThe hostname of the machine at the time of the event
BootIDtextThe kernel’s boot identifier, stable for the lifetime of one boot
EventTimetimestampWhen the event occurred
ProcessedTimetimestampWhen Workshop processed the event
OperatingSystemtextThe platform the telemetry came from, always linux in these tables

Process Events

linux_fork

A process creating another process. SharedThreadGroup is what separates a new thread of the same program from a genuinely independent process, and it decides that on its own. SharedMm and SharedFiles each report one resource shared independently of that: a thread commonly shares both, but neither is required for thread-group membership, and either can be shared by a task that is a separate process.

FieldTypeDescription
SubjectLineageThe process performing the creation, with the process that started it
ObjectProcessThe newly created process
SharedThreadGroupbooleanWhether the new task joined its creator’s thread group, making it a thread of the same program
SharedMmbooleanWhether the new task shares its creator’s address space
SharedFilesbooleanWhether the new task shares its creator’s file descriptor table
ContainerContainerThe container the subject ran in. Absent when it ran directly on the host

linux_exec

A process replacing itself with a new program.

FieldTypeDescription
SubjectLineageThe process performing the execution and the process that started it, as they are after the change took effect
CommtextThe short name the new program will be known by, at most 16 characters. Chosen by the program, so not evidence of what ran
ExecutableFileThe canonical path of the executable image that actually ran
InvokedFileWhat was asked for. For a script this is the script, where Executable is the interpreter
InterpretedbooleanWhether Invoked and Executable differ because an interpreter handled the request
WorkingDirectoryFileThe working directory, needed to make sense of any relative path in Args. Only Path and Type are populated
RootFileThe process’s root directory, which is how a process confined to a subtree becomes visible. Only Path and Type are populated
FileDescriptorsFileDescriptor arrayThe standard descriptors the program inherited
TtyTtyThe terminal the process is attached to
Argstext arrayCommand-line arguments
Envstext arrayEnvironment variables
ArgcnumberAn upper bound on how many entries Args holds, not a guarantee
ArgvEnvpCopiednumberBytes of arguments and environment captured. Zero when capture failed outright
ArgvEnvpTruncatedbooleanWhen set, the absence of an expected argument means nothing
ContainerContainerThe container the subject ran in. Absent when it ran directly on the host

linux_exit

A process terminating. Signal is meaningful only when ExitFromSignal is set, and ExitStatus only when it is not. ExitCode is retained so nothing is lost, but it cannot be interpreted without ExitFromSignal; read the decoded fields instead.

FieldTypeDescription
SubjectLineageThe terminating process, with the process that started it
ExitCodenumberThe raw packed status value a waiting parent receives. Cannot be interpreted without ExitFromSignal; read Signal and ExitStatus instead
ExitFromSignalbooleanWhether the process was killed by a signal rather than ending on its own
SignalnumberThe signal that killed the process, meaningful only when ExitFromSignal is set
ExitStatusnumberThe status the process chose to end with, meaningful only when ExitFromSignal is not set
CoreDumpedbooleanWhether a memory dump was started, not that one was completed or written anywhere
GroupDeadbooleanWhether a whole program ended, as opposed to one thread of a still-running program
ContainerContainerThe container the subject ran in. Absent when it ran directly on the host

Inventory Events

Unlike every other table on this page, inventory tables are not populated by the continuous telemetry stream. They are produced on demand by the Package Inventory command (Hosts → Commands → Run command), which asks each targeted host to run a read-only scan and upload the results into its normal telemetry prefix. A host that has never been scanned has no rows.

Two consequences worth knowing when querying:

  • BootSessionUUID is always empty. An on-demand scan isn’t tied to a boot session.
  • EventTime is the scan time, not the time a package was installed — the scan observes current state and has no visibility into when it came to be.

packages

One row per package discovered on a host. Every ecosystem shares this single table, distinguished by Ecosystem, so a fleet-wide query needs no unions.

FieldTypeDescription
EventIDtextUnique identifier for the inventory record
MachineIDtextThe unique machine ID (host UUID)
HostnametextThe hostname of the scanned host
BootSessionUUIDtextAlways empty because an inventory scan is not tied to a boot session
EventTimetimestampWhen the inventory scan observed the package
ProcessedTimetimestampWhen Workshop processed the inventory record
OperatingSystemtextThe platform the scanned host is running
RecordTypetextAlways package in this table
RunIDtextIdentifier shared by every row from one scan; use it to isolate a single scan’s results
ProfiletextScan profile that produced the row: baseline, project, or deep
Ecosystemtextnpm, pypi, go, rubygems, packagist, mcp, editor-extension, browser-extension, homebrew, agent-skill, or nix
PackageNametextPackage name as written in the manifest or lock file
NormalizedNametextEcosystem-normalized name — join on this rather than PackageName
VersiontextInstalled version. Empty when no exact version could be determined
ProjectPathtextRoot of the project the package belongs to, for project-scoped finds
RootKindtextWhy the containing directory was walked: global_package_root, user_package_root, project_root, editor_extension_root, browser_extension_root, mcp_config_root, homebrew_root, agent_skill_root, deep_home_root, or unknown
InstallScopetextEcosystem-specific dependency scope (e.g. prod/dev for npm and pnpm, indirect for Go modules)
PackageManagertextManager that installed the package (e.g. npm, pnpm, homebrew, firefox-extension)
SourceTypetextKind of evidence the row came from (e.g. package.json, browser-extension)
SourceFiletextPath to the manifest, lock file, or metadata file the row was read from
DirectDependencybooleanWhether the package is directly depended on rather than transitive. Null when the ecosystem can’t distinguish
HasLifecycleScriptsbooleanWhether the package declares install-time lifecycle scripts — these execute on install, so they are a supply-chain execution surface
LifecycleScriptstext arrayNames of the declared lifecycle scripts
ConfidencetextHow certain the identification is: high, or medium when the name or version had to be inferred
RequestedSpectextFor MCP entries configured by spec, the requested selector (e.g. @playwright/mcp@latest) with PackageName normalized to the bare name
LocalAliastextLocal name assigned in a config file, where that differs from the package it resolves to. Set only for mcp (the key under mcpServers) and agent-skill (the local skill name)

Common Nested Types

The following types are used throughout the Linux telemetry schema to represent shared data structures.

Credentials

The identity a process runs as. Two sets are carried on every process: the real set records who started it, the effective set is what permission checks are made against, so a program that has changed identity is still attributable to whoever launched it.

FieldTypeDescription
UIDnumberThe real owner of the process, and who is able to signal it
GIDnumberThe real group of the process
SUIDnumberSaved user ID, which a process that has dropped privilege can return to
SGIDnumberSaved group ID, the group equivalent of SUID
EUIDnumberThe process’s privileges for most non-filesystem access
EGIDnumberThe group equivalent of EUID
FSUIDnumberThe identity used when accessing filesystem objects
FSGIDnumberThe group equivalent of FSUID
CapInheritablenumberInheritable capability set, as a bitmask
CapPermittednumberPermitted capability set, as a bitmask
CapEffectivenumberEffective capability set, as a bitmask
CapBoundingnumberBounding capability set, as a bitmask
CapAmbientnumberAmbient capability set, as a bitmask
SecurebitsnumberThe securebits flags in force for the process

Namespaces

The isolated views of system resources a process sits in. Each value identifies one view: two processes sharing a value share that resource, and differing values are the clearest signal that a process is isolated from the host, whether by a container runtime, a sandbox, or a program that has isolated itself. These are operating system namespaces and have nothing to do with the Kubernetes namespace on KubernetesPod.

FieldTypeDescription
UTSnumberHostname and domain name
IPCnumberShared memory, semaphores and message queues
MntnumberWhich filesystems are mounted, and where
PIDnumberWhich other processes are visible, and under what numbers
PIDForChildrennumberThe view processes started from here will be placed in. Differs from PID only between requesting a new view and starting the first child
NetnumberNetwork interfaces, addresses, routes and ports
TimenumberThe system clock offsets a process observes
TimeForChildrennumberSame requester-versus-children distinction as PIDForChildren
CgroupnumberHow much of the resource-control hierarchy is visible from here
UsernumberHow user and group identities map to those on the host, which is how an unprivileged user can appear as an administrator inside a container

Cgroup

The resource-control group a process belongs to, as opposed to Namespaces.Cgroup, which describes only how much of the hierarchy the process can see. A process is always in one, container or not.

FieldTypeDescription
IDnumberUnique for as long as the group exists and not reused while it does, which makes it the key for attributing a process to a container
LevelnumberHow far below the root of the hierarchy the group sits, where zero is the root itself. A container’s group is never at the root

Process

A running program, or one thread of it. PID alone does not identify a process over time because process numbers are recycled; PID together with StartBoottime does, within one boot of the host. Build identity on StartBoottime rather than StartTime, which is derived from the wall clock and inherits its error.

FieldTypeDescription
PIDnumberProcess ID as seen from the host
NSPIDnumberProcess ID as seen inside the process’s own PID namespace
TGIDnumberThread group ID, the ID of the program this thread belongs to
NSTGIDnumberThread group ID as seen inside the process’s own PID namespace
StartBoottimenumberNanoseconds since boot at which the process started
StartTimetimestampWall-clock time the process started
PGIDnumberProcess group ID
SIDnumberSession ID
LoginUIDnumberThe user who logged in, surviving later changes of identity. 4294967295 when unset
SessionIDnumberAn identifier for that login. 4294967295 when unset
RealCredentialsCredentialsThe identity the process was started with
CredentialsCredentialsThe identity the process runs as now
NamespacesNamespacesThe kernel namespaces the process sits in
CgroupCgroupThe specific resource-control group the process belongs to, as opposed to Namespaces.Cgroup, which describes only how much of the hierarchy it can see. The primary key for attributing a process to a container
ExecutableFileThe running program. Only Identity is populated here
NoNewPrivsbooleanWhether the process is barred from gaining privileges through exec

Lineage

A process together with the process that started it, captured at the same moment so the pair is internally consistent.

FieldTypeDescription
TaskProcessThe process itself
ParentProcessThe process that started it

Filesystem

The filesystem a file resides on. Where a file lives is often as telling as what it contains: an executable on an in-memory or layered filesystem did not come from the host’s own installation.

FieldTypeDescription
TypetextCoarse filesystem type, e.g. FILESYSTEM_TYPE_OVERLAYFS, FILESYSTEM_TYPE_TMPFS
MagicnumberThe exact filesystem magic value the kernel reported, set even when Type is unknown

FileIdentity

What distinguishes one file from another independently of the name it is reached by. Two observations agreeing on Dev and Ino describe the same file even under different paths; agreeing on Size and Mtime as well means it has not changed in between.

FieldTypeDescription
DevnumberDevice ID
InonumberInode number
SizenumberFile size in bytes
AtimetimestampLast access time
MtimetimestampLast modification time
CtimetimestampLast status change time

Digest

A measurement of a file’s contents, which is what allows the same binary to be recognised wherever it appears and however it is named.

FieldTypeDescription
AlgorithmtextDigest algorithm, e.g. HASH_ALGORITHM_SHA256
ValuetextHex-encoded digest value
ValidbooleanFalse when no measurement was available, in which case the other fields are meaningless

File

Everything known about a single file. Any observation populates the subset it actually has, so an absent field means “not observed”, never “observed to be empty”.

FieldTypeDescription
PathtextFile path
IdentityFileIdentityDevice, inode, size and timestamps
TypetextThe kind of file, e.g. FILE_TYPE_REGULAR
FilesystemFilesystemThe filesystem the file resides on
DigestDigestMeasurement of the file’s contents
DeletedbooleanWhether the file had already been removed when it was observed. Null when the file itself was not inspected
PathTruncatedbooleanWhether Path ran out of room before it was complete. Null exactly where Path was not observed

FileDescriptor

A numbered channel a program inherited when it started. Only 0, 1 and 2 are reported. A pipe or socket where a terminal is expected is a strong indication that a program’s output is being captured.

FieldTypeDescription
FDnumberFile descriptor number
FileFileWhat the descriptor refers to

Tty

The terminal a process is attached to.

FieldTypeDescription
MajornumberMajor device number
MinornumberMinor device number
NametextTerminal device name
PresentbooleanFalse for a service, which has no terminal; the other fields then carry nothing

Image

The image a container was created from.

FieldTypeDescription
ReferencetextThe image reference, which is mutable and reusable
DigesttextThe image digest, which is stable and always means one image

WorkloadOwner

What ultimately created and owns a pod. A pod’s own name is generated and replaced every time it is rescheduled, so this is the durable identity to attribute activity to.

FieldTypeDescription
KindtextController kind, e.g. Deployment. Clusters routinely add their own kinds
NametextController name

KubernetesPod

Where a container sits within an orchestrator, populated only when the host is managed by one. Namespace here is a Kubernetes namespace, a tenancy boundary within a cluster, which is a wholly different concept to the kernel Namespaces on a process. Nothing relates the two.

FieldTypeDescription
NametextPod name, regenerated whenever the pod is rescheduled
NamespacetextKubernetes namespace the pod runs in
UIDtextPod UID
ContainerNametextName of the container within the pod
OwnerWorkloadOwnerThe controller that owns the pod
ServiceAccounttextThe identity the pod’s processes present to the cluster

Container

The container an event occurred inside. CgroupPath is observed directly and is present whenever the activity happened inside any resource-control group. Everything below it is resolved against the local container runtime and is absent when the runtime is unknown or unreachable, or when the activity did not happen inside a container at all.

FieldTypeDescription
CgroupPathtextThe cgroup path the activity happened in
RuntimetextThe container runtime, e.g. CONTAINER_RUNTIME_DOCKER
IDtextContainer ID
NametextContainer name
ImageImageThe image the container was created from
Labelsmap(text, text)Labels attached at creation, carried whole
PodKubernetesPodOrchestrator attribution, when the host is managed by one