Open Source / iLEAPP #2522

Reading ManagedSettings stores in a sysdiagnose

A sysdiagnose holds the stores behind Screen Time and Family Controls restrictions, and nothing read them. These two artifacts report each stored key, the directory of stores and the version, and say plainly what the files cannot show.

  • Daniel Addai, SectraTEK Lab
  • Merged
  • CC BY 4.0

View iLEAPP pull request #2522 on GitHub

Summary

  • A sysdiagnose carries the ManagedSettings stores, the settings behind Screen Time and Family Controls restrictions. No iLEAPP artifact read them before this pull request.
  • Two artifacts now report one row per stored key and the directory of stores, for the store names of both iOS 26.6 and iOS 27.0.
  • Counts on two real sysdiagnoses equal an independent count from the property lists. The pull request was merged on 20 October 2026.
  • A row says what a store held. The files carry no time, and a stored false or an absent key is not a finding that a feature was off.
  • 2artifacts added
  • 73 + 21setting rows on two sysdiagnoses
  • 8 of 8test cases pass
  • +5,562lines added, 418 in the parser

The issue

A sysdiagnose is the archive an iPhone builds for Apple support, and examiners use it because it carries many system files in one place. Under logs/ManagedSettings it holds a copy of the ManagedSettings stores, the settings stores that Screen Time and the Family Controls framework use to apply restrictions. Until this contribution no artifact read them, so a question such as "was a web-content restriction or a bedtime schedule stored on this phone?" had no table to look at.

The files are property lists with names that mean little on their own: a store identifier as the file name, a component folder, a record file that links the two. Reading them by hand is slow, and it is easy to over-read the result. A key with a value of false is a stored value, not a finding that a feature was off. A key that is absent is not a setting that was switched off. The numbers stored for a policy are not documented in the files, and none of the files carries a time, so no row says when anything was set.

There is a second difficulty. The store names changed between releases. An iOS 26.6 sysdiagnose names five stores, an iOS 27.0 sysdiagnose names eight, and only two names are the same. A reading written for one set would miss the other. Figure 1 shows where the files are and which report table reads each one.

A sysdiagnose folder that no report read, and the two artifacts that read itLeft: the files under logs/ManagedSettings in a sysdiagnose. Right: the two artifacts, ManagedSettings - Settings for the store and effective-settings files and ManagedSettings - Store Directory for the record and version files.Inside a sysdiagnoseReport tableslogs/ManagedSettings/User/ and System/<component folder>/<store identifier>.plistclientEffectiveSettings.plistEffectiveSettings.plistSettingRecords.plistDataVersion.plist*.tokenized.plistnot readManagedSettings - Settingsone row per top-level key of eachstore fileManagedSettings - Store Directorycomponent and store entries, andthe data versionSchematic of the folder layout described in the pull request.
Show this figure as a table
Files and the artifact that reads each
FileArtifact
Store file, clientEffectiveSettings.plist, EffectiveSettings.plistManagedSettings - Settings
SettingRecords.plist, DataVersion.plistManagedSettings - Store Directory
*.tokenized.plistnot read
Figure 1The ManagedSettings files in a sysdiagnose and the report table that reads each. Tokenized copies are not read. Source: PR #2522 body (diagram, synthetic example of the layout; no device values).

The essence

The contribution adds two artifacts that read the ManagedSettings files from a sysdiagnose_*.tar.gz archive, an extracted folder or standalone files. ManagedSettings - Settings gives one row per top-level key of each store file, each component effective-settings file and the user effective-settings file. ManagedSettings - Store Directory gives the component and store entries of SettingRecords.plist and the values of DataVersion.plist.

One artifact reads both sets of store names. A key the parser does not describe is reported with its key and value as stored, and the notes name what is not covered.

The two artifacts, the files they read and their columns
ArtifactFiles readColumns
ManagedSettings - SettingsStore files, clientEffectiveSettings.plist, EffectiveSettings.plistScope, component folder, file kind, store name, group, plain label, key as stored, value as text, list-entry count, active, syncToWatch, version
ManagedSettings - Store DirectorySettingRecords.plist, DataVersion.plistStore name, identifier, active, syncToWatch, whether the store file is in the archive, team identifier, record identifier, container path as stored, data version

Our methodology

No public description of these files was available, so the parser was built from the files themselves, on a sysdiagnose from each of two releases in the Lab's research corpus. The label for a setting is written from the key name alone, because the file does not describe its keys, and the group (install, web content, shield, Siri and intelligence, user safety, Safari, device activity) comes from the part of the key before the first dot.

Three rules came from that reading and are kept in the code. Key material is reported by size only, for example "<n bytes, not copied>", and the .tokenized.plist copies are not read. A file that cannot be read becomes a row that says so instead of an exception. And the notes follow the repository's wording rules: reporting only, no verdict, no meaning claimed for stored numbers such as a maximum rating.

The artifacts were checked on real sysdiagnoses through the real tool, on the archive, on an extracted folder, and on a folder that holds the archive. An iOS 27.0 sysdiagnose gave 73 setting rows from 13 property lists and 11 store-directory rows. An iOS 26.6 sysdiagnose gave 21 and 9 from 12 lists. Every count equals an independent count taken straight from the property lists with no iLEAPP code, the HTML, TSV and LAVA tables match, and no report holds a local path (Figure 3). During review the artifact was also run on two public-corpus iOS 26 sysdiagnoses, which gave 16 and 7 rows, and 10 and 5, with no errors; three older sysdiagnoses from iOS 13 to 17 hold no ManagedSettings folder.

The fixtures are of two kinds. A generator builds a synthetic tree with com.example.* identifiers and fabricated key material, and ten unit tests cover both store-name sets, an unreadable file, an unknown key, key material not copied, tokenized copies, decoy folders and sidecars not read, and the archive and the folder giving the same rows. Two further cases keep the structure of real files with every identifying value replaced: the store and record identifiers are generated replacements that keep the links between files, the key bytes are generated, and the file times and the archive folder name are shifted by one constant offset. The replacement table is not in the repository. The test runner passes 8 of 8 cases.

From a sysdiagnose property list to a report rowPipeline of four steps: find the property lists in an archive, folder or standalone file; classify each file kind; parse it, with an unreadable file kept as a row; write rows with a plain label, the key and value as stored, and the store flags.1 Findsysdiagnose_*.tar.gz,an extracted folder,or standalone files2 Classifystore, component effective,effective, records, version;tokenized copies skipped3 Parseproperty list read as stored;an unreadable file becomesa row, not an error4 Reportscope, component, file kind,store name, group, label,key and value as storedRules the parser keepsLabel from the key name only; a key the parser does not describe keeps its key and value.Key material is reported by size only, such as “<n bytes, not copied>”.An absent key is never reported as off; the files carry no time.Schematic of the steps in scripts/artifacts/managedSettings.py.
Show this figure as a table
Steps of the parser
StepWhat happens
FindArchive, folder or standalone files
ClassifyStore, component effective, effective, records, version; tokenized skipped
ParseProperty list read as stored; an unreadable file becomes a row
ReportScope, component, kind, store name, group, label, key and value as stored
Figure 2The four steps from file to row, and the rules the parser keeps. Source: diff file scripts/artifacts/managedSettings.py (diagram, synthetic example of the flow).
Rows from four sysdiagnosesBar chart: iOS 27.0 gave 73 setting rows and 11 store-directory rows; iOS 26.6 gave 21 and 9; two public-corpus iOS 26 sysdiagnoses gave 16 and 7, and 10 and 5.Rows per sysdiagnosesetting rowsstore-directory rowsiOS 27.0 (13 property lists)7311iOS 26.6 (12 property lists)219iOS 26 public corpus A167iOS 26 public corpus B105020406080
Show this figure as a table
Rows per sysdiagnose
SysdiagnoseSetting rowsStore-directory rows
iOS 27.07311
iOS 26.6219
iOS 26 public corpus A167
iOS 26 public corpus B105
Figure 3Setting rows and store-directory rows on four sysdiagnoses. The first two counts equal an independent count from the property lists. Source: PR #2522 body, Tested and Corpus run (aggregate counts, no synthetic values).

The value

For a case, the artifact answers a narrow question well: which settings stores did this sysdiagnose hold, and what keys and values were in them? That is useful when a phone is said to have been managed or restricted, and equally when it is not. On both iOS 26 public-corpus images no restriction was set, and the artifact makes that visible instead of leaving it to a manual read of the files. A sysdiagnose from a device with Screen Time restrictions would close the gap, because none was seen in the corpus.

The limits are the larger part of the value. A row says what a store held in the sysdiagnose copy. It does not show that a person set a restriction, who set it, or when. The same value can appear in a store file, in its component's effective settings and in the effective settings file, so rows are not a count of settings. Two sources are not read: the live stores of a full file system extraction, which use a different layout, and the diagnostic tool's text output, which was empty on both images. The Lab report treats the live file system listing separately and counts it, but its artifacts do not read it.

The practical advice is short. Compare the store names with the release, state which file a value came from, and do not read a stored false or an absent key as a finding.

Store names by releaseMatrix of eleven store names against two releases. Eight names were seen on iOS 27.0 and five on iOS 26.6, with Default and UserSafety on both.Store names seen in one sysdiagnose per releaseiOS 27.0iOS 26.6ContentAndPrivacyseennot seenRestrictionsseennot seendefaultGroupseennot seendowntimeseennot seenscheduleDowntimeseennot seenbedtimeseennot seenUserSafetyseenseenDefaultseenseenSafariStorenot seenseenUserSafetyStorenot seenseenDeviceActivityStorenot seenseenOne artifact reads both name sets. Names come from the pull request; the files carry no time.
Show this figure as a table
Store names by release
Store nameiOS 27.0iOS 26.6
ContentAndPrivacyseennot seen
Restrictionsseennot seen
defaultGroupseennot seen
downtimeseennot seen
scheduleDowntimeseennot seen
bedtimeseennot seen
UserSafetyseenseen
Defaultseenseen
SafariStorenot seenseen
UserSafetyStorenot seenseen
DeviceActivityStorenot seenseen
Figure 4Store names differ between the two releases; one artifact reads both sets. Source: PR #2522 body; LAB-TR-0009 section 4.6 (names only, no synthetic values).

Where this sits in the Lab's research

This work is one part of Lab Technical Report LAB-TR-0009, Screen Time on iOS 15 to 27, published on Zenodo (doi:10.5281/zenodo.23720067, version 1.2; the concept DOI 10.5281/zenodo.23717932 always points to the newest version). Its fourth research question asks what a sysdiagnose holds of the ManagedSettings stores and how store names vary between releases. The report counts 13 property lists on iOS 27.0 and 12 on iOS 26.6, finds no time in any file, and lists the store names of both releases (Figure 4 draws them). It also lists the live listing on the file system, which these two artifacts do not read.

No other Lab report covers this pull request; the method above is from the pull request and from LAB-TR-0009.

Sources

Every figure in the text and in the diagrams is listed here with where it comes from. Nothing else was used: no device, image or report was opened for this post.

Every number and claim in this post, with where it comes from
Number or claimSource
2 artifacts; +5,562 lines in total, 418 of them in managedSettings.pyPR #2522 file list
Merged 20 October 2026PR #2522 state and mergedAt
iOS 27.0: 73 setting rows from 13 property lists, 11 store-directory rows; iOS 26.6: 21 and 9 from 12PR #2522 body, Tested
Counts equal an independent count from the plists; HTML, TSV and LAVA tables matchPR #2522 body, Tested
Public-corpus iOS 26 sysdiagnoses: 16 and 7 rows; 10 and 5; no errorsPR #2522 body, Corpus run (added in review)
Three older sysdiagnoses hold no ManagedSettings folderPR #2522 body; LAB-TR-0009 Table 8
10 unit tests; 8 of 8 test casesPR #2522 body, Tested
Store names: eight on iOS 27.0, five on iOS 26.6, two in commonPR #2522 body; LAB-TR-0009 section 4.6 and Figure 4
Key material by size only; tokenized copies not read; unreadable file becomes a rowPR #2522 body; diff file scripts/artifacts/managedSettings.py
Sanitized real-structure fixtures: identifiers replaced, key bytes generated, one constant time offsetPR #2522 body, Real-structure test fixtures
No restriction set on both iOS 26 corpus images; no managed device seenPR #2522 body, What the data does and does not show; Corpus run
Live file system stores and the diagnostic tool output not readPR #2522 body, Corpus run
Files carry no timePR #2522 body; LAB-TR-0009 section 4.6
Zenodo DOI 10.5281/zenodo.23720067 (version 1.2), concept 10.5281/zenodo.23717932papers/zenodo/DOIS.md