Spotlight Store (.Spotlight-V100): macOS Metadata Index
Per-volume Spotlight index and per-user CoreSpotlight stores holding file metadata such as download origin, last used date and use count.
- Location
- /System/Volumes/Data/.Spotlight-V100/Store-V2/<UUID>/store.db
- Proves
- Where a file came from, when it was added and opened, and that it existed even after deletion
- Timestamps
- Mac absolute time (seconds since 2001-01-01 UTC); mdls shows UTC
- Access
- root; Full Disk Access on a live system
- Retention
- Until the indexer updates or purges the entry, or the index is rebuilt
- Collection
- mac_apt, Disk image, ditto
Tools
Compare all toolsWhat it is
Spotlight keeps a metadata index for every indexed volume. The mds family of processes extracts attributes (kMDItem*) from each file and stores them in a proprietary database under .Spotlight-V100 at the volume root. Since macOS 10.13, each user also has CoreSpotlight stores, where apps donate searchable content.
The value for an investigation is metadata that the file system does not keep: download URL, date added to a folder, last used date and use count. Because the store is updated asynchronously, it can still hold entries for files that were deleted. The Spotlight forensics guide covers live querying and attributes in depth; this page is the quick reference.
Where it lives
| Scope / macOS | Path | Notes |
|---|---|---|
| Boot Data volume, 10.15+ | /System/Volumes/Data/.Spotlight-V100/Store-V2/<UUID>/ | store.db, .store.db and support files |
| Boot volume, 10.14 and earlier | /.Spotlight-V100/Store-V2/<UUID>/ | Same structure |
| External / removable volume | /Volumes/<name>/.Spotlight-V100/ | Shows the drive was indexed by a Mac |
| User CoreSpotlight, 10.13 to 11 | ~/Library/Metadata/CoreSpotlight/index.spotlightV3/ | App-donated content |
| User CoreSpotlight, 12+ | ~/Library/Metadata/CoreSpotlight/NSFileProtectionComplete/index.spotlightV3/ and sibling NSFileProtectionCompleteUnlessOpen, NSFileProtectionCompleteUntilFirstUserAuthentication folders | Some of these can be encrypted |
| Spotlight bar shortcuts, 10.15 | ~/Library/Application Support/com.apple.spotlight/com.apple.spotlight.Shortcuts | Typed search terms and chosen result |
| Spotlight bar shortcuts, 11 to 13 | ~/Library/Application Support/com.apple.spotlight/com.apple.spotlight.Shortcuts.v3 | As above |
| Spotlight bar shortcuts, 14+ | ~/Library/Group Containers/group.com.apple.spotlight/com.apple.spotlight.Shortcuts.v3 | As above |
Protection: .Spotlight-V100 is root-owned and privacy-protected, so a live read needs root and Full Disk Access. User ~/Library stores need Full Disk Access for the collector.
What it proves
- Download origin (
kMDItemWhereFroms), even when the browser history was cleared. - When an item arrived in its current folder (
kMDItemDateAdded). - That a file was opened through Launch Services, when last, and on which days.
- That a file existed at some point, via entries surviving in the store after deletion.
- What a user typed into the Spotlight bar and which result they picked (shortcuts file).
It does not prove:
- Every access. Reads with
cat, scripts or direct file APIs do not update usage attributes. - Absence. Excluded folders, volumes with
.metadata_never_index, disabled indexing and indexing lag all leave gaps.
Key fields
| Attribute | Meaning |
|---|---|
kMDItemWhereFroms | Download URL and referrer; comes from the com.apple.metadata:kMDItemWhereFroms xattr |
kMDItemDateAdded | When the item was placed in its current parent folder |
kMDItemLastUsedDate | Last open through Launch Services |
kMDItemUseCount | Number of recorded uses (not always present) |
kMDItemUsedDates | Array of days the item was used |
kMDItemFSCreationDate, kMDItemFSContentChangeDate | File system dates as indexed |
kMDItemContentType, kMDItemContentTypeTree | Uniform Type Identifier; spots renamed executables or disk images |
kMDItemFSName, kMDItemDisplayName | File name as indexed |
In spotlight_parser output each record starts with Inode_Num, Flags, Store_ID, Parent_Inode_Num and Last_Updated. The inode and parent inode are how full paths are rebuilt; Last_Updated is when the index record was last written, not a file time.
Timestamps
Attribute dates in the store are Mac absolute time: seconds (stored as a double) since 2001-01-01 00:00:00 UTC. Add 978307200 to get Unix time. The per-record update time is different: spotlight_parser decodes it as microseconds since the Unix epoch. mdls and both parsers print converted UTC values.
# Convert a raw Mac absolute time value on macOS
date -u -r $(( 780000000 + 978307200 ))
kMDItemUsedDates values are day-granular; do not read a time of day into them.
Retention
There is no time-based expiry. An entry lives until the indexer updates or removes it, the volume is reindexed (mdutil -E, or macOS rebuilding a damaged index), or indexing is turned off. Entries for deleted files can linger for some time, and the hidden .store.db can hold items that differ from store.db.
Collection
Collect the whole Store-V2/<UUID> folder, not only store.db: the dbStr-* map files and journals belong to that store instance.
# Live, root, terminal with Full Disk Access
sudo ditto /System/Volumes/Data/.Spotlight-V100 /Volumes/CASE/MBP01/Spotlight-V100
ditto ~/Library/Metadata/CoreSpotlight /Volumes/CASE/MBP01/CoreSpotlight_user
- mac_apt
SPOTLIGHTreads volume and user stores straight from an image;SPOTLIGHTSHORTCUTShandles the shortcuts plists. - UAC
fullprofile collects the Spotlight shortcuts files (files/applications/spotlight.yaml); copy.Spotlight-V100separately. - Before collecting, record
mdutil -s /(read only). Never runmdutil -Eormdutil -i offon evidence.
Parsing
spotlight_parser (Python 3, needs lz4 and pyliblzfse) takes one database and an output folder:
python3 spotlight_parser.py -p MBP01 /cases/MBP01/Spotlight-V100/Store-V2/<UUID>/store.db /cases/MBP01/spot_out
python3 spotlight_parser.py -p MBP01_hidden /cases/MBP01/Spotlight-V100/Store-V2/<UUID>/.store.db /cases/MBP01/spot_out
It writes a _data.txt dump of every record and a _fullpaths.tsv mapping of inode to path. Its author recommends mac_apt for SQLite output and per-app views:
python3 mac_apt.py -o /cases/MBP01/mac_apt E01 /cases/MBP01.E01 SPOTLIGHT SPOTLIGHTSHORTCUTS
When mac_apt processes .store.db, it only writes items that are new or different from store.db (the .store-DIFF output).
Live alternative, for targeted checks only:
mdls -name kMDItemWhereFroms -name kMDItemDateAdded -name kMDItemLastUsedDate -name kMDItemUseCount ~/Downloads/tool.dmg
mdfind 'kMDItemWhereFroms == "*example.com*"'
Investigator tips
- Parse both
store.dband.store.db; the differences can hold deleted or older entries. - Filter parsed output by path (
Users/*/Downloads,private/tmp,Users/Shared) and bykMDItemContentType(applications, disk images, scripts, archives). - Pivot
kMDItemWhereFromsto Safari history and quarantine events to confirm the download. - Use FSEvents to bracket when a store-only (deleted) file was removed.
- A
.Spotlight-V100folder on a USB drive shows it was mounted on a Mac; see USB devices. - Mounting an evidence image read-write can trigger indexing and change the store. Mount read-only.
- Indexing can be switched off per volume. Check
mdutilandmdsactivity in the Unified Logs if the store looks unexpectedly empty. - Validate parser output against a test Mac on the same macOS version; the store format evolves.