Skip to content

File accessAnti-forensics

FSEvents (.fseventsd): macOS File System Change Log

Per-volume gzip logs written by fseventsd that record created, modified, renamed and deleted paths, often the only trace of a removed file.

Location
/System/Volumes/Data/.fseventsd/
Proves
Which paths were created, changed, renamed or deleted on a volume, in event order
Timestamps
None per record; estimate from log file mtime and dated paths
Access
root; Full Disk Access on a live system
Retention
Rolling, purged by fseventsd (months observed, varies)
Collection
UAC, mac_apt, Disk image, ditto

What it is

The fseventsd daemon keeps a persistent journal of file system changes so that applications (Time Machine, Spotlight, sync clients) can ask "what changed under this folder since event X". Each writable volume it tracks gets a hidden .fseventsd folder holding gzip-compressed log files. Each record is a path, a 64-bit event ID and a flag mask, and newer formats add a node ID.

For an investigator this is a path-level history of the volume: files that were created and deleted in /private/tmp, an archive that briefly existed in ~/Downloads, a LaunchAgent plist that was written and later removed, or the contents of a USB stick. The in-depth FSEvents guide covers the format in detail; this page is the quick reference.

Where it lives

macOSPathNotes
10.15 Catalina and later/System/Volumes/Data/.fseventsd/The Data volume holds user activity; paths inside are relative to the Data volume root
10.14 and earlier/.fseventsd/Single boot volume
Any, external or removable volume/Volumes/<name>/.fseventsd/Travels with the drive

Inside the folder:

  • Log files with 16-digit hexadecimal names derived from event IDs, so they sort in event order.
  • fseventsd-uuid: identifies the event stream for that volume. A new UUID means the history was reset.
  • no_log (optional): an empty file that tells fseventsd not to log the volume.

Protection: the folder is root-owned. On a live system the collecting process also needs Full Disk Access. mac_apt additionally checks /private/var/db/fseventsd as an alternative location.

What it proves

  • A file or folder with a given name existed at a path on that volume, even after deletion.
  • The kind of change: created, modified, renamed or moved, removed, permissions or ownership changed, extended attribute added or removed (for example com.apple.quarantine being stripped).
  • The relative order of changes on a volume (event IDs increase monotonically).
  • That a removable volume was written on a Mac, and which paths were touched on it.

It does not prove:

  • An exact time. Records carry no timestamp.
  • Which user or process made the change.
  • File content. Pair it with APFS snapshots, Time Machine or carving.
  • Activity on read-only mounts or network shares, which are not logged.

Key fields

FieldMeaning
Page magic 1SLDVersion 1 page: path, event ID (8 bytes), flags (4 bytes)
Page magic 2SLDVersion 2 page (macOS 10.13 High Sierra onward): adds node ID (8 bytes)
Page magic 3SLDVersion 3 page (macOS 14 Sonoma onward): adds a further 4-byte field, decoded as a user ID (fs_uid) by FSEventsParser and left as unknown by mac_apt
PathNull-terminated path relative to the volume root, as it was when logged
Event ID64-bit counter per volume; ordering only
FlagsBitmask of change type and object type
Node IDFile system object ID (on APFS, the inode number); links a file across renames

Common flag names reported by parsers: Created, Removed, Renamed (mac_apt: RenamedOrMoved), Modified, InodeMetaMod, PermissionChange, ExtendedAttrModified / ExtendedAttrRemoved (mac_apt: XAttrModified / XAttrRemoved), FinderInfoMod, FolderCreated, Exchange, ItemCloned, LastHardLinkRemoved, Mount, Unmount, EndOfTransaction, plus object types such as FileEvent, FolderEvent, SymbolicLink, HardLink. The on-disk bit values differ from Apple's public FSEventStreamEventFlags API constants, so trust a parser that decodes the on-disk layout.

Timestamps

There are none inside records. Estimate time from:

  1. Log file modification time. A file is written when fseventsd flushes, so its mtime is an upper bound for its records and the previous file's mtime is a rough lower bound.
  2. Dated paths. Paths with dates in their names (rotated logs, snapshot names, diagnostic reports) appearing as Created anchor nearby event IDs. FSEventsParser uses this to fill its approx_dates_plus_minus_one_day column.
  3. Correlation. A real timestamp from another artifact for one path (APFS birth time, a quarantine event, a Unified Log entry) dates its neighbours.

Always report FSEvents times as a range and name the anchor used.

Retention

No fixed period. fseventsd purges older log files on its own, and history depends on volume activity and space. CrowdStrike has reported events around four months old on typical systems; busy volumes may keep much less. A changed fseventsd-uuid or a volume reformat starts a new history. Collect early and preserve file mtimes.

Collection

Copy the entire folder and keep modification times (they are your only clock):

# Live, as root, from a terminal with Full Disk Access
sudo ditto /System/Volumes/Data/.fseventsd /Volumes/CASE/MBP01/fseventsd
sudo ls -lT /System/Volumes/Data/.fseventsd > /Volumes/CASE/MBP01/fseventsd_listing.txt
  • UAC: the ir_triage and full profiles include the files/logs/macos.yaml artifact, which collects /.fseventsd and /System/Volumes/*/.fseventsd.
  • mac_apt: the FSEVENTS plugin reads the folder directly from an E01, DMG, raw or mounted image.
  • Aftermath: not collected by default; add the folder with --collect-dirs.
  • Removable media: image the drive or copy its .fseventsd with the same care.

Parsing

FSEventsParser (handles 1SLD, 2SLD and 3SLD) writes a SQLite database plus TSV reports:

python3 FSEParser_V4.1.py -s /cases/MBP01/fseventsd -t folder -o /cases/MBP01/out -c MBP01
-- FSEventsParser schema: deleted items under a user's Downloads
SELECT id_hex, fullpath, type, flags, approx_dates_plus_minus_one_day, source, source_modified_time
FROM fsevents_sorted_by_event_id
WHERE fullpath LIKE 'Users/%/Downloads/%' AND flags LIKE '%Removed;%'
ORDER BY id;

mac_apt parses the same data from an image, with a SourceModDate column for each record:

python3 mac_apt.py -o /cases/MBP01/mac_apt E01 /cases/MBP01.E01 FSEVENTS

FSEvents Parser decodes a .fseventsd folder (or a UAC, Aftermath or Velociraptor collection) in the browser, with decoded flags, time windows, rename pairs and CSV or Timesketch export; nothing is uploaded.

Investigator tips

  • Pair Renamed records by node ID (version 2 and later) to follow a file through renames and moves.
  • ExtendedAttrRemoved on a freshly downloaded app bundle can mean the quarantine attribute was stripped. Check quarantine events.
  • Watch Library/LaunchAgents and Library/LaunchDaemons paths for short-lived persistence; see launch agents and daemons.
  • A no_log file or a recent fseventsd-uuid can be deliberate anti-forensics, or just a volume formatted elsewhere. Check the Unified Logs and APFS timestamps of those files.
  • On the internal Data volume, new Volumes/<name> paths show a volume was mounted. Combine with the drive's own .fseventsd and USB device evidence.
  • Coalescing: one record can combine Created;Modified;Removed. You cannot recover the exact order inside a single record.
  • Parsed output easily exceeds a million rows. Filter by path first (Users/*/Downloads, private/tmp, Users/Shared, Library/LaunchAgents), then widen.
  • Deduplicate before reporting: FSEventsParser does not deduplicate, and carved gzip files can repeat records.

See also