macOS and Linux essentials · Lesson 2 of 5
macOS activity: Unified Logs, FSEvents, KnowledgeC and Biome
10 min read
On a Mac, the answer to "what did this user do, and when?" is spread across a binary system log, a file system change journal, usage databases and a handful of small SQLite files. Each source sees a different slice of activity and keeps time differently, and one of them keeps no timestamps at all. This lesson shows where each source lives, how to collect and query it, which time format it uses, and how to corroborate one source with another. Lesson 6.1 covered the macOS layout and plists, and Lesson 3.1 covered Mac Absolute Time, which most sources here use. Details below change between macOS releases, so always confirm the behavior on a reference system of the same version.
Unified Logging
Since macOS 10.12, almost all system and application logging goes to the Unified Logging system. Messages are stored in binary tracev3 files under /private/var/db/diagnostics, in the subdirectories Persist (most retained messages), Special (selected messages kept longer), Signpost (performance markers) and HighVolume (often empty). A tracev3 file stores mainly the arguments of each message. The format strings live in /private/var/db/uuidtext, in files named after the UUID of the binary that logged, and the timesync subdirectory of diagnostics holds the records needed to turn the stored boot-relative clock into wall-clock time. Without uuidtext and timesync files, messages cannot be fully rendered or dated, so collect all three. Because space is capped, retention runs from days to a few weeks depending on how busy the system is.
On a live Mac, sudo log collect --output case.logarchive gathers everything into one .logarchive bundle; --last 3d or --start "2026-03-14 00:00:00" limits the range. Analysis uses log show --archive case.logarchive --predicate '…' --style json. A predicate is a filter on fields such as process, subsystem, category and eventMessage, for example process == "loginwindow" or subsystem == "com.apple.xprotect" or eventMessage CONTAINS "USBMSC". Without --info and --debug, those two lower levels are not shown. log show prints times in the analysis machine's time zone with a numeric offset, such as 2026-03-14 06:25:41.208114-0300; convert to UTC before reporting.
In production logs, dynamic strings that a developer did not mark as public appear as <private>. That is a logging decision made when the event was written, not an anti-forensic act, and the hidden value cannot be recovered from the log afterward.
Useful starting points (message wording changes between releases, so test predicates first):
| Activity | Predicate starting point |
|---|---|
| Login, lock and unlock | process == "loginwindow" |
| sudo use | process == "sudo" (user, TTY, working directory, command) |
| USB storage and mounts | eventMessage CONTAINS "USBMSC"; process == "diskarbitrationd" |
| Screen sharing | process == "screensharingd" (authentication result, viewer address) |
| Gatekeeper | process == "syspolicyd" |
| XProtect | subsystem == "com.apple.xprotect" or the XProtect process name for that version |
On a Windows or Linux analysis host, open-source parsers such as Mandiant's macos-UnifiedLogs library and its example command-line tools read a logarchive, or a copied diagnostics plus uuidtext set, and export CSV or JSON. Mac-only tools like log are not needed for offline work.
The older text logs in /private/var/log still exist. system.log now holds little, but install.log records software installations and updates, with local times that carry a UTC offset.
FSEvents
The fseventsd daemon records file system changes in .fseventsd at the root of each writable volume, including external drives. On macOS 10.15 and later, the directory that matters for user files is on the Data volume (mounted at /System/Volumes/Data on a live system). Finding .fseventsd on a USB drive's FAT32, exFAT or APFS volume shows it was mounted writable on a Mac.
Each file in .fseventsd is gzip-compressed and contains pages that start with a signature: 1SLD on older systems, 2SLD from macOS 10.13 (which adds a node ID to each record), and a later 3SLD variant. Each record holds a full path, an 8-byte event ID that only increases, and a 4-byte flags field with bits such as Created, Removed, Renamed, Modified, InodeMetaMod, FolderCreated and Mount.
FSEvents has no per-record timestamp. Event IDs give order, not time. The usual estimate is the modification time of the gzip file itself: all records in that file were written before it, and generally after the previous file's modification time. Narrow the window by finding in the same file a path whose time you know from another source. FSEvents is valuable because it keeps the names of deleted files, including files on external media, and renames that reveal an earlier name. A file named no_log in .fseventsd stops logging for that volume. Parsers include FSEventsParser and the FSEvents module of mac_apt.
KnowledgeC
The knowledgeC database is a SQLite file kept by the CoreDuet framework. There are two copies: per user at ~/Library/Application Support/Knowledge/knowledgeC.db and system-wide at /private/var/db/CoreDuet/Knowledge/knowledgeC.db. Copy the -wal and -shm files with each, because recent rows are often still in the write-ahead log.
Events live in table ZOBJECT. ZSTREAMNAME names the stream, for example /app/inFocus (an application in the foreground), /app/usage, /device/isLocked or /safari/history; which streams exist varies by version. ZVALUESTRING holds the value, such as an app's bundle ID. ZSTARTDATE, ZENDDATE and ZCREATIONDATE are Mac Absolute Time in UTC, and ZSECONDSFROMGMT records the device's UTC offset at that moment, for example -10800 for UTC−3.
On recent macOS, many streams, application focus among them, are written to Biome instead. On macOS 13 and later, expect knowledgeC to be sparse or to lack streams that older systems recorded. An empty /app/inFocus there is not evidence of deletion.
Biome
Biome stores streams in per-stream folders under ~/Library/Biome and, for system streams, /private/var/db/biome; application focus is in a stream named App.InFocus. The files use Apple's SEGB format: the ASCII signature SEGB in a header (two header versions exist), followed by records whose payloads are usually protobuf messages, with record times in Mac Absolute Time. Tool support is young and changes quickly. Open-source SEGB parsers and Biome modules in community tools exist, but test any tool against a known file before trusting its output.
Downloads, browsing and other user activity
- Quarantine database:
~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2(SQLite, no extension). TableLSQuarantineEventholds a UUID (LSQuarantineEventIdentifier),LSQuarantineTimeStamp(Mac Absolute Time), the agent (LSQuarantineAgentName,LSQuarantineAgentBundleIdentifier),LSQuarantineDataURLString(the file's URL) andLSQuarantineOriginURLString(the referring page). The downloaded file carries acom.apple.quarantineextended attribute, for example0083;69b52883;Safari;<UUID>: flags, the download time as hexadecimal Unix seconds, the agent, and the same UUID. Rows remain after the file is deleted. - Safari:
~/Library/Safari/History.db, wherehistory_itemsholds URLs andhistory_visitsholds each visit withvisit_timein Mac Absolute Time. Module 4 covers browsers. - Recent items:
~/Library/Application Support/com.apple.sharedfilelist/holds.sfl2files (.sfl3on newer releases) for recent documents, applications and servers, plus per-application recent document lists. They are archived plists containing bookmark data (path, volume); list order shows recency, but items generally carry no individual timestamps. OS X 10.11 and 10.12 used.sflfiles in the same folder; systems before 10.11 usedcom.apple.recentitems.plistin~/Library/Preferences. - TCC.db: the privacy permission database, per user at
~/Library/Application Support/com.apple.TCC/TCC.dband system-wide at/Library/Application Support/com.apple.TCC/TCC.db. Theaccesstable lists aservice(such askTCCServiceScreenCapture,kTCCServiceAccessibilityorkTCCServiceSystemPolicyAllFilesfor Full Disk Access), theclientapp,auth_value(2 means allowed on macOS 11 and later) andlast_modifiedin Unix seconds. A remote-control tool granted screen recording and accessibility stands out here. - Spotlight:
.Spotlight-V100on each indexed volume holds the metadata index, including attributes such askMDItemLastUsedDateandkMDItemWhereFroms; parsers such as the Spotlight plugin ofmac_aptmodule can read it.
Which source answers which question
| Question | Source | Path | Time format |
|---|---|---|---|
| When did the user log in or unlock? | Unified Logs | /private/var/db/diagnostics + uuidtext | Rendered with offset; convert to UTC |
| Which files were created, renamed or deleted? | FSEvents | .fseventsd on each volume | None; estimate from the gzip file's mtime |
| Which app was in the foreground, and for how long? | knowledgeC / Biome | knowledgeC.db; ~/Library/Biome | Mac Absolute Time |
| What was downloaded, from where? | Quarantine DB | ~/Library/Preferences/com.apple.LaunchServices.QuarantineEventsV2 | Mac Absolute Time (xattr: hex Unix) |
| Which pages were visited? | Safari | ~/Library/Safari/History.db | Mac Absolute Time |
| Which app got screen or disk access? | TCC | .../com.apple.TCC/TCC.db | Unix seconds |
Worked example: a download, a launch and a log entry
A Mac set to UTC−3 is examined. The quarantine database contains one row of interest:
| Field | Value |
|---|---|
LSQuarantineTimeStamp | 795172867.0 |
LSQuarantineAgentName | Safari |
LSQuarantineOriginURLString | https://www.example.net/support |
LSQuarantineDataURLString | https://downloads.example.net/files/RemoteHelper.dmg |
Mac Absolute to Unix: 795,172,867 + 978,307,200 = 1,773,480,067, which is 2026-03-14T09:21:07Z. In hex, 1,773,480,067 is 69b52883, the value expected in the second field of the file's com.apple.quarantine attribute.
The user's knowledgeC has an /app/inFocus row with ZVALUESTRING = net.example.RemoteHelper, ZSTARTDATE = 795173145.0, ZENDDATE = 795173412.0 and ZSECONDSFROMGMT = -10800.
- Start: 795,173,145 + 978,307,200 = 1,773,480,345 = 2026-03-14T09:25:45Z.
- End: 795,173,412 + 978,307,200 = 1,773,480,612 = 2026-03-14T09:30:12Z.
- Duration: 795,173,412 − 795,173,145 = 267 s (4 min 27 s).
- -10,800 s is −3 h, so the local start was 06:25:45. Do not apply the offset to the stored value;
ZSTARTDATEis already UTC.
In the unified log, log show --archive case.logarchive --predicate 'process == "syspolicyd" AND eventMessage CONTAINS "RemoteHelper"' returns a Gatekeeper assessment of /Applications/RemoteHelper.app at 2026-03-14 06:25:41.208114-0300. Adding 3 hours gives 09:25:41Z, four seconds before the app came into focus, consistent with a first launch being checked by Gatekeeper. Three independent sources now give one sequence: download at 09:21:07Z, Gatekeeper check at 09:25:41Z, app in focus from 09:25:45Z to 09:30:12Z. FSEvents can add the creation of RemoteHelper.app in /Applications, but only as an ordered event without its own time.
Exam focus: Expect questions on where each source lives, which time format it uses (Mac Absolute Time for knowledgeC, Biome, quarantine and Safari; Unix seconds in TCC and in hex in the quarantine attribute; no timestamp in FSEvents), and on converting a Mac Absolute value to ISO 8601 UTC. Know why uuidtext and timesync files must be collected with tracev3 files, what <private> means, and why knowledgeC can be sparse on recent macOS.
Key points
- Unified Logs: tracev3 files in
/private/var/db/diagnosticsneeduuidtextand timesync data; collect withlog collect, filter withlog show --predicate, and convert the printed offset to UTC. - FSEvents records paths, flags and event IDs per volume, including deleted names on external drives, but no per-record time.
- knowledgeC stores usage streams such as
/app/inFocusin Mac Absolute Time withZSECONDSFROMGMT; recent macOS moves much of it to Biome SEGB files. - The quarantine database links a download's URL, agent and time to the file's
com.apple.quarantineattribute. - TCC.db shows which apps received privacy permissions, timed in Unix seconds.
- Corroborate: download, log entry and usage record should form one consistent sequence.